From d22224a401244de8eb424a54783d282a2533f7dd Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Fri, 14 Jul 2023 01:13:43 +0200 Subject: [PATCH 001/632] [3.11] gh-106368: Increase Argument Clinic test coverage (#106728) (#106731) - improve output_parameter() coverage - improve coverage for Function.kind (cherry picked from commit ec45c513d389510930a62631a21a1dbb3f3aabb7) Co-authored-by: Erlend E. Aasland * Fix merge --- Lib/test/clinic.test.c | 506 ++++++++++++++++++++++++++++++++++++++++ Lib/test/test_clinic.py | 37 +++ 2 files changed, 543 insertions(+) diff --git a/Lib/test/clinic.test.c b/Lib/test/clinic.test.c index 0abadbe7f3474e..1eb5a3e7cce0d8 100644 --- a/Lib/test/clinic.test.c +++ b/Lib/test/clinic.test.c @@ -3,6 +3,10 @@ output preset block [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=3c81ac2402d06a8b]*/ +/*[clinic input] +class Test "TestObj *" "TestType" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=fc7e50384d12b83f]*/ /*[clinic input] test_object_converter @@ -61,6 +65,58 @@ test_object_converter_impl(PyObject *module, PyObject *a, PyObject *b, /*[clinic end generated code: output=886f4f9b598726b6 input=005e6a8a711a869b]*/ +/*[clinic input] +cloned = test_object_converter +Check the clone feature. +[clinic start generated code]*/ + +PyDoc_STRVAR(cloned__doc__, +"cloned($module, a, b, c, d, /)\n" +"--\n" +"\n" +"Check the clone feature."); + +#define CLONED_METHODDEF \ + {"cloned", _PyCFunction_CAST(cloned), METH_FASTCALL, cloned__doc__}, + +static PyObject * +cloned_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *c, + PyUnicode_Object *d); + +static PyObject * +cloned(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *a; + PyObject *b; + PyObject *c; + PyUnicode_Object *d; + + if (!_PyArg_CheckPositional("cloned", nargs, 4, 4)) { + goto exit; + } + a = args[0]; + if (!PyUnicode_FSConverter(args[1], &b)) { + goto exit; + } + if (!PyUnicode_Check(args[2])) { + _PyArg_BadArgument("cloned", "argument 3", "str", args[2]); + goto exit; + } + c = args[2]; + d = (PyUnicode_Object *)args[3]; + return_value = cloned_impl(module, a, b, c, d); + +exit: + return return_value; +} + +static PyObject * +cloned_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *c, + PyUnicode_Object *d) +/*[clinic end generated code: output=026b483e27c38065 input=0543614019d6fcc7]*/ + + /*[clinic input] test_object_converter_one_arg @@ -3556,3 +3612,453 @@ test_paramname_module(PyObject *module, PyObject *const *args, Py_ssize_t nargs, static PyObject * test_paramname_module_impl(PyObject *module, PyObject *mod) /*[clinic end generated code: output=23379a7ffa65c514 input=afefe259667f13ba]*/ + + +/*[clinic input] +Test.cls_with_param + cls: defining_class + / + a: int +[clinic start generated code]*/ + +PyDoc_STRVAR(Test_cls_with_param__doc__, +"cls_with_param($self, /, a)\n" +"--\n" +"\n"); + +#define TEST_CLS_WITH_PARAM_METHODDEF \ + {"cls_with_param", _PyCFunction_CAST(Test_cls_with_param), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, Test_cls_with_param__doc__}, + +static PyObject * +Test_cls_with_param_impl(TestObj *self, PyTypeObject *cls, int a); + +static PyObject * +Test_cls_with_param(TestObj *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + static const char * const _keywords[] = {"a", NULL}; + static _PyArg_Parser _parser = {NULL, _keywords, "cls_with_param", 0}; + PyObject *argsbuf[1]; + int a; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + a = _PyLong_AsInt(args[0]); + if (a == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = Test_cls_with_param_impl(self, cls, a); + +exit: + return return_value; +} + +static PyObject * +Test_cls_with_param_impl(TestObj *self, PyTypeObject *cls, int a) +/*[clinic end generated code: output=9c06a8cfc495b4d1 input=af158077bd237ef9]*/ + + +/*[clinic input] +Test.__init__ +Empty init method. +[clinic start generated code]*/ + +PyDoc_STRVAR(Test___init____doc__, +"Test()\n" +"--\n" +"\n" +"Empty init method."); + +static int +Test___init___impl(TestObj *self); + +static int +Test___init__(PyObject *self, PyObject *args, PyObject *kwargs) +{ + int return_value = -1; + + if ((Py_IS_TYPE(self, TestType) || + Py_TYPE(self)->tp_new == TestType->tp_new) && + !_PyArg_NoPositional("Test", args)) { + goto exit; + } + if ((Py_IS_TYPE(self, TestType) || + Py_TYPE(self)->tp_new == TestType->tp_new) && + !_PyArg_NoKeywords("Test", kwargs)) { + goto exit; + } + return_value = Test___init___impl((TestObj *)self); + +exit: + return return_value; +} + +static int +Test___init___impl(TestObj *self) +/*[clinic end generated code: output=f02b7d23eec3dc47 input=4ea79fee54d0c3ff]*/ + + +/*[clinic input] +@classmethod +Test.__new__ +Empty new method. +[clinic start generated code]*/ + +PyDoc_STRVAR(Test__doc__, +"Test()\n" +"--\n" +"\n" +"Empty new method."); + +static PyObject * +Test_impl(PyTypeObject *type); + +static PyObject * +Test(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + + if ((type == TestType || + type->tp_init == TestType->tp_init) && + !_PyArg_NoPositional("Test", args)) { + goto exit; + } + if ((type == TestType || + type->tp_init == TestType->tp_init) && + !_PyArg_NoKeywords("Test", kwargs)) { + goto exit; + } + return_value = Test_impl(type); + +exit: + return return_value; +} + +static PyObject * +Test_impl(PyTypeObject *type) +/*[clinic end generated code: output=3a8a564e799cf5ce input=6fe98a19f097907f]*/ + + +/*[clinic input] +Test.cls_no_params + cls: defining_class + / +[clinic start generated code]*/ + +PyDoc_STRVAR(Test_cls_no_params__doc__, +"cls_no_params($self, /)\n" +"--\n" +"\n"); + +#define TEST_CLS_NO_PARAMS_METHODDEF \ + {"cls_no_params", _PyCFunction_CAST(Test_cls_no_params), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, Test_cls_no_params__doc__}, + +static PyObject * +Test_cls_no_params_impl(TestObj *self, PyTypeObject *cls); + +static PyObject * +Test_cls_no_params(TestObj *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + if (nargs) { + PyErr_SetString(PyExc_TypeError, "cls_no_params() takes no arguments"); + return NULL; + } + return Test_cls_no_params_impl(self, cls); +} + +static PyObject * +Test_cls_no_params_impl(TestObj *self, PyTypeObject *cls) +/*[clinic end generated code: output=cc8845f22cff3dcb input=e7e2e4e344e96a11]*/ + + +/*[clinic input] +Test.metho_not_default_return_converter -> int + a: object + / +[clinic start generated code]*/ + +PyDoc_STRVAR(Test_metho_not_default_return_converter__doc__, +"metho_not_default_return_converter($self, a, /)\n" +"--\n" +"\n"); + +#define TEST_METHO_NOT_DEFAULT_RETURN_CONVERTER_METHODDEF \ + {"metho_not_default_return_converter", (PyCFunction)Test_metho_not_default_return_converter, METH_O, Test_metho_not_default_return_converter__doc__}, + +static int +Test_metho_not_default_return_converter_impl(TestObj *self, PyObject *a); + +static PyObject * +Test_metho_not_default_return_converter(TestObj *self, PyObject *a) +{ + PyObject *return_value = NULL; + int _return_value; + + _return_value = Test_metho_not_default_return_converter_impl(self, a); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyLong_FromLong((long)_return_value); + +exit: + return return_value; +} + +static int +Test_metho_not_default_return_converter_impl(TestObj *self, PyObject *a) +/*[clinic end generated code: output=3350de11bd538007 input=428657129b521177]*/ + + +/*[clinic input] +Test.an_metho_arg_named_arg + arg: int + Name should be mangled to 'arg_' in generated output. + / +[clinic start generated code]*/ + +PyDoc_STRVAR(Test_an_metho_arg_named_arg__doc__, +"an_metho_arg_named_arg($self, arg, /)\n" +"--\n" +"\n" +"\n" +"\n" +" arg\n" +" Name should be mangled to \'arg_\' in generated output."); + +#define TEST_AN_METHO_ARG_NAMED_ARG_METHODDEF \ + {"an_metho_arg_named_arg", (PyCFunction)Test_an_metho_arg_named_arg, METH_O, Test_an_metho_arg_named_arg__doc__}, + +static PyObject * +Test_an_metho_arg_named_arg_impl(TestObj *self, int arg); + +static PyObject * +Test_an_metho_arg_named_arg(TestObj *self, PyObject *arg_) +{ + PyObject *return_value = NULL; + int arg; + + arg = _PyLong_AsInt(arg_); + if (arg == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = Test_an_metho_arg_named_arg_impl(self, arg); + +exit: + return return_value; +} + +static PyObject * +Test_an_metho_arg_named_arg_impl(TestObj *self, int arg) +/*[clinic end generated code: output=7d590626642194ae input=2a53a57cf5624f95]*/ + + +/*[clinic input] +Test.__init__ + *args: object + / +Varargs init method. For example, nargs is translated to PyTuple_GET_SIZE. +[clinic start generated code]*/ + +PyDoc_STRVAR(Test___init____doc__, +"Test(*args)\n" +"--\n" +"\n" +"Varargs init method. For example, nargs is translated to PyTuple_GET_SIZE."); + +static int +Test___init___impl(TestObj *self, PyObject *args); + +static int +Test___init__(PyObject *self, PyObject *args, PyObject *kwargs) +{ + int return_value = -1; + PyObject *__clinic_args = NULL; + + if ((Py_IS_TYPE(self, TestType) || + Py_TYPE(self)->tp_new == TestType->tp_new) && + !_PyArg_NoKeywords("Test", kwargs)) { + goto exit; + } + if (!_PyArg_CheckPositional("Test", PyTuple_GET_SIZE(args), 0, PY_SSIZE_T_MAX)) { + goto exit; + } + __clinic_args = PyTuple_GetSlice(0, -1); + return_value = Test___init___impl((TestObj *)self, __clinic_args); + +exit: + Py_XDECREF(__clinic_args); + return return_value; +} + +static int +Test___init___impl(TestObj *self, PyObject *args) +/*[clinic end generated code: output=126ad63fc2e5139e input=96c3ddc0cd38fc0c]*/ + + +/*[clinic input] +@classmethod +Test.__new__ + *args: object + / +Varargs new method. For example, nargs is translated to PyTuple_GET_SIZE. +[clinic start generated code]*/ + +PyDoc_STRVAR(Test__doc__, +"Test(*args)\n" +"--\n" +"\n" +"Varargs new method. For example, nargs is translated to PyTuple_GET_SIZE."); + +static PyObject * +Test_impl(PyTypeObject *type, PyObject *args); + +static PyObject * +Test(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + PyObject *__clinic_args = NULL; + + if ((type == TestType || + type->tp_init == TestType->tp_init) && + !_PyArg_NoKeywords("Test", kwargs)) { + goto exit; + } + if (!_PyArg_CheckPositional("Test", PyTuple_GET_SIZE(args), 0, PY_SSIZE_T_MAX)) { + goto exit; + } + __clinic_args = PyTuple_GetSlice(0, -1); + return_value = Test_impl(type, __clinic_args); + +exit: + Py_XDECREF(__clinic_args); + return return_value; +} + +static PyObject * +Test_impl(PyTypeObject *type, PyObject *args) +/*[clinic end generated code: output=4f01d446cfe4aeb9 input=26a672e2e9750120]*/ + + +/*[clinic input] +Test.__init__ + a: object +Init method with positional or keyword arguments. +[clinic start generated code]*/ + +PyDoc_STRVAR(Test___init____doc__, +"Test(a)\n" +"--\n" +"\n" +"Init method with positional or keyword arguments."); + +static int +Test___init___impl(TestObj *self, PyObject *a); + +static int +Test___init__(PyObject *self, PyObject *args, PyObject *kwargs) +{ + int return_value = -1; + static const char * const _keywords[] = {"a", NULL}; + static _PyArg_Parser _parser = {NULL, _keywords, "Test", 0}; + PyObject *argsbuf[1]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + PyObject *a; + + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 1, 0, argsbuf); + if (!fastargs) { + goto exit; + } + a = fastargs[0]; + return_value = Test___init___impl((TestObj *)self, a); + +exit: + return return_value; +} + +static int +Test___init___impl(TestObj *self, PyObject *a) +/*[clinic end generated code: output=5afcf1a525211a09 input=a8f9222a6ab35c59]*/ + + +/*[clinic input] +@classmethod +Test.class_method +[clinic start generated code]*/ + +PyDoc_STRVAR(Test_class_method__doc__, +"class_method($type, /)\n" +"--\n" +"\n"); + +#define TEST_CLASS_METHOD_METHODDEF \ + {"class_method", (PyCFunction)Test_class_method, METH_NOARGS|METH_CLASS, Test_class_method__doc__}, + +static PyObject * +Test_class_method_impl(PyTypeObject *type); + +static PyObject * +Test_class_method(PyTypeObject *type, PyObject *Py_UNUSED(ignored)) +{ + return Test_class_method_impl(type); +} + +static PyObject * +Test_class_method_impl(PyTypeObject *type) +/*[clinic end generated code: output=47fb7ecca1abcaaa input=43bc4a0494547b80]*/ + + +/*[clinic input] +@staticmethod +Test.static_method +[clinic start generated code]*/ + +PyDoc_STRVAR(Test_static_method__doc__, +"static_method()\n" +"--\n" +"\n"); + +#define TEST_STATIC_METHOD_METHODDEF \ + {"static_method", (PyCFunction)Test_static_method, METH_NOARGS|METH_STATIC, Test_static_method__doc__}, + +static PyObject * +Test_static_method_impl(); + +static PyObject * +Test_static_method(void *null, PyObject *Py_UNUSED(ignored)) +{ + return Test_static_method_impl(); +} + +static PyObject * +Test_static_method_impl() +/*[clinic end generated code: output=82524a63025cf7ab input=dae892fac55ae72b]*/ + + +/*[clinic input] +@coexist +Test.meth_coexist +[clinic start generated code]*/ + +PyDoc_STRVAR(Test_meth_coexist__doc__, +"meth_coexist($self, /)\n" +"--\n" +"\n"); + +#define TEST_METH_COEXIST_METHODDEF \ + {"meth_coexist", (PyCFunction)Test_meth_coexist, METH_NOARGS|METH_COEXIST, Test_meth_coexist__doc__}, + +static PyObject * +Test_meth_coexist_impl(TestObj *self); + +static PyObject * +Test_meth_coexist(TestObj *self, PyObject *Py_UNUSED(ignored)) +{ + return Test_meth_coexist_impl(self); +} + +static PyObject * +Test_meth_coexist_impl(TestObj *self) +/*[clinic end generated code: output=808a293d0cd27439 input=2a1d75b5e6fec6dd]*/ diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index f2e0c5e03f8b9f..fa65a41ec30d06 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -997,6 +997,43 @@ def test_defining_class_param_cannot_be_optional(self): out = self.parse_function_should_fail(block) self.assertEqual(out, expected_error_msg) + def test_slot_methods_cannot_access_defining_class(self): + block = """ + module foo + class Foo "" "" + Foo.__init__ + cls: defining_class + a: object + """ + msg = "Slot methods cannot access their defining class." + with self.assertRaisesRegex(ValueError, msg): + self.parse_function(block) + + def test_new_must_be_a_class_method(self): + expected_error_msg = ( + "Error on line 0:\n" + "__new__ must be a class method!\n" + ) + out = self.parse_function_should_fail(""" + module foo + class Foo "" "" + Foo.__new__ + """) + self.assertEqual(out, expected_error_msg) + + def test_init_must_be_a_normal_method(self): + expected_error_msg = ( + "Error on line 0:\n" + "__init__ must be a normal method, not a class or static method!\n" + ) + out = self.parse_function_should_fail(""" + module foo + class Foo "" "" + @classmethod + Foo.__init__ + """) + self.assertEqual(out, expected_error_msg) + def parse(self, text): c = FakeClinic() parser = DSLParser(c) From 2186212191f2ef149d73073b4583d74a49d176e2 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 14 Jul 2023 04:02:25 -0400 Subject: [PATCH 002/632] [3.11] gh-106446: Fix failed doctest in stdtypes (GH-106447) (#106742) (cherry picked from commit 89867d2491c0c3ef77bc237899b2f0762f43c03c) Co-authored-by: Charlie Zhao Co-authored-by: Terry Jan Reedy --- Doc/library/stdtypes.rst | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 31eedd04325552..f47ed686136ffc 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -3903,7 +3903,7 @@ copying. >>> m = memoryview(bytearray(b'abc')) >>> mm = m.toreadonly() >>> mm.tolist() - [89, 98, 99] + [97, 98, 99] >>> mm[0] = 42 Traceback (most recent call last): File "", line 1, in @@ -3959,6 +3959,7 @@ copying. :mod:`struct` syntax. One of the formats must be a byte format ('B', 'b' or 'c'). The byte length of the result must be the same as the original length. + Note that all byte lengths may depend on the operating system. Cast 1D/long to 1D/unsigned bytes:: @@ -3989,8 +3990,8 @@ copying. >>> x = memoryview(b) >>> x[0] = b'a' Traceback (most recent call last): - File "", line 1, in - ValueError: memoryview: invalid value for format "B" + ... + TypeError: memoryview: invalid type for format 'B' >>> y = x.cast('c') >>> y[0] = b'a' >>> b @@ -4735,8 +4736,10 @@ An example of dictionary view usage:: >>> # set operations >>> keys & {'eggs', 'bacon', 'salad'} {'bacon'} - >>> keys ^ {'sausage', 'juice'} - {'juice', 'sausage', 'bacon', 'spam'} + >>> keys ^ {'sausage', 'juice'} == {'juice', 'sausage', 'bacon', 'spam'} + True + >>> keys | ['juice', 'juice', 'juice'] == {'bacon', 'spam', 'juice'} + True >>> # get back a read-only proxy for the original dictionary >>> values.mapping @@ -4943,8 +4946,8 @@ exception to disallow mistakes like ``dict[str][str]``:: >>> dict[str][str] Traceback (most recent call last): - File "", line 1, in - TypeError: There are no type variables left in dict[str] + ... + TypeError: dict[str] is not a generic class However, such expressions are valid when :ref:`type variables ` are used. The index must have as many elements as there are type variable items @@ -5150,13 +5153,15 @@ enables cleaner type hinting syntax compared to :data:`typing.Union`. >>> isinstance("", int | str) True - However, union objects containing :ref:`parameterized generics - ` cannot be used:: + However, :ref:`parameterized generics ` in + union objects cannot be checked:: - >>> isinstance(1, int | list[int]) + >>> isinstance(1, int | list[int]) # short-circuit evaluation + True + >>> isinstance([1], int | list[int]) Traceback (most recent call last): - File "", line 1, in - TypeError: isinstance() argument 2 cannot contain a parameterized generic + ... + TypeError: isinstance() argument 2 cannot be a parameterized generic The user-exposed type for the union object can be accessed from :data:`types.UnionType` and used for :func:`isinstance` checks. An object cannot be @@ -5472,7 +5477,7 @@ types, where they are relevant. Some of these are not reported by the definition order. Example:: >>> int.__subclasses__() - [] + [, , , ] .. _int_max_str_digits: @@ -5508,7 +5513,7 @@ When an operation would exceed the limit, a :exc:`ValueError` is raised: >>> _ = int('2' * 5432) Traceback (most recent call last): ... - ValueError: Exceeds the limit (4300 digits) for integer string conversion: value has 5432 digits; use sys.set_int_max_str_digits() to increase the limit. + ValueError: Exceeds the limit (4300 digits) for integer string conversion: value has 5432 digits; use sys.set_int_max_str_digits() to increase the limit >>> i = int('2' * 4300) >>> len(str(i)) 4300 @@ -5516,7 +5521,7 @@ When an operation would exceed the limit, a :exc:`ValueError` is raised: >>> len(str(i_squared)) Traceback (most recent call last): ... - ValueError: Exceeds the limit (4300 digits) for integer string conversion: value has 8599 digits; use sys.set_int_max_str_digits() to increase the limit. + ValueError: Exceeds the limit (4300 digits) for integer string conversion; use sys.set_int_max_str_digits() to increase the limit >>> len(hex(i_squared)) 7144 >>> assert int(hex(i_squared), base=16) == i*i # Hexadecimal is unlimited. From d488970ae6e4b0de1212e202602be686de49ab7a Mon Sep 17 00:00:00 2001 From: Dennis Sweeney <36520290+sweeneyde@users.noreply.github.com> Date: Fri, 14 Jul 2023 22:17:09 -0400 Subject: [PATCH 003/632] =?UTF-8?q?[3.11]=20gh-105235:=20Prevent=20reading?= =?UTF-8?q?=20outside=20buffer=20during=20mmap.find()=20(=E2=80=A6=20(#106?= =?UTF-8?q?710)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [3.11] gh-105235: Prevent reading outside buffer during mmap.find() (GH-105252) * Add a special case for s[-m:] == p in _PyBytes_Find * Add tests for _PyBytes_Find * Make sure that start <= end in mmap.find. (cherry picked from commit ab86426a3472ab68747815299d390b213793c3d1) --- Lib/test/test_mmap.py | 21 ++++ ...-06-02-19-37-29.gh-issue-105235.fgFGTi.rst | 1 + Modules/_testinternalcapi.c | 114 ++++++++++++++++++ Modules/mmapmodule.c | 7 +- Objects/bytesobject.c | 21 +++- 5 files changed, 161 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-06-02-19-37-29.gh-issue-105235.fgFGTi.rst diff --git a/Lib/test/test_mmap.py b/Lib/test/test_mmap.py index 517cbe0cb115ab..bab868600895c1 100644 --- a/Lib/test/test_mmap.py +++ b/Lib/test/test_mmap.py @@ -299,6 +299,27 @@ def test_find_end(self): self.assertEqual(m.find(b'one', 1, -2), -1) self.assertEqual(m.find(bytearray(b'one')), 0) + for i in range(-n-1, n+1): + for j in range(-n-1, n+1): + for p in [b"o", b"on", b"two", b"ones", b"s"]: + expected = data.find(p, i, j) + self.assertEqual(m.find(p, i, j), expected, (p, i, j)) + + def test_find_does_not_access_beyond_buffer(self): + try: + flags = mmap.MAP_PRIVATE | mmap.MAP_ANONYMOUS + PAGESIZE = mmap.PAGESIZE + PROT_NONE = 0 + PROT_READ = mmap.PROT_READ + except AttributeError as e: + raise unittest.SkipTest("mmap flags unavailable") from e + for i in range(0, 2049): + with mmap.mmap(-1, PAGESIZE * (i + 1), + flags=flags, prot=PROT_NONE) as guard: + with mmap.mmap(-1, PAGESIZE * (i + 2048), + flags=flags, prot=PROT_READ) as fm: + fm.find(b"fo", -2) + def test_rfind(self): # test the new 'end' parameter works as expected diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-06-02-19-37-29.gh-issue-105235.fgFGTi.rst b/Misc/NEWS.d/next/Core and Builtins/2023-06-02-19-37-29.gh-issue-105235.fgFGTi.rst new file mode 100644 index 00000000000000..c28d0101cd4bad --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-06-02-19-37-29.gh-issue-105235.fgFGTi.rst @@ -0,0 +1 @@ +Prevent out-of-bounds memory access during ``mmap.find()`` calls. diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index 238de749fffc5d..4a8c43658998fa 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -14,6 +14,7 @@ #include "Python.h" #include "pycore_atomic_funcs.h" // _Py_atomic_int_get() #include "pycore_bitutils.h" // _Py_bswap32() +#include "pycore_bytesobject.h" // _PyBytes_Find() #include "pycore_fileutils.h" // _Py_normpath #include "pycore_frame.h" // _PyInterpreterFrame #include "pycore_gc.h" // PyGC_Head @@ -380,6 +381,118 @@ test_edit_cost(PyObject *self, PyObject *Py_UNUSED(args)) } +static int +check_bytes_find(const char *haystack0, const char *needle0, + int offset, Py_ssize_t expected) +{ + Py_ssize_t len_haystack = strlen(haystack0); + Py_ssize_t len_needle = strlen(needle0); + Py_ssize_t result_1 = _PyBytes_Find(haystack0, len_haystack, + needle0, len_needle, offset); + if (result_1 != expected) { + PyErr_Format(PyExc_AssertionError, + "Incorrect result_1: '%s' in '%s' (offset=%zd)", + needle0, haystack0, offset); + return -1; + } + // Allocate new buffer with no NULL terminator. + char *haystack = PyMem_Malloc(len_haystack); + if (haystack == NULL) { + PyErr_NoMemory(); + return -1; + } + char *needle = PyMem_Malloc(len_needle); + if (needle == NULL) { + PyMem_Free(haystack); + PyErr_NoMemory(); + return -1; + } + memcpy(haystack, haystack0, len_haystack); + memcpy(needle, needle0, len_needle); + Py_ssize_t result_2 = _PyBytes_Find(haystack, len_haystack, + needle, len_needle, offset); + PyMem_Free(haystack); + PyMem_Free(needle); + if (result_2 != expected) { + PyErr_Format(PyExc_AssertionError, + "Incorrect result_2: '%s' in '%s' (offset=%zd)", + needle0, haystack0, offset); + return -1; + } + return 0; +} + +static int +check_bytes_find_large(Py_ssize_t len_haystack, Py_ssize_t len_needle, + const char *needle) +{ + char *zeros = PyMem_RawCalloc(len_haystack, 1); + if (zeros == NULL) { + PyErr_NoMemory(); + return -1; + } + Py_ssize_t res = _PyBytes_Find(zeros, len_haystack, needle, len_needle, 0); + PyMem_RawFree(zeros); + if (res != -1) { + PyErr_Format(PyExc_AssertionError, + "check_bytes_find_large(%zd, %zd) found %zd", + len_haystack, len_needle, res); + return -1; + } + return 0; +} + +static PyObject * +test_bytes_find(PyObject *self, PyObject *Py_UNUSED(args)) +{ + #define CHECK(H, N, O, E) do { \ + if (check_bytes_find(H, N, O, E) < 0) { \ + return NULL; \ + } \ + } while (0) + + CHECK("", "", 0, 0); + CHECK("Python", "", 0, 0); + CHECK("Python", "", 3, 3); + CHECK("Python", "", 6, 6); + CHECK("Python", "yth", 0, 1); + CHECK("ython", "yth", 1, 1); + CHECK("thon", "yth", 2, -1); + CHECK("Python", "thon", 0, 2); + CHECK("ython", "thon", 1, 2); + CHECK("thon", "thon", 2, 2); + CHECK("hon", "thon", 3, -1); + CHECK("Pytho", "zz", 0, -1); + CHECK("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "ab", 0, -1); + CHECK("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "ba", 0, -1); + CHECK("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "bb", 0, -1); + CHECK("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab", "ab", 0, 30); + CHECK("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaba", "ba", 0, 30); + CHECK("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaabb", "bb", 0, 30); + #undef CHECK + + // Hunt for segfaults + // n, m chosen here so that (n - m) % (m + 1) == 0 + // This would make default_find in fastsearch.h access haystack[n]. + if (check_bytes_find_large(2048, 2, "ab") < 0) { + return NULL; + } + if (check_bytes_find_large(4096, 16, "0123456789abcdef") < 0) { + return NULL; + } + if (check_bytes_find_large(8192, 2, "ab") < 0) { + return NULL; + } + if (check_bytes_find_large(16384, 4, "abcd") < 0) { + return NULL; + } + if (check_bytes_find_large(32768, 2, "ab") < 0) { + return NULL; + } + Py_RETURN_NONE; +} + + static PyObject * normalize_path(PyObject *self, PyObject *filename) { @@ -537,6 +650,7 @@ static PyMethodDef TestMethods[] = { {"reset_path_config", test_reset_path_config, METH_NOARGS}, {"test_atomic_funcs", test_atomic_funcs, METH_NOARGS}, {"test_edit_cost", test_edit_cost, METH_NOARGS}, + {"test_bytes_find", test_bytes_find, METH_NOARGS}, {"normalize_path", normalize_path, METH_O, NULL}, {"get_getpath_codeobject", get_getpath_codeobject, METH_NOARGS, NULL}, {"EncodeLocaleEx", encode_locale_ex, METH_VARARGS}, diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c index 4b0c1e27e0c532..8ff63f51188ad5 100644 --- a/Modules/mmapmodule.c +++ b/Modules/mmapmodule.c @@ -351,12 +351,17 @@ mmap_gfind(mmap_object *self, Py_ssize_t res; CHECK_VALID_OR_RELEASE(NULL, view); - if (reverse) { + if (end < start) { + res = -1; + } + else if (reverse) { + assert(0 <= start && start <= end && end <= self->size); res = _PyBytes_ReverseFind( self->data + start, end - start, view.buf, view.len, start); } else { + assert(0 <= start && start <= end && end <= self->size); res = _PyBytes_Find( self->data + start, end - start, view.buf, view.len, start); diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index 3b0ff9a19d977f..279579f63418c0 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -1283,8 +1283,25 @@ _PyBytes_Find(const char *haystack, Py_ssize_t len_haystack, const char *needle, Py_ssize_t len_needle, Py_ssize_t offset) { - return stringlib_find(haystack, len_haystack, - needle, len_needle, offset); + assert(len_haystack >= 0); + assert(len_needle >= 0); + // Extra checks because stringlib_find accesses haystack[len_haystack]. + if (len_needle == 0) { + return offset; + } + if (len_needle > len_haystack) { + return -1; + } + assert(len_haystack >= 1); + Py_ssize_t res = stringlib_find(haystack, len_haystack - 1, + needle, len_needle, offset); + if (res == -1) { + Py_ssize_t last_align = len_haystack - len_needle; + if (memcmp(haystack + last_align, needle, len_needle) == 0) { + return offset + last_align; + } + } + return res; } Py_ssize_t From b132b77fb11a6c9b8de5cf0f4afb8acc7f57a96c Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 15 Jul 2023 02:48:01 -0700 Subject: [PATCH 004/632] [3.11] gh-106368: Increase Argument Clinic BlockParser test coverage (GH-106759) (#106770) (cherry picked from commit 2d7d1aa4bcd5da0177458b22b1b856db76aa20d4) Co-authored-by: Erlend E. Aasland --- Lib/test/test_clinic.py | 100 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 96 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index fa65a41ec30d06..0e3fbb7627ab5a 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -18,6 +18,19 @@ from clinic import DSLParser +class _ParserBase(TestCase): + maxDiff = None + + def expect_parser_failure(self, parser, _input): + with support.captured_stdout() as stdout: + with self.assertRaises(SystemExit): + parser(_input) + return stdout.getvalue() + + def parse_function_should_fail(self, _input): + return self.expect_parser_failure(self.parse_function, _input) + + class FakeConverter: def __init__(self, name, args): self.name = name @@ -88,7 +101,15 @@ def directive(self, name, args): _module_and_class = clinic.Clinic._module_and_class -class ClinicWholeFileTest(TestCase): + +class ClinicWholeFileTest(_ParserBase): + def setUp(self): + self.clinic = clinic.Clinic(clinic.CLanguage(None), filename="test.c") + + def expect_failure(self, raw): + _input = dedent(raw).strip() + return self.expect_parser_failure(self.clinic.parse, _input) + def test_eol(self): # regression test: # clinic's block parser didn't recognize @@ -98,15 +119,86 @@ def test_eol(self): # so it would spit out an end line for you. # and since you really already had one, # the last line of the block got corrupted. - c = clinic.Clinic(clinic.CLanguage(None), filename="file") raw = "/*[clinic]\nfoo\n[clinic]*/" - cooked = c.parse(raw).splitlines() + cooked = self.clinic.parse(raw).splitlines() end_line = cooked[2].rstrip() # this test is redundant, it's just here explicitly to catch # the regression test so we don't forget what it looked like self.assertNotEqual(end_line, "[clinic]*/[clinic]*/") self.assertEqual(end_line, "[clinic]*/") + def test_mangled_marker_line(self): + raw = """ + /*[clinic input] + [clinic start generated code]*/ + /*[clinic end generated code: foo]*/ + """ + msg = ( + 'Error in file "test.c" on line 3:\n' + "Mangled Argument Clinic marker line: '/*[clinic end generated code: foo]*/'\n" + ) + out = self.expect_failure(raw) + self.assertEqual(out, msg) + + def test_checksum_mismatch(self): + raw = """ + /*[clinic input] + [clinic start generated code]*/ + /*[clinic end generated code: output=0123456789abcdef input=fedcba9876543210]*/ + """ + msg = ( + 'Error in file "test.c" on line 3:\n' + 'Checksum mismatch!\n' + 'Expected: 0123456789abcdef\n' + 'Computed: da39a3ee5e6b4b0d\n' + ) + out = self.expect_failure(raw) + self.assertIn(msg, out) + + def test_garbage_after_stop_line(self): + raw = """ + /*[clinic input] + [clinic start generated code]*/foobarfoobar! + """ + msg = ( + 'Error in file "test.c" on line 2:\n' + "Garbage after stop line: 'foobarfoobar!'\n" + ) + out = self.expect_failure(raw) + self.assertEqual(out, msg) + + def test_whitespace_before_stop_line(self): + raw = """ + /*[clinic input] + [clinic start generated code]*/ + """ + msg = ( + 'Error in file "test.c" on line 2:\n' + "Whitespace is not allowed before the stop line: ' [clinic start generated code]*/'\n" + ) + out = self.expect_failure(raw) + self.assertEqual(out, msg) + + def test_parse_with_body_prefix(self): + clang = clinic.CLanguage(None) + clang.body_prefix = "//" + clang.start_line = "//[{dsl_name} start]" + clang.stop_line = "//[{dsl_name} stop]" + cl = clinic.Clinic(clang, filename="test.c") + raw = dedent(""" + //[clinic start] + //module test + //[clinic stop] + """).strip() + out = cl.parse(raw) + expected = dedent(""" + //[clinic start] + //module test + // + //[clinic stop] + /*[clinic end generated code: output=da39a3ee5e6b4b0d input=65fab8adff58cf08]*/ + """).lstrip() # Note, lstrip() because of the newline + self.assertEqual(out, expected) class ClinicGroupPermuterTest(TestCase): @@ -285,7 +377,7 @@ def test_clinic_1(self): """) -class ClinicParserTest(TestCase): +class ClinicParserTest(_ParserBase): def checkDocstring(self, fn, expected): self.assertTrue(hasattr(fn, "docstring")) self.assertEqual(fn.docstring.strip(), From 7dead6a33ac786465c3d927d5c2025caf0c1a71b Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Sat, 15 Jul 2023 11:34:29 +0100 Subject: [PATCH 005/632] [3.11] gh-106745: typing docs: Clarify that removal of PEP-585 aliases is not currently planned (#106748) (#106773) --- Doc/library/typing.rst | 58 +++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 25b7cfe703a135..d03f0af1bc6b61 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -2745,6 +2745,7 @@ Constant .. versionadded:: 3.5.2 .. _generic-concrete-collections: +.. _deprecated-typing-aliases: Deprecated aliases ------------------ @@ -2753,16 +2754,21 @@ This module defines several deprecated aliases to pre-existing standard library classes. These were originally included in the typing module in order to support parameterizing these generic classes using ``[]``. However, the aliases became redundant in Python 3.9 when the -corresponding pre-existing classes were enhanced to support ``[]``. +corresponding pre-existing classes were enhanced to support ``[]`` (see +:pep:`585`). -The redundant types are deprecated as of Python 3.9 but no -deprecation warnings are issued by the interpreter. -It is expected that type checkers will flag the deprecated types -when the checked program targets Python 3.9 or newer. +The redundant types are deprecated as of Python 3.9. However, while the aliases +may be removed at some point, removal of these aliases is not currently +planned. As such, no deprecation warnings are currently issued by the +interpreter for these aliases. -The deprecated types will be removed from the :mod:`typing` module -no sooner than the first Python version released 5 years after the release of Python 3.9.0. -See details in :pep:`585`—*Type Hinting Generics In Standard Collections*. +If at some point it is decided to remove these deprecated aliases, a +deprecation warning will be issued by the interpreter for at least two releases +prior to removal. The aliases are guaranteed to remain in the typing module +without deprecation warnings until at least Python 3.14. + +Type checkers are encouraged to flag uses of the deprecated types if the +program they are checking targets a minimum Python version of 3.9 or newer. .. _corresponding-to-built-in-types: @@ -3295,16 +3301,26 @@ Certain features in ``typing`` are deprecated and may be removed in a future version of Python. The following table summarizes major deprecations for your convenience. This is subject to change, and not all deprecations are listed. -+----------------------------------+---------------+-------------------+----------------+ -| Feature | Deprecated in | Projected removal | PEP/issue | -+==================================+===============+===================+================+ -| ``typing.io`` and ``typing.re`` | 3.8 | 3.13 | :issue:`38291` | -| submodules | | | | -+----------------------------------+---------------+-------------------+----------------+ -| ``typing`` versions of standard | 3.9 | Undecided | :pep:`585` | -| collections | | | | -+----------------------------------+---------------+-------------------+----------------+ -| ``typing.ByteString`` | 3.9 | 3.14 | :gh:`91896` | -+----------------------------------+---------------+-------------------+----------------+ -| ``typing.Text`` | 3.11 | Undecided | :gh:`92332` | -+----------------------------------+---------------+-------------------+----------------+ +.. list-table:: + :header-rows: 1 + + * - Feature + - Deprecated in + - Projected removal + - PEP/issue + * - ``typing.io`` and ``typing.re`` submodules + - 3.8 + - 3.13 + - :issue:`38291` + * - ``typing`` versions of standard collections + - 3.9 + - Undecided (see :ref:`deprecated-typing-aliases` for more information) + - :pep:`585` + * - :class:`typing.ByteString` + - 3.9 + - 3.14 + - :gh:`91896` + * - :data:`typing.Text` + - 3.11 + - Undecided + - :gh:`92332` From 465f5b09df5bb975ba6226712f2cf172b8421f0c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 15 Jul 2023 10:15:38 -0400 Subject: [PATCH 006/632] [3.11] gh-106752: Sync with zipp 3.16.2 (GH-106757) (#106778) * gh-106752: Sync with zipp 3.16.2 (#106757) * gh-106752: Sync with zipp 3.16.2 * Add blurb (cherry picked from commit 22980dc7c9dcec4b74fea815542601ef582c230e) * [3.11] gh-106752: Sync with zipp 3.16.2 (GH-106757) * gh-106752: Sync with zipp 3.16.2 * Add blurb. (cherry picked from commit 22980dc7c9dcec4b74fea815542601ef582c230e) Co-authored-by: Jason R. Coombs * Remove Python 3.12 concerns from changelog. --- Lib/test/test_zipfile.py | 9 +++++++++ Lib/zipfile.py | 11 +++++++---- .../2023-07-14-16-54-13.gh-issue-106752.BT1Yxw.rst | 3 +++ 3 files changed, 19 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-07-14-16-54-13.gh-issue-106752.BT1Yxw.rst diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index bb2c24c8c50c58..c8e0159765ec2c 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -3443,6 +3443,13 @@ def test_suffixes(self, alpharep): e = root / '.hgrc' assert e.suffixes == [] + @pass_alpharep + def test_suffix_no_filename(self, alpharep): + alpharep.filename = None + root = zipfile.Path(alpharep) + assert root.joinpath('example').suffix == "" + assert root.joinpath('example').suffixes == [] + @pass_alpharep def test_stem(self, alpharep): """ @@ -3460,6 +3467,8 @@ def test_stem(self, alpharep): d = root / "d" assert d.stem == "d" + assert (root / ".gitignore").stem == ".gitignore" + @pass_alpharep def test_root_parent(self, alpharep): root = zipfile.Path(alpharep) diff --git a/Lib/zipfile.py b/Lib/zipfile.py index 1dec6a8b97bc4f..6189db5e3e420d 100644 --- a/Lib/zipfile.py +++ b/Lib/zipfile.py @@ -2420,21 +2420,24 @@ def open(self, mode='r', *args, pwd=None, **kwargs): encoding, args, kwargs = _extract_text_encoding(*args, **kwargs) return io.TextIOWrapper(stream, encoding, *args, **kwargs) + def _base(self): + return pathlib.PurePosixPath(self.at or self.root.filename) + @property def name(self): - return pathlib.Path(self.at).name or self.filename.name + return self._base().name @property def suffix(self): - return pathlib.Path(self.at).suffix or self.filename.suffix + return self._base().suffix @property def suffixes(self): - return pathlib.Path(self.at).suffixes or self.filename.suffixes + return self._base().suffixes @property def stem(self): - return pathlib.Path(self.at).stem or self.filename.stem + return self._base().stem @property def filename(self): diff --git a/Misc/NEWS.d/next/Library/2023-07-14-16-54-13.gh-issue-106752.BT1Yxw.rst b/Misc/NEWS.d/next/Library/2023-07-14-16-54-13.gh-issue-106752.BT1Yxw.rst new file mode 100644 index 00000000000000..d36c97da49d1b8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-07-14-16-54-13.gh-issue-106752.BT1Yxw.rst @@ -0,0 +1,3 @@ +Fixed several bug in zipfile.Path in +``name``/``suffix``/``suffixes``/``stem`` operations when no filename is +present and the Path is not at the root of the zipfile. From c73f9c045e3adc130bda6a3e2b986b639c0621d5 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 15 Jul 2023 15:50:43 -0700 Subject: [PATCH 007/632] [3.11] Docs: Normalize Argument Clinic How-To section capitalization (GH-106788) (#106792) (cherry picked from commit 8c177294899b621fe04ae755abd41b4d319dd4b5) Co-authored-by: Erlend E. Aasland --- Doc/howto/clinic.rst | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/Doc/howto/clinic.rst b/Doc/howto/clinic.rst index c4bf6ef764777a..70b3e21d81a61c 100644 --- a/Doc/howto/clinic.rst +++ b/Doc/howto/clinic.rst @@ -27,7 +27,8 @@ Argument Clinic How-To version of Argument Clinic that ships with the next version of CPython *could* be totally incompatible and break all your code. -The Goals Of Argument Clinic + +The goals of Argument Clinic ============================ Argument Clinic's primary goal @@ -78,7 +79,7 @@ and it should be able to do many interesting and smart things with all the information you give it. -Basic Concepts And Usage +Basic concepts and usage ======================== Argument Clinic ships with CPython; you'll find it in ``Tools/clinic/clinic.py``. @@ -141,7 +142,7 @@ For the sake of clarity, here's the terminology we'll use with Argument Clinic: a block.) -Converting Your First Function +Converting your first function ============================== The best way to get a sense of how Argument Clinic works is to @@ -550,7 +551,8 @@ Let's dive in! Congratulations, you've ported your first function to work with Argument Clinic! -Advanced Topics + +Advanced topics =============== Now that you've had some experience working with Argument Clinic, it's time @@ -628,7 +630,8 @@ after the last argument). Currently the generated code will use :c:func:`PyArg_ParseTuple`, but this will change soon. -Optional Groups + +Optional groups --------------- Some legacy functions have a tricky approach to parsing their arguments: @@ -888,6 +891,7 @@ available. For each converter it'll show you all the parameters it accepts, along with the default value for each parameter. Just run ``Tools/clinic/clinic.py --converters`` to see the full list. + Py_buffer --------- @@ -897,7 +901,6 @@ you *must* not call :c:func:`PyBuffer_Release` on the provided buffer. Argument Clinic generates code that does it for you (in the parsing function). - Advanced converters ------------------- @@ -964,6 +967,7 @@ value called ``NULL`` for just this reason: from Python's perspective it behaves like a default value of ``None``, but the C variable is initialized with ``NULL``. + Expressions specified as default values --------------------------------------- @@ -1021,7 +1025,6 @@ you're not permitted to use: * Tuple/list/set/dict literals. - Using a return converter ------------------------ @@ -1140,6 +1143,7 @@ then modifying it. Cloning is an all-or nothing proposition. Also, the function you are cloning from must have been previously defined in the current file. + Calling Python code ------------------- @@ -1374,6 +1378,7 @@ handle initialization and cleanup. You can see more examples of custom converters in the CPython source tree; grep the C files for the string ``CConverter``. + Writing a custom return converter --------------------------------- @@ -1388,8 +1393,9 @@ write your own return converter, please read ``Tools/clinic/clinic.py``, specifically the implementation of ``CReturnConverter`` and all its subclasses. + METH_O and METH_NOARGS ----------------------------------------------- +---------------------- To convert a function using ``METH_O``, make sure the function's single argument is using the ``object`` converter, and mark the @@ -1409,8 +1415,9 @@ any arguments. You can still use a self converter, a return converter, and specify a ``type`` argument to the object converter for ``METH_O``. + tp_new and tp_init functions ----------------------------------------------- +---------------------------- You can convert ``tp_new`` and ``tp_init`` functions. Just name them ``__new__`` or ``__init__`` as appropriate. Notes: @@ -1431,6 +1438,7 @@ them ``__new__`` or ``__init__`` as appropriate. Notes: (If your function doesn't support keywords, the parsing function generated will throw an exception if it receives any.) + Changing and redirecting Clinic's output ---------------------------------------- @@ -1715,7 +1723,7 @@ the file was not modified by hand before it gets overwritten. The #ifdef trick ----------------------------------------------- +---------------- If you're converting a function that isn't available on all platforms, there's a trick you can use to make life a little easier. The existing @@ -1795,7 +1803,6 @@ Argument Clinic added to your file (it'll be at the very bottom), then move it above the ``PyMethodDef`` structure where that macro is used. - Using Argument Clinic in Python files ------------------------------------- From 9aeb9d1e80a8b6ff3d613c6d7fc4efd49a9a8a79 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 16 Jul 2023 00:03:24 -0700 Subject: [PATCH 008/632] wasm: do not use inline comment in .editorconfig (GH-106610) It is no longer valid since 0.15.0 https://github.com/editorconfig/specification/blob/v0.15/index.rstGH-no-inline-comments (cherry picked from commit 64c0890b697783db9b3f67e3bb4dcee1165a0b9b) Co-authored-by: Eisuke Kawashima --- Tools/wasm/.editorconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tools/wasm/.editorconfig b/Tools/wasm/.editorconfig index da1aa6acaccc7e..4de5fe5954d84b 100644 --- a/Tools/wasm/.editorconfig +++ b/Tools/wasm/.editorconfig @@ -1,4 +1,5 @@ -root = false # This extends the root .editorconfig +# This extends the root .editorconfig +root = false [*.{html,js}] trim_trailing_whitespace = true From 501178ac9d13e95b5c70f28a01046b681acbf1f0 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 16 Jul 2023 01:38:46 -0700 Subject: [PATCH 009/632] [3.11] Docs search: Replace jQuery with vanilla JavaScript (GH-106743) (#106803) Docs search: Replace jQuery with vanilla JavaScript (GH-106743) * Replace jQuery with vanilla JavaScript * Switch 'var' to 'const' or 'let' (cherry picked from commit c02ee4503151105dc892018ebc7f633e7f3f62f8) Co-authored-by: Hugo van Kemenade --- Doc/tools/templates/search.html | 74 ++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 30 deletions(-) diff --git a/Doc/tools/templates/search.html b/Doc/tools/templates/search.html index f2ac2ea0f09873..852974461380f2 100644 --- a/Doc/tools/templates/search.html +++ b/Doc/tools/templates/search.html @@ -1,48 +1,62 @@ {% extends "!search.html" %} {% block extrahead %} {{ super() }} + -{% endblock %} \ No newline at end of file +{% endblock %} From 77104f551158cd399071e0d92b165d02569be6d7 Mon Sep 17 00:00:00 2001 From: Inada Naoki Date: Sun, 16 Jul 2023 20:43:56 +0900 Subject: [PATCH 010/632] Doc: devmode: add -Xdev option to example (GH-106253) (cherry picked from commit 83bd568d2b57337a91ef046c1f52f9ebb03a7803) Co-authored-by: Simone Rubino --- Doc/library/devmode.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/devmode.rst b/Doc/library/devmode.rst index 44e7d4f541d817..fd82b3a5b86313 100644 --- a/Doc/library/devmode.rst +++ b/Doc/library/devmode.rst @@ -198,7 +198,7 @@ descriptor" error when finalizing the file object: .. code-block:: shell-session - $ python3 script.py + $ python3 -X dev script.py import os script.py:10: ResourceWarning: unclosed file <_io.TextIOWrapper name='script.py' mode='r' encoding='UTF-8'> main() From 9532f0e6dd96aaba7aa50135ecab69923a674a15 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 16 Jul 2023 05:27:51 -0700 Subject: [PATCH 011/632] Doc: fix section levels of devmode doc (GH-106801) Doc: fix section levels of devmode doc (GH-106801) (cherry picked from commit e58960160fcb4fce63177fcd9ef605f887377767) Co-authored-by: Inada Naoki --- Doc/library/devmode.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/library/devmode.rst b/Doc/library/devmode.rst index fd82b3a5b86313..90138dd2a75f99 100644 --- a/Doc/library/devmode.rst +++ b/Doc/library/devmode.rst @@ -16,7 +16,7 @@ setting the :envvar:`PYTHONDEVMODE` environment variable to ``1``. See also :ref:`Python debug build `. Effects of the Python Development Mode -====================================== +-------------------------------------- Enabling the Python Development Mode is similar to the following command, but with additional effects described below:: @@ -107,7 +107,7 @@ value can be read from :data:`sys.flags.dev_mode `. ResourceWarning Example -======================= +----------------------- Example of a script counting the number of lines of the text file specified in the command line:: @@ -171,7 +171,7 @@ application more deterministic and more reliable. Bad file descriptor error example -================================= +--------------------------------- Script displaying the first line of itself:: From 6193f783d02190a66f287b3a36c187a9323dcc2b Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 17 Jul 2023 01:15:52 -0700 Subject: [PATCH 012/632] [3.11] gh-106780: Add __match_args__ to tutorial example (GH-106784) (#106820) Add Point definition with this attribute before example that needs it. (cherry picked from commit 7aa89e505d893cd5e6f33b84d66e5fa769089931) Co-authored-by: Terry Jan Reedy --- Doc/tutorial/controlflow.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Doc/tutorial/controlflow.rst b/Doc/tutorial/controlflow.rst index 4336bf50df40a7..e140f51f1dda78 100644 --- a/Doc/tutorial/controlflow.rst +++ b/Doc/tutorial/controlflow.rst @@ -343,7 +343,13 @@ Dotted names (like ``foo.bar``), attribute names (the ``x=`` and ``y=`` above) o (recognized by the "(...)" next to them like ``Point`` above) are never assigned to. Patterns can be arbitrarily nested. For example, if we have a short -list of points, we could match it like this:: +list of Points, with ``__match_args__`` added, we could match it like this:: + + class Point: + __match_args__ = ('x', 'y') + def __init__(self, x, y): + self.x = x + self.y = y match points: case []: From 263d8aa017bd6d858aa9488154bc160fa7249a9e Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Mon, 17 Jul 2023 12:07:52 -0500 Subject: [PATCH 013/632] [3.11] gh-99079: Update Windows build to use OpenSSL 3.0.9 (GH-106649) (GH-106761) Co-authored-by: Steve Dower --- .../Windows/2023-07-11-20-48-17.gh-issue-99079.CIMftz.rst | 1 + PCbuild/get_externals.bat | 4 ++-- PCbuild/openssl.props | 4 ++-- PCbuild/python.props | 4 ++-- PCbuild/readme.txt | 2 +- PCbuild/regen.targets | 3 ++- 6 files changed, 10 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2023-07-11-20-48-17.gh-issue-99079.CIMftz.rst diff --git a/Misc/NEWS.d/next/Windows/2023-07-11-20-48-17.gh-issue-99079.CIMftz.rst b/Misc/NEWS.d/next/Windows/2023-07-11-20-48-17.gh-issue-99079.CIMftz.rst new file mode 100644 index 00000000000000..11f411be0f17c5 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-07-11-20-48-17.gh-issue-99079.CIMftz.rst @@ -0,0 +1 @@ +Update Windows build to use OpenSSL 3.0.9 diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index 4fc37efd4e3331..d41955ac482c73 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -53,7 +53,7 @@ echo.Fetching external libraries... set libraries= set libraries=%libraries% bzip2-1.0.8 if NOT "%IncludeLibffiSrc%"=="false" set libraries=%libraries% libffi-3.4.4 -if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1u +if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-3.0.9 set libraries=%libraries% sqlite-3.42.0.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.12.1 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.12.1 @@ -77,7 +77,7 @@ echo.Fetching external binaries... set binaries= if NOT "%IncludeLibffi%"=="false" set binaries=%binaries% libffi-3.4.4 -if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.1u +if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-3.0.9 if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.12.1 if NOT "%IncludeSSLSrc%"=="false" set binaries=%binaries% nasm-2.11.06 diff --git a/PCbuild/openssl.props b/PCbuild/openssl.props index 6081d3c8c69641..a4186f01da21cd 100644 --- a/PCbuild/openssl.props +++ b/PCbuild/openssl.props @@ -10,10 +10,10 @@ - <_DLLSuffix>-1_1 + <_DLLSuffix>-3 <_DLLSuffix Condition="$(Platform) == 'ARM'">$(_DLLSuffix)-arm <_DLLSuffix Condition="$(Platform) == 'ARM64'">$(_DLLSuffix)-arm64 - $(_DLLSuffix) + $(_DLLSuffix) <_SSLDLL Include="$(opensslOutDir)\libcrypto$(_DLLSuffix).dll" /> diff --git a/PCbuild/python.props b/PCbuild/python.props index 68052ef668aa6c..d3586235c82652 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -74,8 +74,8 @@ $(ExternalsDir)libffi-3.4.4\ $(libffiDir)$(ArchName)\ $(libffiOutDir)include - $(ExternalsDir)openssl-1.1.1u\ - $(ExternalsDir)openssl-bin-1.1.1u\$(ArchName)\ + $(ExternalsDir)openssl-3.0.9\ + $(ExternalsDir)openssl-bin-3.0.9\$(ArchName)\ $(opensslOutDir)include $(ExternalsDir)\nasm-2.11.06\ $(ExternalsDir)\zlib-1.2.13\ diff --git a/PCbuild/readme.txt b/PCbuild/readme.txt index d48f7e18eb3074..e9a09172274671 100644 --- a/PCbuild/readme.txt +++ b/PCbuild/readme.txt @@ -168,7 +168,7 @@ _lzma Homepage: https://tukaani.org/xz/ _ssl - Python wrapper for version 1.1.1u of the OpenSSL secure sockets + Python wrapper for version 3.0 of the OpenSSL secure sockets library, which is downloaded from our binaries repository at https://github.com/python/cpython-bin-deps. diff --git a/PCbuild/regen.targets b/PCbuild/regen.targets index 24b5ced1de0e01..d7cb6dbe72acea 100644 --- a/PCbuild/regen.targets +++ b/PCbuild/regen.targets @@ -97,8 +97,9 @@ <_LicenseSources Include="$(PySourcePath)LICENSE; $(PySourcePath)PC\crtlicense.txt; $(bz2Dir)LICENSE; - $(opensslOutDir)LICENSE; $(libffiDir)LICENSE;" /> + <_LicenseSources Include="$(opensslOutDir)LICENSE.txt" Condition="Exists('$(opensslOutDir)LICENSE.txt')" /> + <_LicenseSources Include="$(opensslOutDir)LICENSE" Condition="!Exists('$(opensslOutDir)LICENSE.txt')" /> <_LicenseSources Include="$(tcltkDir)tcllicense.terms; $(tcltkDir)tklicense.terms; $(tcltkDir)tixlicense.terms" Condition="$(IncludeTkinter)" /> From a782d51913222e340b75b2dd50e646be80dd035b Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 17 Jul 2023 13:31:10 -0700 Subject: [PATCH 014/632] [3.11] gh-106831: Fix NULL check of d2i_SSL_SESSION() result in _ssl.c (GH-106832) (#106836) gh-106831: Fix NULL check of d2i_SSL_SESSION() result in _ssl.c (GH-106832) (cherry picked from commit ebf2c56b33553a448da8f60fcd89a622f071b5f4) Co-authored-by: Nikita Sobolev --- .../Library/2023-07-17-21-45-15.gh-issue-106831.RqVq9X.rst | 2 ++ Modules/_ssl.c | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-07-17-21-45-15.gh-issue-106831.RqVq9X.rst diff --git a/Misc/NEWS.d/next/Library/2023-07-17-21-45-15.gh-issue-106831.RqVq9X.rst b/Misc/NEWS.d/next/Library/2023-07-17-21-45-15.gh-issue-106831.RqVq9X.rst new file mode 100644 index 00000000000000..d3b98626845392 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-07-17-21-45-15.gh-issue-106831.RqVq9X.rst @@ -0,0 +1,2 @@ +Fix potential missing ``NULL`` check of ``d2i_SSL_SESSION`` result in +``_ssl.c``. diff --git a/Modules/_ssl.c b/Modules/_ssl.c index c1a8eaaa2fa0a9..e3bb38e769c9b3 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -2800,7 +2800,7 @@ _ssl_session_dup(SSL_SESSION *session) { /* get length */ slen = i2d_SSL_SESSION(session, NULL); if (slen == 0 || slen > 0xFF00) { - PyErr_SetString(PyExc_ValueError, "i2d() failed."); + PyErr_SetString(PyExc_ValueError, "i2d() failed"); goto error; } if ((senc = PyMem_Malloc(slen)) == NULL) { @@ -2809,12 +2809,13 @@ _ssl_session_dup(SSL_SESSION *session) { } p = senc; if (!i2d_SSL_SESSION(session, &p)) { - PyErr_SetString(PyExc_ValueError, "i2d() failed."); + PyErr_SetString(PyExc_ValueError, "i2d() failed"); goto error; } const_p = senc; newsession = d2i_SSL_SESSION(NULL, &const_p, slen); - if (session == NULL) { + if (newsession == NULL) { + PyErr_SetString(PyExc_ValueError, "d2i() failed"); goto error; } PyMem_Free(senc); From a7acc5cb5c5327abeffe107d20b5d609fe0bdfad Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 17 Jul 2023 14:22:43 -0700 Subject: [PATCH 015/632] [3.11] gh-106368: Increase Argument Clinic test coverage for cpp.Monitor (GH-106833) (#106839) (cherry picked from commit 22379c60ab8f8b49e75da9bd032a8722af50b409) Co-authored-by: Erlend E. Aasland --- Lib/test/clinic.test.c | 132 +++++++++++++++++++++++++++++++++++++++- Lib/test/test_clinic.py | 49 +++++++++++++++ 2 files changed, 180 insertions(+), 1 deletion(-) diff --git a/Lib/test/clinic.test.c b/Lib/test/clinic.test.c index 1eb5a3e7cce0d8..e4d02f83bc636c 100644 --- a/Lib/test/clinic.test.c +++ b/Lib/test/clinic.test.c @@ -3278,6 +3278,47 @@ test_preprocessor_guarded_else_impl(PyObject *module) /*[clinic end generated code: output=13af7670aac51b12 input=6657ab31d74c29fc]*/ #endif +#ifndef CONDITION_C +/*[clinic input] +test_preprocessor_guarded_ifndef_condition_c +[clinic start generated code]*/ + +static PyObject * +test_preprocessor_guarded_ifndef_condition_c_impl(PyObject *module) +/*[clinic end generated code: output=ed422e8c895bb0a5 input=e9b50491cea2b668]*/ +#else +/*[clinic input] +test_preprocessor_guarded_ifndef_not_condition_c +[clinic start generated code]*/ + +static PyObject * +test_preprocessor_guarded_ifndef_not_condition_c_impl(PyObject *module) +/*[clinic end generated code: output=de6f4c6a67f8c536 input=da74e30e01c6f2c5]*/ +#endif + +#if \ +CONDITION_D +/*[clinic input] +test_preprocessor_guarded_if_with_continuation +[clinic start generated code]*/ + +static PyObject * +test_preprocessor_guarded_if_with_continuation_impl(PyObject *module) +/*[clinic end generated code: output=3d0712ca9e2d15b9 input=4a956fd91be30284]*/ +#endif + +#if CONDITION_E || CONDITION_F +#warning "different type of CPP directive" +/*[clinic input] +test_preprocessor_guarded_if_e_or_f +Makes sure cpp.Monitor handles other directives than preprocessor conditionals. +[clinic start generated code]*/ + +static PyObject * +test_preprocessor_guarded_if_e_or_f_impl(PyObject *module) +/*[clinic end generated code: output=e49d24ff64ad88bc input=57b9c37f938bc4f1]*/ +#endif + /*[clinic input] dump buffer output pop @@ -3337,6 +3378,79 @@ test_preprocessor_guarded_else(PyObject *module, PyObject *Py_UNUSED(ignored)) #endif /* !defined(CONDITION_A) && !(CONDITION_B) */ +#if !defined(CONDITION_C) + +PyDoc_STRVAR(test_preprocessor_guarded_ifndef_condition_c__doc__, +"test_preprocessor_guarded_ifndef_condition_c($module, /)\n" +"--\n" +"\n"); + +#define TEST_PREPROCESSOR_GUARDED_IFNDEF_CONDITION_C_METHODDEF \ + {"test_preprocessor_guarded_ifndef_condition_c", (PyCFunction)test_preprocessor_guarded_ifndef_condition_c, METH_NOARGS, test_preprocessor_guarded_ifndef_condition_c__doc__}, + +static PyObject * +test_preprocessor_guarded_ifndef_condition_c(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return test_preprocessor_guarded_ifndef_condition_c_impl(module); +} + +#endif /* !defined(CONDITION_C) */ + +#if defined(CONDITION_C) + +PyDoc_STRVAR(test_preprocessor_guarded_ifndef_not_condition_c__doc__, +"test_preprocessor_guarded_ifndef_not_condition_c($module, /)\n" +"--\n" +"\n"); + +#define TEST_PREPROCESSOR_GUARDED_IFNDEF_NOT_CONDITION_C_METHODDEF \ + {"test_preprocessor_guarded_ifndef_not_condition_c", (PyCFunction)test_preprocessor_guarded_ifndef_not_condition_c, METH_NOARGS, test_preprocessor_guarded_ifndef_not_condition_c__doc__}, + +static PyObject * +test_preprocessor_guarded_ifndef_not_condition_c(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return test_preprocessor_guarded_ifndef_not_condition_c_impl(module); +} + +#endif /* defined(CONDITION_C) */ + +#if (CONDITION_D) + +PyDoc_STRVAR(test_preprocessor_guarded_if_with_continuation__doc__, +"test_preprocessor_guarded_if_with_continuation($module, /)\n" +"--\n" +"\n"); + +#define TEST_PREPROCESSOR_GUARDED_IF_WITH_CONTINUATION_METHODDEF \ + {"test_preprocessor_guarded_if_with_continuation", (PyCFunction)test_preprocessor_guarded_if_with_continuation, METH_NOARGS, test_preprocessor_guarded_if_with_continuation__doc__}, + +static PyObject * +test_preprocessor_guarded_if_with_continuation(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return test_preprocessor_guarded_if_with_continuation_impl(module); +} + +#endif /* (CONDITION_D) */ + +#if (CONDITION_E || CONDITION_F) + +PyDoc_STRVAR(test_preprocessor_guarded_if_e_or_f__doc__, +"test_preprocessor_guarded_if_e_or_f($module, /)\n" +"--\n" +"\n" +"Makes sure cpp.Monitor handles other directives than preprocessor conditionals."); + +#define TEST_PREPROCESSOR_GUARDED_IF_E_OR_F_METHODDEF \ + {"test_preprocessor_guarded_if_e_or_f", (PyCFunction)test_preprocessor_guarded_if_e_or_f, METH_NOARGS, test_preprocessor_guarded_if_e_or_f__doc__}, + +static PyObject * +test_preprocessor_guarded_if_e_or_f(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return test_preprocessor_guarded_if_e_or_f_impl(module); +} + +#endif /* (CONDITION_E || CONDITION_F) */ + #ifndef TEST_PREPROCESSOR_GUARDED_CONDITION_A_METHODDEF #define TEST_PREPROCESSOR_GUARDED_CONDITION_A_METHODDEF #endif /* !defined(TEST_PREPROCESSOR_GUARDED_CONDITION_A_METHODDEF) */ @@ -3348,7 +3462,23 @@ test_preprocessor_guarded_else(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef TEST_PREPROCESSOR_GUARDED_ELSE_METHODDEF #define TEST_PREPROCESSOR_GUARDED_ELSE_METHODDEF #endif /* !defined(TEST_PREPROCESSOR_GUARDED_ELSE_METHODDEF) */ -/*[clinic end generated code: output=3804bb18d454038c input=3fc80c9989d2f2e1]*/ + +#ifndef TEST_PREPROCESSOR_GUARDED_IFNDEF_CONDITION_C_METHODDEF + #define TEST_PREPROCESSOR_GUARDED_IFNDEF_CONDITION_C_METHODDEF +#endif /* !defined(TEST_PREPROCESSOR_GUARDED_IFNDEF_CONDITION_C_METHODDEF) */ + +#ifndef TEST_PREPROCESSOR_GUARDED_IFNDEF_NOT_CONDITION_C_METHODDEF + #define TEST_PREPROCESSOR_GUARDED_IFNDEF_NOT_CONDITION_C_METHODDEF +#endif /* !defined(TEST_PREPROCESSOR_GUARDED_IFNDEF_NOT_CONDITION_C_METHODDEF) */ + +#ifndef TEST_PREPROCESSOR_GUARDED_IF_WITH_CONTINUATION_METHODDEF + #define TEST_PREPROCESSOR_GUARDED_IF_WITH_CONTINUATION_METHODDEF +#endif /* !defined(TEST_PREPROCESSOR_GUARDED_IF_WITH_CONTINUATION_METHODDEF) */ + +#ifndef TEST_PREPROCESSOR_GUARDED_IF_E_OR_F_METHODDEF + #define TEST_PREPROCESSOR_GUARDED_IF_E_OR_F_METHODDEF +#endif /* !defined(TEST_PREPROCESSOR_GUARDED_IF_E_OR_F_METHODDEF) */ +/*[clinic end generated code: output=fcfae7cac7a99e62 input=3fc80c9989d2f2e1]*/ /*[clinic input] test_vararg_and_posonly diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index 0e3fbb7627ab5a..cef121d9a1ead3 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -200,6 +200,55 @@ def test_parse_with_body_prefix(self): """).lstrip() # Note, lstrip() because of the newline self.assertEqual(out, expected) + def test_cpp_monitor_fail_nested_block_comment(self): + raw = """ + /* start + /* nested + */ + */ + """ + msg = ( + 'Error in file "test.c" on line 2:\n' + 'Nested block comment!\n' + ) + out = self.expect_failure(raw) + self.assertEqual(out, msg) + + def test_cpp_monitor_fail_invalid_format_noarg(self): + raw = """ + #if + a() + #endif + """ + msg = ( + 'Error in file "test.c" on line 1:\n' + 'Invalid format for #if line: no argument!\n' + ) + out = self.expect_failure(raw) + self.assertEqual(out, msg) + + def test_cpp_monitor_fail_invalid_format_toomanyargs(self): + raw = """ + #ifdef A B + a() + #endif + """ + msg = ( + 'Error in file "test.c" on line 1:\n' + 'Invalid format for #ifdef line: should be exactly one argument!\n' + ) + out = self.expect_failure(raw) + self.assertEqual(out, msg) + + def test_cpp_monitor_fail_no_matching_if(self): + raw = '#else' + msg = ( + 'Error in file "test.c" on line 1:\n' + '#else without matching #if / #ifdef / #ifndef!\n' + ) + out = self.expect_failure(raw) + self.assertEqual(out, msg) + class ClinicGroupPermuterTest(TestCase): def _test(self, l, m, r, output): From fb0487405314d3df4ce35f45850a6f7b7f041df1 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 18 Jul 2023 10:19:45 +0300 Subject: [PATCH 016/632] [3.11] gh-106719: Fix __annotations__ getter and setter in the type and module types (GH-106720) (GH-106850) No longer suppress arbitrary errors. Simplify the code. (cherry picked from commit e1c295e3da9ff5a3eb6b009a1f821d80e564ac87) --- ...-07-13-15-59-07.gh-issue-106719.jmVrsv.rst | 2 + Objects/moduleobject.c | 48 ++++++++----------- Objects/typeobject.c | 33 +++++-------- 3 files changed, 35 insertions(+), 48 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-07-13-15-59-07.gh-issue-106719.jmVrsv.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-07-13-15-59-07.gh-issue-106719.jmVrsv.rst b/Misc/NEWS.d/next/Core and Builtins/2023-07-13-15-59-07.gh-issue-106719.jmVrsv.rst new file mode 100644 index 00000000000000..dc4bef193a3220 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-07-13-15-59-07.gh-issue-106719.jmVrsv.rst @@ -0,0 +1,2 @@ +No longer suppress arbitrary errors in the ``__annotations__`` getter and +setter in the type and module types. diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index dba20a0b36463d..d18f2060f2f866 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -881,26 +881,20 @@ static PyObject * module_get_annotations(PyModuleObject *m, void *Py_UNUSED(ignored)) { PyObject *dict = PyObject_GetAttr((PyObject *)m, &_Py_ID(__dict__)); - - if ((dict == NULL) || !PyDict_Check(dict)) { + if (dict == NULL) { + return NULL; + } + if (!PyDict_Check(dict)) { PyErr_Format(PyExc_TypeError, ".__dict__ is not a dictionary"); - Py_XDECREF(dict); + Py_DECREF(dict); return NULL; } - PyObject *annotations; - /* there's no _PyDict_GetItemId without WithError, so let's LBYL. */ - if (PyDict_Contains(dict, &_Py_ID(__annotations__))) { - annotations = PyDict_GetItemWithError(dict, &_Py_ID(__annotations__)); - /* - ** _PyDict_GetItemIdWithError could still fail, - ** for instance with a well-timed Ctrl-C or a MemoryError. - ** so let's be totally safe. - */ - if (annotations) { - Py_INCREF(annotations); - } - } else { + PyObject *annotations = PyDict_GetItemWithError(dict, &_Py_ID(__annotations__)); + if (annotations) { + Py_INCREF(annotations); + } + else if (!PyErr_Occurred()) { annotations = PyDict_New(); if (annotations) { int result = PyDict_SetItem( @@ -919,8 +913,10 @@ module_set_annotations(PyModuleObject *m, PyObject *value, void *Py_UNUSED(ignor { int ret = -1; PyObject *dict = PyObject_GetAttr((PyObject *)m, &_Py_ID(__dict__)); - - if ((dict == NULL) || !PyDict_Check(dict)) { + if (dict == NULL) { + return -1; + } + if (!PyDict_Check(dict)) { PyErr_Format(PyExc_TypeError, ".__dict__ is not a dictionary"); goto exit; } @@ -928,19 +924,17 @@ module_set_annotations(PyModuleObject *m, PyObject *value, void *Py_UNUSED(ignor if (value != NULL) { /* set */ ret = PyDict_SetItem(dict, &_Py_ID(__annotations__), value); - goto exit; } - - /* delete */ - if (!PyDict_Contains(dict, &_Py_ID(__annotations__))) { - PyErr_Format(PyExc_AttributeError, "__annotations__"); - goto exit; + else { + /* delete */ + ret = PyDict_DelItem(dict, &_Py_ID(__annotations__)); + if (ret < 0 && PyErr_ExceptionMatches(PyExc_KeyError)) { + PyErr_SetString(PyExc_AttributeError, "__annotations__"); + } } - ret = PyDict_DelItem(dict, &_Py_ID(__annotations__)); - exit: - Py_XDECREF(dict); + Py_DECREF(dict); return ret; } diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 782a7e9722883e..4bdf1881581fc3 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -912,24 +912,16 @@ type_get_annotations(PyTypeObject *type, void *context) } PyObject *annotations; - /* there's no _PyDict_GetItemId without WithError, so let's LBYL. */ - if (PyDict_Contains(type->tp_dict, &_Py_ID(__annotations__))) { - annotations = PyDict_GetItemWithError( - type->tp_dict, &_Py_ID(__annotations__)); - /* - ** PyDict_GetItemWithError could still fail, - ** for instance with a well-timed Ctrl-C or a MemoryError. - ** so let's be totally safe. - */ - if (annotations) { - if (Py_TYPE(annotations)->tp_descr_get) { - annotations = Py_TYPE(annotations)->tp_descr_get( - annotations, NULL, (PyObject *)type); - } else { - Py_INCREF(annotations); - } + annotations = PyDict_GetItemWithError(type->tp_dict, &_Py_ID(__annotations__)); + if (annotations) { + if (Py_TYPE(annotations)->tp_descr_get) { + annotations = Py_TYPE(annotations)->tp_descr_get( + annotations, NULL, (PyObject *)type); + } else { + Py_INCREF(annotations); } - } else { + } + else if (!PyErr_Occurred()) { annotations = PyDict_New(); if (annotations) { int result = PyDict_SetItem( @@ -960,11 +952,10 @@ type_set_annotations(PyTypeObject *type, PyObject *value, void *context) result = PyDict_SetItem(type->tp_dict, &_Py_ID(__annotations__), value); } else { /* delete */ - if (!PyDict_Contains(type->tp_dict, &_Py_ID(__annotations__))) { - PyErr_Format(PyExc_AttributeError, "__annotations__"); - return -1; - } result = PyDict_DelItem(type->tp_dict, &_Py_ID(__annotations__)); + if (result < 0 && PyErr_ExceptionMatches(PyExc_KeyError)) { + PyErr_SetString(PyExc_AttributeError, "__annotations__"); + } } if (result == 0) { From 3841af49e5ba1bcbcccb4c5a4b386cbf13874c90 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 18 Jul 2023 12:53:17 +0300 Subject: [PATCH 017/632] [3.11] gh-86493: Fix possible leaks in modules initialization: _curses_panel, _decimal, posix, xxsubtype (GH-106767) (GH-106849) (GH-106851) (cherry picked from commit 745492355b94d109e47827e5865846f25ae42d26) (cherry picked from commit 970cb8eabaaf5a8311f1aba4ca4968ef7385fce8) --- Modules/_curses_panel.c | 4 +-- Modules/_decimal/_decimal.c | 46 +++++++++---------------- Modules/posixmodule.c | 67 ++++++++++++------------------------- Modules/xxsubtype.c | 10 +++--- 4 files changed, 43 insertions(+), 84 deletions(-) diff --git a/Modules/_curses_panel.c b/Modules/_curses_panel.c index 0b328f9665a0a2..c4be9aeaaee3bb 100644 --- a/Modules/_curses_panel.c +++ b/Modules/_curses_panel.c @@ -670,9 +670,7 @@ _curses_panel_exec(PyObject *mod) state->PyCursesError = PyErr_NewException( "_curses_panel.error", NULL, NULL); - Py_INCREF(state->PyCursesError); - if (PyModule_AddObject(mod, "error", state->PyCursesError) < 0) { - Py_DECREF(state->PyCursesError); + if (PyModule_AddObjectRef(mod, "error", state->PyCursesError) < 0) { return -1; } diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index 0e7d379e006cb1..5cacdca3f44baa 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -5922,21 +5922,15 @@ PyInit__decimal(void) /* Add types to the module */ - Py_INCREF(&PyDec_Type); - CHECK_INT(PyModule_AddObject(m, "Decimal", (PyObject *)&PyDec_Type)); - Py_INCREF(&PyDecContext_Type); - CHECK_INT(PyModule_AddObject(m, "Context", - (PyObject *)&PyDecContext_Type)); - Py_INCREF(DecimalTuple); - CHECK_INT(PyModule_AddObject(m, "DecimalTuple", (PyObject *)DecimalTuple)); - + CHECK_INT(PyModule_AddObjectRef(m, "Decimal", (PyObject *)&PyDec_Type)); + CHECK_INT(PyModule_AddObjectRef(m, "Context", (PyObject *)&PyDecContext_Type)); + CHECK_INT(PyModule_AddObjectRef(m, "DecimalTuple", (PyObject *)DecimalTuple)); /* Create top level exception */ ASSIGN_PTR(DecimalException, PyErr_NewException( "decimal.DecimalException", PyExc_ArithmeticError, NULL)); - Py_INCREF(DecimalException); - CHECK_INT(PyModule_AddObject(m, "DecimalException", DecimalException)); + CHECK_INT(PyModule_AddObjectRef(m, "DecimalException", DecimalException)); /* Create signal tuple */ ASSIGN_PTR(SignalTuple, PyTuple_New(SIGNAL_MAP_LEN)); @@ -5976,8 +5970,7 @@ PyInit__decimal(void) Py_DECREF(base); /* add to module */ - Py_INCREF(cm->ex); - CHECK_INT(PyModule_AddObject(m, cm->name, cm->ex)); + CHECK_INT(PyModule_AddObjectRef(m, cm->name, cm->ex)); /* add to signal tuple */ Py_INCREF(cm->ex); @@ -6007,45 +6000,39 @@ PyInit__decimal(void) ASSIGN_PTR(cm->ex, PyErr_NewException(cm->fqname, base, NULL)); Py_DECREF(base); - Py_INCREF(cm->ex); - CHECK_INT(PyModule_AddObject(m, cm->name, cm->ex)); + CHECK_INT(PyModule_AddObjectRef(m, cm->name, cm->ex)); } /* Init default context template first */ ASSIGN_PTR(default_context_template, PyObject_CallObject((PyObject *)&PyDecContext_Type, NULL)); - Py_INCREF(default_context_template); - CHECK_INT(PyModule_AddObject(m, "DefaultContext", - default_context_template)); + CHECK_INT(PyModule_AddObjectRef(m, "DefaultContext", + default_context_template)); #ifndef WITH_DECIMAL_CONTEXTVAR ASSIGN_PTR(tls_context_key, PyUnicode_FromString("___DECIMAL_CTX__")); - Py_INCREF(Py_False); - CHECK_INT(PyModule_AddObject(m, "HAVE_CONTEXTVAR", Py_False)); + CHECK_INT(PyModule_AddObjectRef(m, "HAVE_CONTEXTVAR", Py_False)); #else ASSIGN_PTR(current_context_var, PyContextVar_New("decimal_context", NULL)); - Py_INCREF(Py_True); - CHECK_INT(PyModule_AddObject(m, "HAVE_CONTEXTVAR", Py_True)); + CHECK_INT(PyModule_AddObjectRef(m, "HAVE_CONTEXTVAR", Py_True)); #endif - Py_INCREF(Py_True); - CHECK_INT(PyModule_AddObject(m, "HAVE_THREADS", Py_True)); + CHECK_INT(PyModule_AddObjectRef(m, "HAVE_THREADS", Py_True)); /* Init basic context template */ ASSIGN_PTR(basic_context_template, PyObject_CallObject((PyObject *)&PyDecContext_Type, NULL)); init_basic_context(basic_context_template); - Py_INCREF(basic_context_template); - CHECK_INT(PyModule_AddObject(m, "BasicContext", - basic_context_template)); + CHECK_INT(PyModule_AddObjectRef(m, "BasicContext", + basic_context_template)); /* Init extended context template */ ASSIGN_PTR(extended_context_template, PyObject_CallObject((PyObject *)&PyDecContext_Type, NULL)); init_extended_context(extended_context_template); Py_INCREF(extended_context_template); - CHECK_INT(PyModule_AddObject(m, "ExtendedContext", - extended_context_template)); + CHECK_INT(PyModule_AddObjectRef(m, "ExtendedContext", + extended_context_template)); /* Init mpd_ssize_t constants */ @@ -6064,8 +6051,7 @@ PyInit__decimal(void) /* Init string constants */ for (i = 0; i < _PY_DEC_ROUND_GUARD; i++) { ASSIGN_PTR(round_map[i], PyUnicode_InternFromString(mpd_round_string[i])); - Py_INCREF(round_map[i]); - CHECK_INT(PyModule_AddObject(m, mpd_round_string[i], round_map[i])); + CHECK_INT(PyModule_AddObjectRef(m, mpd_round_string[i], round_map[i])); } /* Add specification version number */ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 7c367b9d2e9818..91da07908f1150 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -15849,42 +15849,34 @@ posixmodule_exec(PyObject *m) if (setup_confname_tables(m)) return -1; - Py_INCREF(PyExc_OSError); - PyModule_AddObject(m, "error", PyExc_OSError); + if (PyModule_AddObjectRef(m, "error", PyExc_OSError) < 0) { + return -1; + } #if defined(HAVE_WAITID) && !defined(__APPLE__) waitid_result_desc.name = MODNAME ".waitid_result"; - PyObject *WaitidResultType = (PyObject *)PyStructSequence_NewType(&waitid_result_desc); - if (WaitidResultType == NULL) { + state->WaitidResultType = (PyObject *)PyStructSequence_NewType(&waitid_result_desc); + if (PyModule_AddObjectRef(m, "waitid_result", state->WaitidResultType) < 0) { return -1; } - Py_INCREF(WaitidResultType); - PyModule_AddObject(m, "waitid_result", WaitidResultType); - state->WaitidResultType = WaitidResultType; #endif stat_result_desc.name = "os.stat_result"; /* see issue #19209 */ stat_result_desc.fields[7].name = PyStructSequence_UnnamedField; stat_result_desc.fields[8].name = PyStructSequence_UnnamedField; stat_result_desc.fields[9].name = PyStructSequence_UnnamedField; - PyObject *StatResultType = (PyObject *)PyStructSequence_NewType(&stat_result_desc); - if (StatResultType == NULL) { + state->StatResultType = (PyObject *)PyStructSequence_NewType(&stat_result_desc); + if (PyModule_AddObjectRef(m, "stat_result", state->StatResultType) < 0) { return -1; } - Py_INCREF(StatResultType); - PyModule_AddObject(m, "stat_result", StatResultType); - state->StatResultType = StatResultType; - structseq_new = ((PyTypeObject *)StatResultType)->tp_new; - ((PyTypeObject *)StatResultType)->tp_new = statresult_new; + structseq_new = ((PyTypeObject *)state->StatResultType)->tp_new; + ((PyTypeObject *)state->StatResultType)->tp_new = statresult_new; statvfs_result_desc.name = "os.statvfs_result"; /* see issue #19209 */ - PyObject *StatVFSResultType = (PyObject *)PyStructSequence_NewType(&statvfs_result_desc); - if (StatVFSResultType == NULL) { + state->StatVFSResultType = (PyObject *)PyStructSequence_NewType(&statvfs_result_desc); + if (PyModule_AddObjectRef(m, "statvfs_result", state->StatVFSResultType) < 0) { return -1; } - Py_INCREF(StatVFSResultType); - PyModule_AddObject(m, "statvfs_result", StatVFSResultType); - state->StatVFSResultType = StatVFSResultType; #ifdef NEED_TICKS_PER_SECOND # if defined(HAVE_SYSCONF) && defined(_SC_CLK_TCK) ticks_per_second = sysconf(_SC_CLK_TCK); @@ -15897,24 +15889,18 @@ posixmodule_exec(PyObject *m) #if defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDPARAM) sched_param_desc.name = MODNAME ".sched_param"; - PyObject *SchedParamType = (PyObject *)PyStructSequence_NewType(&sched_param_desc); - if (SchedParamType == NULL) { + state->SchedParamType = (PyObject *)PyStructSequence_NewType(&sched_param_desc); + if (PyModule_AddObjectRef(m, "sched_param", state->SchedParamType) < 0) { return -1; } - Py_INCREF(SchedParamType); - PyModule_AddObject(m, "sched_param", SchedParamType); - state->SchedParamType = SchedParamType; - ((PyTypeObject *)SchedParamType)->tp_new = os_sched_param; + ((PyTypeObject *)state->SchedParamType)->tp_new = os_sched_param; #endif /* initialize TerminalSize_info */ - PyObject *TerminalSizeType = (PyObject *)PyStructSequence_NewType(&TerminalSize_desc); - if (TerminalSizeType == NULL) { + state->TerminalSizeType = (PyObject *)PyStructSequence_NewType(&TerminalSize_desc); + if (PyModule_AddObjectRef(m, "terminal_size", state->TerminalSizeType) < 0) { return -1; } - Py_INCREF(TerminalSizeType); - PyModule_AddObject(m, "terminal_size", TerminalSizeType); - state->TerminalSizeType = TerminalSizeType; /* initialize scandir types */ PyObject *ScandirIteratorType = PyType_FromModuleAndSpec(m, &ScandirIteratorType_spec, NULL); @@ -15923,30 +15909,21 @@ posixmodule_exec(PyObject *m) } state->ScandirIteratorType = ScandirIteratorType; - PyObject *DirEntryType = PyType_FromModuleAndSpec(m, &DirEntryType_spec, NULL); - if (DirEntryType == NULL) { + state->DirEntryType = PyType_FromModuleAndSpec(m, &DirEntryType_spec, NULL); + if (PyModule_AddObjectRef(m, "DirEntry", state->DirEntryType) < 0) { return -1; } - Py_INCREF(DirEntryType); - PyModule_AddObject(m, "DirEntry", DirEntryType); - state->DirEntryType = DirEntryType; times_result_desc.name = MODNAME ".times_result"; - PyObject *TimesResultType = (PyObject *)PyStructSequence_NewType(×_result_desc); - if (TimesResultType == NULL) { + state->TimesResultType = (PyObject *)PyStructSequence_NewType(×_result_desc); + if (PyModule_AddObjectRef(m, "times_result", state->TimesResultType) < 0) { return -1; } - Py_INCREF(TimesResultType); - PyModule_AddObject(m, "times_result", TimesResultType); - state->TimesResultType = TimesResultType; - PyTypeObject *UnameResultType = PyStructSequence_NewType(&uname_result_desc); - if (UnameResultType == NULL) { + state->UnameResultType = (PyObject *)PyStructSequence_NewType(&uname_result_desc); + if (PyModule_AddObjectRef(m, "uname_result", state->UnameResultType) < 0) { return -1; } - Py_INCREF(UnameResultType); - PyModule_AddObject(m, "uname_result", (PyObject *)UnameResultType); - state->UnameResultType = (PyObject *)UnameResultType; if ((state->billion = PyLong_FromLong(1000000000)) == NULL) return -1; diff --git a/Modules/xxsubtype.c b/Modules/xxsubtype.c index 12306f2fc5247c..7f3a13f32033ad 100644 --- a/Modules/xxsubtype.c +++ b/Modules/xxsubtype.c @@ -279,14 +279,12 @@ xxsubtype_exec(PyObject* m) if (PyType_Ready(&spamdict_type) < 0) return -1; - Py_INCREF(&spamlist_type); - if (PyModule_AddObject(m, "spamlist", - (PyObject *) &spamlist_type) < 0) + if (PyModule_AddObjectRef(m, "spamlist", + (PyObject *) &spamlist_type) < 0) return -1; - Py_INCREF(&spamdict_type); - if (PyModule_AddObject(m, "spamdict", - (PyObject *) &spamdict_type) < 0) + if (PyModule_AddObjectRef(m, "spamdict", + (PyObject *) &spamdict_type) < 0) return -1; return 0; } From 38553442c88c331d91562b77f023345e0b33b5f9 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 18 Jul 2023 03:25:25 -0700 Subject: [PATCH 018/632] [3.11] Docs: Normalise Argument Clinic advanced topics headings (GH-106842) (#106852) (cherry picked from commit 4cb0b9c0a9f6a4154238c98013d2679229b1f794) Co-authored-by: Erlend E. Aasland Co-authored-by: Ezio Melotti --- Doc/howto/clinic.rst | 95 +++++++++++++++++++++----------------------- 1 file changed, 46 insertions(+), 49 deletions(-) diff --git a/Doc/howto/clinic.rst b/Doc/howto/clinic.rst index 70b3e21d81a61c..287a4acebb1330 100644 --- a/Doc/howto/clinic.rst +++ b/Doc/howto/clinic.rst @@ -552,15 +552,12 @@ Let's dive in! Congratulations, you've ported your first function to work with Argument Clinic! -Advanced topics -=============== +How-to guides +============= -Now that you've had some experience working with Argument Clinic, it's time -for some advanced topics. - -Symbolic default values ------------------------ +How to use symbolic default values +---------------------------------- The default value you provide for a parameter can't be any arbitrary expression. Currently the following are explicitly supported: @@ -575,8 +572,8 @@ expression. Currently the following are explicitly supported: to allow full expressions like ``CONSTANT - 1``.) -Renaming the C functions and variables generated by Argument Clinic -------------------------------------------------------------------- +How to to rename C functions and variables generated by Argument Clinic +----------------------------------------------------------------------- Argument Clinic automatically names the functions it generates for you. Occasionally this may cause a problem, if the generated name collides with @@ -618,8 +615,8 @@ array) would be ``file``, but the C variable would be named ``file_obj``. You can use this to rename the ``self`` parameter too! -Converting functions using PyArg_UnpackTuple --------------------------------------------- +How to convert functions using ``PyArg_UnpackTuple`` +---------------------------------------------------- To convert a function parsing its arguments with :c:func:`PyArg_UnpackTuple`, simply write out all the arguments, specifying each as an ``object``. You @@ -631,8 +628,8 @@ Currently the generated code will use :c:func:`PyArg_ParseTuple`, but this will change soon. -Optional groups ---------------- +How to use optional groups +-------------------------- Some legacy functions have a tricky approach to parsing their arguments: they count the number of positional arguments, then use a ``switch`` statement @@ -724,8 +721,8 @@ Notes: use optional groups for new code. -Using real Argument Clinic converters, instead of "legacy converters" ---------------------------------------------------------------------- +How to use real Argument Clinic converters, instead of "legacy converters" +-------------------------------------------------------------------------- To save time, and to minimize how much you need to learn to achieve your first port to Argument Clinic, the walkthrough above tells @@ -892,8 +889,8 @@ it accepts, along with the default value for each parameter. Just run ``Tools/clinic/clinic.py --converters`` to see the full list. -Py_buffer ---------- +How to use the ``Py_buffer`` converter +-------------------------------------- When using the ``Py_buffer`` converter (or the ``'s*'``, ``'w*'``, ``'*y'``, or ``'z*'`` legacy converters), @@ -901,8 +898,8 @@ you *must* not call :c:func:`PyBuffer_Release` on the provided buffer. Argument Clinic generates code that does it for you (in the parsing function). -Advanced converters -------------------- +How to use advanced converters +------------------------------ Remember those format units you skipped for your first time because they were advanced? Here's how to handle those too. @@ -933,8 +930,8 @@ hard-coded encoding strings for parameters whose format units start with ``e``. .. _default_values: -Parameter default values ------------------------- +How to assign default values to parameter +----------------------------------------- Default values for parameters can be any of a number of values. At their simplest, they can be string, int, or float literals: @@ -957,8 +954,8 @@ There's also special support for a default value of ``NULL``, and for simple expressions, documented in the following sections. -The ``NULL`` default value --------------------------- +How to use the ``NULL`` default value +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ For string and object parameters, you can set them to ``None`` to indicate that there's no default. However, that means the C variable will be @@ -968,8 +965,8 @@ behaves like a default value of ``None``, but the C variable is initialized with ``NULL``. -Expressions specified as default values ---------------------------------------- +How to use expressions as default values +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The default value for a parameter can be more than just a literal value. It can be an entire expression, using math operators and looking up attributes @@ -1025,8 +1022,8 @@ you're not permitted to use: * Tuple/list/set/dict literals. -Using a return converter ------------------------- +How to use return converters +---------------------------- By default, the impl function Argument Clinic generates for you returns :c:type:`PyObject * `. @@ -1100,8 +1097,8 @@ their parameters (if any), just run ``Tools/clinic/clinic.py --converters`` for the full list. -Cloning existing functions --------------------------- +How to clone existing functions +------------------------------- If you have a number of functions that look similar, you may be able to use Clinic's "clone" feature. When you clone an existing function, @@ -1144,8 +1141,8 @@ Also, the function you are cloning from must have been previously defined in the current file. -Calling Python code -------------------- +How to call Python code +----------------------- The rest of the advanced topics require you to write Python code which lives inside your C file and modifies Argument Clinic's @@ -1172,8 +1169,8 @@ variable to the C code:: /*[python checksum:...]*/ -Using a "self converter" ------------------------- +How to use the "self converter" +------------------------------- Argument Clinic automatically adds a "self" parameter for you using a default converter. It automatically sets the ``type`` @@ -1224,8 +1221,8 @@ type for ``self``, it's best to create your own converter, subclassing [clinic start generated code]*/ -Using a "defining class" converter ----------------------------------- +How to use the "defining class" converter +----------------------------------------- Argument Clinic facilitates gaining access to the defining class of a method. This is useful for :ref:`heap type ` methods that need to fetch @@ -1287,8 +1284,8 @@ state. Example from the ``setattro`` slot method in See also :pep:`573`. -Writing a custom converter --------------------------- +How to write a custom converter +------------------------------- As we hinted at in the previous section... you can write your own converters! A converter is simply a Python class that inherits from ``CConverter``. @@ -1379,8 +1376,8 @@ You can see more examples of custom converters in the CPython source tree; grep the C files for the string ``CConverter``. -Writing a custom return converter ---------------------------------- +How to write a custom return converter +-------------------------------------- Writing a custom return converter is much like writing a custom converter. Except it's somewhat simpler, because return @@ -1394,8 +1391,8 @@ specifically the implementation of ``CReturnConverter`` and all its subclasses. -METH_O and METH_NOARGS ----------------------- +How to convert ``METH_O`` and ``METH_NOARGS`` functions +------------------------------------------------------- To convert a function using ``METH_O``, make sure the function's single argument is using the ``object`` converter, and mark the @@ -1416,8 +1413,8 @@ You can still use a self converter, a return converter, and specify a ``type`` argument to the object converter for ``METH_O``. -tp_new and tp_init functions ----------------------------- +How to convert ``tp_new`` and ``tp_init`` functions +--------------------------------------------------- You can convert ``tp_new`` and ``tp_init`` functions. Just name them ``__new__`` or ``__init__`` as appropriate. Notes: @@ -1439,8 +1436,8 @@ them ``__new__`` or ``__init__`` as appropriate. Notes: generated will throw an exception if it receives any.) -Changing and redirecting Clinic's output ----------------------------------------- +How to change and redirect Clinic's output +------------------------------------------ It can be inconvenient to have Clinic's output interspersed with your conventional hand-edited C code. Luckily, Clinic is configurable: @@ -1722,8 +1719,8 @@ it in a Clinic block lets Clinic use its existing checksum functionality to ensu the file was not modified by hand before it gets overwritten. -The #ifdef trick ----------------- +How to use the ``#ifdef`` trick +------------------------------- If you're converting a function that isn't available on all platforms, there's a trick you can use to make life a little easier. The existing @@ -1803,8 +1800,8 @@ Argument Clinic added to your file (it'll be at the very bottom), then move it above the ``PyMethodDef`` structure where that macro is used. -Using Argument Clinic in Python files -------------------------------------- +How to use Argument Clinic in Python files +------------------------------------------ It's actually possible to use Argument Clinic to preprocess Python files. There's no point to using Argument Clinic blocks, of course, as the output From 0c47ed7bbf156e9fc01e276ef053206f3e6a3e62 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 18 Jul 2023 16:16:04 -0700 Subject: [PATCH 019/632] [3.11] Docs: Argument Clinic: Group guides about default values (GH-106872) (#106873) Docs: Argument Clinic: Group guides about default values (GH-106872) Previous ToC layout (excerpt): - How to use symbolic default values ... - How to assign default values to parameter - How to use the ``NULL`` default value - How to use expressions as default values New layout: - How to assign default values to parameter - The ``NULL`` default value - Symbolic default values - Expressions as default values (cherry picked from commit 505eede38d141d43e40e246319b157e3c77211d3) Co-authored-by: Erlend E. Aasland --- Doc/howto/clinic.rst | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/Doc/howto/clinic.rst b/Doc/howto/clinic.rst index 287a4acebb1330..c2e5a68fb9eb21 100644 --- a/Doc/howto/clinic.rst +++ b/Doc/howto/clinic.rst @@ -556,22 +556,6 @@ How-to guides ============= -How to use symbolic default values ----------------------------------- - -The default value you provide for a parameter can't be any arbitrary -expression. Currently the following are explicitly supported: - -* Numeric constants (integer and float) -* String constants -* ``True``, ``False``, and ``None`` -* Simple symbolic constants like ``sys.maxsize``, which must - start with the name of the module - -(In the future, this may need to get even more elaborate, -to allow full expressions like ``CONSTANT - 1``.) - - How to to rename C functions and variables generated by Argument Clinic ----------------------------------------------------------------------- @@ -954,8 +938,8 @@ There's also special support for a default value of ``NULL``, and for simple expressions, documented in the following sections. -How to use the ``NULL`` default value -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The ``NULL`` default value +^^^^^^^^^^^^^^^^^^^^^^^^^^ For string and object parameters, you can set them to ``None`` to indicate that there's no default. However, that means the C variable will be @@ -965,8 +949,24 @@ behaves like a default value of ``None``, but the C variable is initialized with ``NULL``. -How to use expressions as default values -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Symbolic default values +^^^^^^^^^^^^^^^^^^^^^^^ + +The default value you provide for a parameter can't be any arbitrary +expression. Currently the following are explicitly supported: + +* Numeric constants (integer and float) +* String constants +* ``True``, ``False``, and ``None`` +* Simple symbolic constants like ``sys.maxsize``, which must + start with the name of the module + +(In the future, this may need to get even more elaborate, +to allow full expressions like ``CONSTANT - 1``.) + + +Expressions as default values +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The default value for a parameter can be more than just a literal value. It can be an entire expression, using math operators and looking up attributes From fced79f91e186dd6a608221ad7cee2eeb443c40d Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 19 Jul 2023 09:40:38 +0300 Subject: [PATCH 020/632] [3.11] gh-86493: Fix possible leaks in some modules initialization (GH-106768) (GH-106855) (GH-106863) [3.11] [3.12] gh-86493: Fix possible leaks in some modules initialization (GH-106768) (GH-106855) Fix _ssl, _stat, _testinternalcapi, _threadmodule, cmath, math, posix, time. (cherry picked from commit 3e65baee72131b49f4ce8ca2da568a6f2001ce93). (cherry picked from commit a423ddbdeada8a2fd8657453b9e9f58ba0dd921d) --- Include/cpython/modsupport.h | 1 + Modules/_ssl.c | 8 ++++---- Modules/_stat.c | 18 +++++++++--------- Modules/_testinternalcapi.c | 2 +- Modules/_threadmodule.c | 4 ++-- Modules/cmathmodule.c | 16 +++++++--------- Modules/mathmodule.c | 10 +++++----- Modules/posixmodule.c | 12 ++++-------- Modules/timemodule.c | 7 ++----- Python/modsupport.c | 29 ++++++++++------------------- 10 files changed, 45 insertions(+), 62 deletions(-) diff --git a/Include/cpython/modsupport.h b/Include/cpython/modsupport.h index 769eb52bf6e3ae..205e174243987e 100644 --- a/Include/cpython/modsupport.h +++ b/Include/cpython/modsupport.h @@ -103,5 +103,6 @@ PyAPI_FUNC(PyObject * const *) _PyArg_UnpackKeywordsWithVararg( (minpos), (maxpos), (minkw), (buf))) PyAPI_FUNC(PyObject *) _PyModule_CreateInitialized(PyModuleDef*, int apiver); +PyAPI_FUNC(int) _PyModule_Add(PyObject *, const char *, PyObject *); PyAPI_DATA(const char *) _Py_PackageContext; diff --git a/Modules/_ssl.c b/Modules/_ssl.c index e3bb38e769c9b3..09257223924463 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -6130,22 +6130,22 @@ sslmodule_init_versioninfo(PyObject *m) */ libver = OpenSSL_version_num(); r = PyLong_FromUnsignedLong(libver); - if (r == NULL || PyModule_AddObject(m, "OPENSSL_VERSION_NUMBER", r)) + if (_PyModule_Add(m, "OPENSSL_VERSION_NUMBER", r) < 0) return -1; parse_openssl_version(libver, &major, &minor, &fix, &patch, &status); r = Py_BuildValue("IIIII", major, minor, fix, patch, status); - if (r == NULL || PyModule_AddObject(m, "OPENSSL_VERSION_INFO", r)) + if (_PyModule_Add(m, "OPENSSL_VERSION_INFO", r) < 0) return -1; r = PyUnicode_FromString(OpenSSL_version(OPENSSL_VERSION)); - if (r == NULL || PyModule_AddObject(m, "OPENSSL_VERSION", r)) + if (_PyModule_Add(m, "OPENSSL_VERSION", r) < 0) return -1; libver = OPENSSL_VERSION_NUMBER; parse_openssl_version(libver, &major, &minor, &fix, &patch, &status); r = Py_BuildValue("IIIII", major, minor, fix, patch, status); - if (r == NULL || PyModule_AddObject(m, "_OPENSSL_API_VERSION", r)) + if (_PyModule_Add(m, "_OPENSSL_API_VERSION", r) < 0) return -1; return 0; diff --git a/Modules/_stat.c b/Modules/_stat.c index 546e6a5f94ca15..6e0d6902fe75fc 100644 --- a/Modules/_stat.c +++ b/Modules/_stat.c @@ -592,17 +592,17 @@ stat_exec(PyObject *module) ADD_INT_MACRO(module, FILE_ATTRIBUTE_TEMPORARY); ADD_INT_MACRO(module, FILE_ATTRIBUTE_VIRTUAL); - if (PyModule_AddObject(module, "IO_REPARSE_TAG_SYMLINK", - PyLong_FromUnsignedLong(IO_REPARSE_TAG_SYMLINK)) < 0) { - return -1; + if (_PyModule_Add(module, "IO_REPARSE_TAG_SYMLINK", + PyLong_FromUnsignedLong(IO_REPARSE_TAG_SYMLINK)) < 0) { + return -1; } - if (PyModule_AddObject(module, "IO_REPARSE_TAG_MOUNT_POINT", - PyLong_FromUnsignedLong(IO_REPARSE_TAG_MOUNT_POINT)) < 0) { - return -1; + if (_PyModule_Add(module, "IO_REPARSE_TAG_MOUNT_POINT", + PyLong_FromUnsignedLong(IO_REPARSE_TAG_MOUNT_POINT)) < 0) { + return -1; } - if (PyModule_AddObject(module, "IO_REPARSE_TAG_APPEXECLINK", - PyLong_FromUnsignedLong(IO_REPARSE_TAG_APPEXECLINK)) < 0) { - return -1; + if (_PyModule_Add(module, "IO_REPARSE_TAG_APPEXECLINK", + PyLong_FromUnsignedLong(IO_REPARSE_TAG_APPEXECLINK)) < 0) { + return -1; } #endif diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index 4a8c43658998fa..7e60da5906801b 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -682,7 +682,7 @@ PyInit__testinternalcapi(void) return NULL; } - if (PyModule_AddObject(module, "SIZEOF_PYGC_HEAD", + if (_PyModule_Add(module, "SIZEOF_PYGC_HEAD", PyLong_FromSsize_t(sizeof(PyGC_Head))) < 0) { goto error; } diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index 879c94a109690d..199e3b89d99213 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -1648,8 +1648,8 @@ thread_module_exec(PyObject *module) // Round towards minus infinity timeout_max = floor(timeout_max); - if (PyModule_AddObject(module, "TIMEOUT_MAX", - PyFloat_FromDouble(timeout_max)) < 0) { + if (_PyModule_Add(module, "TIMEOUT_MAX", + PyFloat_FromDouble(timeout_max)) < 0) { return -1; } diff --git a/Modules/cmathmodule.c b/Modules/cmathmodule.c index 53e34061d53773..7fffb31cff0f22 100644 --- a/Modules/cmathmodule.c +++ b/Modules/cmathmodule.c @@ -1264,30 +1264,28 @@ static PyMethodDef cmath_methods[] = { static int cmath_exec(PyObject *mod) { - if (PyModule_AddObject(mod, "pi", PyFloat_FromDouble(Py_MATH_PI)) < 0) { + if (_PyModule_Add(mod, "pi", PyFloat_FromDouble(Py_MATH_PI)) < 0) { return -1; } - if (PyModule_AddObject(mod, "e", PyFloat_FromDouble(Py_MATH_E)) < 0) { + if (_PyModule_Add(mod, "e", PyFloat_FromDouble(Py_MATH_E)) < 0) { return -1; } // 2pi - if (PyModule_AddObject(mod, "tau", PyFloat_FromDouble(Py_MATH_TAU)) < 0) { + if (_PyModule_Add(mod, "tau", PyFloat_FromDouble(Py_MATH_TAU)) < 0) { return -1; } - if (PyModule_AddObject(mod, "inf", PyFloat_FromDouble(m_inf())) < 0) { + if (_PyModule_Add(mod, "inf", PyFloat_FromDouble(m_inf())) < 0) { return -1; } - if (PyModule_AddObject(mod, "infj", - PyComplex_FromCComplex(c_infj())) < 0) { + if (_PyModule_Add(mod, "infj", PyComplex_FromCComplex(c_infj())) < 0) { return -1; } #if _PY_SHORT_FLOAT_REPR == 1 - if (PyModule_AddObject(mod, "nan", PyFloat_FromDouble(m_nan())) < 0) { + if (_PyModule_Add(mod, "nan", PyFloat_FromDouble(m_nan())) < 0) { return -1; } - if (PyModule_AddObject(mod, "nanj", - PyComplex_FromCComplex(c_nanj())) < 0) { + if (_PyModule_Add(mod, "nanj", PyComplex_FromCComplex(c_nanj())) < 0) { return -1; } #endif diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 0a907a0c04ba1e..5f5b71c4c00102 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -3825,21 +3825,21 @@ math_ulp_impl(PyObject *module, double x) static int math_exec(PyObject *module) { - if (PyModule_AddObject(module, "pi", PyFloat_FromDouble(Py_MATH_PI)) < 0) { + if (_PyModule_Add(module, "pi", PyFloat_FromDouble(Py_MATH_PI)) < 0) { return -1; } - if (PyModule_AddObject(module, "e", PyFloat_FromDouble(Py_MATH_E)) < 0) { + if (_PyModule_Add(module, "e", PyFloat_FromDouble(Py_MATH_E)) < 0) { return -1; } // 2pi - if (PyModule_AddObject(module, "tau", PyFloat_FromDouble(Py_MATH_TAU)) < 0) { + if (_PyModule_Add(module, "tau", PyFloat_FromDouble(Py_MATH_TAU)) < 0) { return -1; } - if (PyModule_AddObject(module, "inf", PyFloat_FromDouble(m_inf())) < 0) { + if (_PyModule_Add(module, "inf", PyFloat_FromDouble(m_inf())) < 0) { return -1; } #if _PY_SHORT_FLOAT_REPR == 1 - if (PyModule_AddObject(module, "nan", PyFloat_FromDouble(m_nan())) < 0) { + if (_PyModule_Add(module, "nan", PyFloat_FromDouble(m_nan())) < 0) { return -1; } #endif diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 91da07908f1150..c000a320322875 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -12563,7 +12563,7 @@ setup_confname_table(struct constdef *table, size_t tablesize, } Py_DECREF(o); } - return PyModule_AddObject(module, tablename, d); + return _PyModule_Add(module, tablename, d); } /* Return -1 on failure, 0 on success. */ @@ -15837,11 +15837,9 @@ posixmodule_exec(PyObject *m) #endif /* Initialize environ dictionary */ - PyObject *v = convertenviron(); - Py_XINCREF(v); - if (v == NULL || PyModule_AddObject(m, "environ", v) != 0) + if (_PyModule_Add(m, "environ", convertenviron()) != 0) { return -1; - Py_DECREF(v); + } if (all_ins(m)) return -1; @@ -15965,9 +15963,7 @@ posixmodule_exec(PyObject *m) Py_DECREF(unicode); } - PyModule_AddObject(m, "_have_functions", list); - - return 0; + return _PyModule_Add(m, "_have_functions", list); } diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 18f9ddb909c028..b8e0e481cbb5e8 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -1775,11 +1775,9 @@ init_timezone(PyObject *m) return -1; } #endif // MS_WINDOWS - PyObject *tzname_obj = Py_BuildValue("(NN)", otz0, otz1); - if (tzname_obj == NULL) { + if (_PyModule_Add(m, "tzname", Py_BuildValue("(NN)", otz0, otz1)) < 0) { return -1; } - PyModule_AddObject(m, "tzname", tzname_obj); #else // !HAVE_DECL_TZNAME static const time_t YEAR = (365 * 24 + 6) * 3600; time_t t; @@ -1822,10 +1820,9 @@ init_timezone(PyObject *m) PyModule_AddIntConstant(m, "daylight", janzone != julyzone); tzname_obj = Py_BuildValue("(zz)", janname, julyname); } - if (tzname_obj == NULL) { + if (_PyModule_Add(m, "tzname", tzname_obj) < 0) { return -1; } - PyModule_AddObject(m, "tzname", tzname_obj); #endif // !HAVE_DECL_TZNAME if (PyErr_Occurred()) { diff --git a/Python/modsupport.c b/Python/modsupport.c index 8655daa1fc5e0e..89ffae8c0f16d0 100644 --- a/Python/modsupport.c +++ b/Python/modsupport.c @@ -658,13 +658,16 @@ PyModule_AddObjectRef(PyObject *mod, const char *name, PyObject *value) PyModule_GetName(mod)); return -1; } - - if (PyDict_SetItemString(dict, name, value)) { - return -1; - } - return 0; + return PyDict_SetItemString(dict, name, value); } +int +_PyModule_Add(PyObject *mod, const char *name, PyObject *value) +{ + int res = PyModule_AddObjectRef(mod, name, value); + Py_XDECREF(value); + return res; +} int PyModule_AddObject(PyObject *mod, const char *name, PyObject *value) @@ -679,25 +682,13 @@ PyModule_AddObject(PyObject *mod, const char *name, PyObject *value) int PyModule_AddIntConstant(PyObject *m, const char *name, long value) { - PyObject *obj = PyLong_FromLong(value); - if (!obj) { - return -1; - } - int res = PyModule_AddObjectRef(m, name, obj); - Py_DECREF(obj); - return res; + return _PyModule_Add(m, name, PyLong_FromLong(value)); } int PyModule_AddStringConstant(PyObject *m, const char *name, const char *value) { - PyObject *obj = PyUnicode_FromString(value); - if (!obj) { - return -1; - } - int res = PyModule_AddObjectRef(m, name, obj); - Py_DECREF(obj); - return res; + return _PyModule_Add(m, name, PyUnicode_FromString(value)); } int From a6313d78f21f79ca64dedd38e637509dc530a1b6 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 19 Jul 2023 13:03:44 -0700 Subject: [PATCH 021/632] [3.11] gh-106882: Note that `asyncio.Server` is only publicly exposed on 3.11+ (GH-106901) (#106903) gh-106882: Note that `asyncio.Server` is only publicly exposed on 3.11+ (GH-106901) And later versions of 3.10, 3.9 (cherry picked from commit 1e1f4e91a905bab3103250a3ceadac0693b926d9) Co-authored-by: Jack Nelson --- Doc/library/asyncio-eventloop.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index d6aab2a5fce1b0..83fb27060b214d 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -1561,6 +1561,9 @@ Do not instantiate the :class:`Server` class directly. .. versionchanged:: 3.7 Server object is an asynchronous context manager since Python 3.7. + .. versionchanged:: 3.11 + This class was exposed publicly as ``asyncio.Server`` in Python 3.9.11, 3.10.3 and 3.11. + .. method:: close() Stop serving: close listening sockets and set the :attr:`sockets` From a5c8d240c0648ac9f982bbea0fc1b2ab3a959543 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 20 Jul 2023 16:12:00 -0700 Subject: [PATCH 022/632] [3.11] Fix typo in tkinter docs (GH-106936) (#106938) Fix typo in tkinter docs (GH-106936) (cherry picked from commit 60e83968d555d53b97de04a0a00b2cdeb3187d39) Signed-off-by: Makonede <61922615+Makonede@users.noreply.github.com> Co-authored-by: Makonede <61922615+Makonede@users.noreply.github.com> --- Doc/library/tkinter.ttk.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/tkinter.ttk.rst b/Doc/library/tkinter.ttk.rst index 4ff2b2159c3622..9f2f9eb858afd4 100644 --- a/Doc/library/tkinter.ttk.rst +++ b/Doc/library/tkinter.ttk.rst @@ -102,7 +102,7 @@ themed widgets and is not supposed to be directly instantiated. Standard Options ^^^^^^^^^^^^^^^^ -All the :mod:`ttk` Widgets accepts the following options: +All the :mod:`ttk` Widgets accept the following options: .. tabularcolumns:: |l|L| From 951b08c5006fe6a7daff4a9769964b4017a9b754 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 20 Jul 2023 23:31:00 -0700 Subject: [PATCH 023/632] [3.11] gh-106368: Increase Argument Clinic test coverage for IndentStack (GH-106933) (#106944) (cherry picked from commit 8d228cf66f316803e95685d6553084f3d60cd9c5) Co-authored-by: Erlend E. Aasland --- Lib/test/test_clinic.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index cef121d9a1ead3..605d101b5bef90 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -1035,6 +1035,25 @@ def test_function_not_at_column_0(self): Nested docstring here, goeth. """) + def test_indent_stack_no_tabs(self): + out = self.parse_function_should_fail(""" + module foo + foo.bar + *vararg1: object + \t*vararg2: object + """) + msg = "Tab characters are illegal in the Clinic DSL." + self.assertIn(msg, out) + + def test_indent_stack_illegal_outdent(self): + out = self.parse_function_should_fail(""" + module foo + foo.bar + a: object + b: object + """) + self.assertIn("Illegal outdent", out) + def test_directive(self): c = FakeClinic() parser = DSLParser(c) From 0a57620887f9877398c44062539da3e58235f0e6 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 20 Jul 2023 23:32:56 -0700 Subject: [PATCH 024/632] [3.11] Docs: Argument Clinic: Add Background and Tutorial top-level sections (GH-106904) (#106946) Add Background as a toplevel section with the following subsections: - Background - The goals of Argument Clinic - Basic concepts and usage Rename "Converting your first function" to Tutorial. Add anchors for Background, Tutorial, and How-to Guides: - :ref:`clinic-background` - :ref:`clinic-tutorial` - :ref:`clinic-howtos` Link to these from within the Abstract. Break the compatibility paragraph out of Abstract and make it a note. (cherry picked from commit 81861fd90b4ae981e7881cd03a3c370713063525) Co-authored-by: Erlend E. Aasland Co-authored-by: Ezio Melotti --- Doc/howto/clinic.rst | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/Doc/howto/clinic.rst b/Doc/howto/clinic.rst index c2e5a68fb9eb21..50703d9b4723d9 100644 --- a/Doc/howto/clinic.rst +++ b/Doc/howto/clinic.rst @@ -13,12 +13,20 @@ Argument Clinic How-To Argument Clinic is a preprocessor for CPython C files. Its purpose is to automate all the boilerplate involved - with writing argument parsing code for "builtins". - This document shows you how to convert your first C - function to work with Argument Clinic, and then introduces - some advanced topics on Argument Clinic usage. + with writing argument parsing code for "builtins", + module level functions, and class methods. + This document is divided in three major sections: - Currently Argument Clinic is considered internal-only + * :ref:`clinic-background` talks about the basic concepts and goals of + Argument Clinic. + * :ref:`clinic-tutorial` guides you through all the steps required to + adapt an existing C function to Argument Clinic. + * :ref:`clinic-howtos` details how to handle specific tasks. + + +.. note:: + + Argument Clinic is considered internal-only for CPython. Its use is not supported for files outside CPython, and no guarantees are made regarding backwards compatibility for future versions. In other words: if you @@ -28,8 +36,14 @@ Argument Clinic How-To of CPython *could* be totally incompatible and break all your code. +.. _clinic-background: + +Background +========== + + The goals of Argument Clinic -============================ +---------------------------- Argument Clinic's primary goal is to take over responsibility for all argument parsing code @@ -80,7 +94,7 @@ things with all the information you give it. Basic concepts and usage -======================== +------------------------ Argument Clinic ships with CPython; you'll find it in ``Tools/clinic/clinic.py``. If you run that script, specifying a C file as an argument: @@ -142,8 +156,10 @@ For the sake of clarity, here's the terminology we'll use with Argument Clinic: a block.) -Converting your first function -============================== +.. _clinic-tutorial: + +Tutorial +======== The best way to get a sense of how Argument Clinic works is to convert a function to work with it. Here, then, are the bare @@ -552,6 +568,8 @@ Let's dive in! Congratulations, you've ported your first function to work with Argument Clinic! +.. _clinic-howtos: + How-to guides ============= From 6bde1b9f049e35e557068bd8c6034ee9c6fe61dc Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 21 Jul 2023 14:48:30 +0300 Subject: [PATCH 025/632] [3.11] gh-106919: Use role :c:macro: for referencing the C "constants" (GH-106920) (GH-106952) (cherry picked from commit fcc816dbff7ca66c26f57a506e4d2330fe41d0ff) --- Doc/c-api/arg.rst | 4 +- Doc/c-api/call.rst | 12 +-- Doc/c-api/complex.rst | 4 +- Doc/c-api/exceptions.rst | 12 +-- Doc/c-api/file.rst | 2 +- Doc/c-api/float.rst | 4 +- Doc/c-api/gcsupport.rst | 12 +-- Doc/c-api/init.rst | 62 ++++++------- Doc/c-api/long.rst | 8 +- Doc/c-api/memory.rst | 18 ++-- Doc/c-api/module.rst | 6 +- Doc/c-api/object.rst | 12 +-- Doc/c-api/slice.rst | 2 +- Doc/c-api/stable.rst | 2 +- Doc/c-api/structures.rst | 56 +++++++---- Doc/c-api/sys.rst | 4 +- Doc/c-api/type.rst | 8 +- Doc/c-api/typeobj.rst | 138 ++++++++++++++-------------- Doc/c-api/unicode.rst | 10 +- Doc/c-api/veryhigh.rst | 8 +- Doc/extending/extending.rst | 4 +- Doc/extending/newtypes_tutorial.rst | 8 +- Doc/howto/isolating-extensions.rst | 10 +- Doc/library/dis.rst | 4 +- Doc/library/os.rst | 14 +-- Doc/reference/compound_stmts.rst | 4 +- Doc/using/cmdline.rst | 8 +- Doc/whatsnew/2.2.rst | 6 +- Doc/whatsnew/2.3.rst | 8 +- Doc/whatsnew/2.4.rst | 2 +- Doc/whatsnew/2.6.rst | 6 +- Doc/whatsnew/2.7.rst | 2 +- Doc/whatsnew/3.10.rst | 10 +- Doc/whatsnew/3.11.rst | 4 +- Doc/whatsnew/3.6.rst | 10 +- Doc/whatsnew/3.8.rst | 2 +- Doc/whatsnew/3.9.rst | 2 +- Misc/NEWS.d/3.10.0a3.rst | 2 +- Misc/NEWS.d/3.10.0b1.rst | 8 +- Misc/NEWS.d/3.11.0a1.rst | 8 +- Misc/NEWS.d/3.11.0a7.rst | 2 +- Misc/NEWS.d/3.6.0a1.rst | 2 +- Misc/NEWS.d/3.9.0a1.rst | 2 +- 43 files changed, 265 insertions(+), 247 deletions(-) diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst index 6a53c79bd3becf..ee130ab28510b0 100644 --- a/Doc/c-api/arg.rst +++ b/Doc/c-api/arg.rst @@ -380,7 +380,7 @@ Other objects *items*. Format units for sequences may be nested. It is possible to pass "long" integers (integers whose value exceeds the -platform's :const:`LONG_MAX`) however no proper range checking is done --- the +platform's :c:macro:`LONG_MAX`) however no proper range checking is done --- the most significant bits are silently truncated when the receiving field is too small to receive the value (actually, the semantics are inherited from downcasts in C --- your mileage may vary). @@ -492,7 +492,7 @@ API Functions A simpler form of parameter retrieval which does not use a format string to specify the types of the arguments. Functions which use this method to retrieve - their parameters should be declared as :const:`METH_VARARGS` in function or + their parameters should be declared as :c:macro:`METH_VARARGS` in function or method tables. The tuple containing the actual parameters should be passed as *args*; it must actually be a tuple. The length of the tuple must be at least *min* and no more than *max*; *min* and *max* may be equal. Additional diff --git a/Doc/c-api/call.rst b/Doc/c-api/call.rst index 36149f156c6764..a574965bb12605 100644 --- a/Doc/c-api/call.rst +++ b/Doc/c-api/call.rst @@ -63,7 +63,7 @@ the arguments to an args tuple and kwargs dict anyway, then there is no point in implementing vectorcall. Classes can implement the vectorcall protocol by enabling the -:const:`Py_TPFLAGS_HAVE_VECTORCALL` flag and setting +:c:macro:`Py_TPFLAGS_HAVE_VECTORCALL` flag and setting :c:member:`~PyTypeObject.tp_vectorcall_offset` to the offset inside the object structure where a *vectorcallfunc* appears. This is a pointer to a function with the following signature: @@ -75,7 +75,7 @@ This is a pointer to a function with the following signature: values of the keyword arguments. This can be *NULL* if there are no arguments. - *nargsf* is the number of positional arguments plus possibly the - :const:`PY_VECTORCALL_ARGUMENTS_OFFSET` flag. + :c:macro:`PY_VECTORCALL_ARGUMENTS_OFFSET` flag. To get the actual number of positional arguments from *nargsf*, use :c:func:`PyVectorcall_NARGS`. - *kwnames* is a tuple containing the names of the keyword arguments; @@ -95,7 +95,7 @@ This is a pointer to a function with the following signature: ``args[0]`` may be changed. Whenever they can do so cheaply (without additional allocation), callers - are encouraged to use :const:`PY_VECTORCALL_ARGUMENTS_OFFSET`. + are encouraged to use :c:macro:`PY_VECTORCALL_ARGUMENTS_OFFSET`. Doing so will allow callables such as bound methods to make their onward calls (which include a prepended *self* argument) very efficiently. @@ -165,7 +165,7 @@ Vectorcall Support API This is a specialized function, intended to be put in the :c:member:`~PyTypeObject.tp_call` slot or be used in an implementation of ``tp_call``. - It does not check the :const:`Py_TPFLAGS_HAVE_VECTORCALL` flag + It does not check the :c:macro:`Py_TPFLAGS_HAVE_VECTORCALL` flag and it does not fall back to ``tp_call``. .. versionadded:: 3.8 @@ -383,11 +383,11 @@ please see individual documentation for details. *args[0]*, and the *args* array starting at *args[1]* represents the arguments of the call. There must be at least one positional argument. *nargsf* is the number of positional arguments including *args[0]*, - plus :const:`PY_VECTORCALL_ARGUMENTS_OFFSET` if the value of ``args[0]`` may + plus :c:macro:`PY_VECTORCALL_ARGUMENTS_OFFSET` if the value of ``args[0]`` may temporarily be changed. Keyword arguments can be passed just like in :c:func:`PyObject_Vectorcall`. - If the object has the :const:`Py_TPFLAGS_METHOD_DESCRIPTOR` feature, + If the object has the :c:macro:`Py_TPFLAGS_METHOD_DESCRIPTOR` feature, this will call the unbound method object with the full *args* vector as arguments. diff --git a/Doc/c-api/complex.rst b/Doc/c-api/complex.rst index cb8b270fcbab6e..6679ce76f1dc6f 100644 --- a/Doc/c-api/complex.rst +++ b/Doc/c-api/complex.rst @@ -64,7 +64,7 @@ pointers. This is consistent throughout the API. representation. If *divisor* is null, this method returns zero and sets - :c:data:`errno` to :c:data:`EDOM`. + :c:data:`errno` to :c:macro:`EDOM`. .. c:function:: Py_complex _Py_c_pow(Py_complex num, Py_complex exp) @@ -73,7 +73,7 @@ pointers. This is consistent throughout the API. representation. If *num* is null and *exp* is not a positive real number, - this method returns zero and sets :c:data:`errno` to :c:data:`EDOM`. + this method returns zero and sets :c:data:`errno` to :c:macro:`EDOM`. Complex Numbers as Python Objects diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst index 092e548fb8fd83..76d32fd1763f57 100644 --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -154,7 +154,7 @@ For convenience, some of these functions will always return a tuple object whose first item is the integer :c:data:`errno` value and whose second item is the corresponding error message (gotten from :c:func:`strerror`), and then calls ``PyErr_SetObject(type, object)``. On Unix, when the - :c:data:`errno` value is :const:`EINTR`, indicating an interrupted system call, + :c:data:`errno` value is :c:macro:`EINTR`, indicating an interrupted system call, this calls :c:func:`PyErr_CheckSignals`, and if that set the error indicator, leaves it set to that. The function always returns ``NULL``, so a wrapper function around a system call can write ``return PyErr_SetFromErrno(type);`` @@ -567,7 +567,7 @@ Signal Handling be interruptible by user requests (such as by pressing Ctrl-C). .. note:: - The default Python signal handler for :const:`SIGINT` raises the + The default Python signal handler for :c:macro:`SIGINT` raises the :exc:`KeyboardInterrupt` exception. @@ -578,7 +578,7 @@ Signal Handling single: SIGINT single: KeyboardInterrupt (built-in exception) - Simulate the effect of a :const:`SIGINT` signal arriving. + Simulate the effect of a :c:macro:`SIGINT` signal arriving. This is equivalent to ``PyErr_SetInterruptEx(SIGINT)``. .. note:: @@ -690,7 +690,7 @@ Exception Objects .. c:function:: PyObject* PyException_GetCause(PyObject *ex) - Return the cause (either an exception instance, or :const:`None`, + Return the cause (either an exception instance, or ``None``, set by ``raise ... from ...``) associated with the exception as a new reference, as accessible from Python through :attr:`__cause__`. @@ -699,7 +699,7 @@ Exception Objects Set the cause associated with the exception to *cause*. Use ``NULL`` to clear it. There is no type check to make sure that *cause* is either an exception - instance or :const:`None`. This steals a reference to *cause*. + instance or ``None``. This steals a reference to *cause*. :attr:`__suppress_context__` is implicitly set to ``True`` by this function. @@ -788,7 +788,7 @@ because the :ref:`call protocol ` takes care of recursion handling. Marks a point where a recursive C-level call is about to be performed. - If :const:`USE_STACKCHECK` is defined, this function checks if the OS + If :c:macro:`USE_STACKCHECK` is defined, this function checks if the OS stack overflowed using :c:func:`PyOS_CheckStack`. In this is the case, it sets a :exc:`MemoryError` and returns a nonzero value. diff --git a/Doc/c-api/file.rst b/Doc/c-api/file.rst index f32ecba9f27029..b36c800e00444a 100644 --- a/Doc/c-api/file.rst +++ b/Doc/c-api/file.rst @@ -93,7 +93,7 @@ the :mod:`io` APIs instead. .. index:: single: Py_PRINT_RAW Write object *obj* to file object *p*. The only supported flag for *flags* is - :const:`Py_PRINT_RAW`; if given, the :func:`str` of the object is written + :c:macro:`Py_PRINT_RAW`; if given, the :func:`str` of the object is written instead of the :func:`repr`. Return ``0`` on success or ``-1`` on failure; the appropriate exception will be set. diff --git a/Doc/c-api/float.rst b/Doc/c-api/float.rst index fd0be1108c6300..4f6ac0d8175c6b 100644 --- a/Doc/c-api/float.rst +++ b/Doc/c-api/float.rst @@ -109,7 +109,7 @@ Pack functions The pack routines write 2, 4 or 8 bytes, starting at *p*. *le* is an :c:expr:`int` argument, non-zero if you want the bytes string in little-endian format (exponent last, at ``p+1``, ``p+3``, or ``p+6`` ``p+7``), zero if you -want big-endian format (exponent first, at *p*). The :c:data:`PY_BIG_ENDIAN` +want big-endian format (exponent first, at *p*). The :c:macro:`PY_BIG_ENDIAN` constant can be used to use the native endian: it is equal to ``1`` on big endian processor, or ``0`` on little endian processor. @@ -140,7 +140,7 @@ Unpack functions The unpack routines read 2, 4 or 8 bytes, starting at *p*. *le* is an :c:expr:`int` argument, non-zero if the bytes string is in little-endian format (exponent last, at ``p+1``, ``p+3`` or ``p+6`` and ``p+7``), zero if big-endian -(exponent first, at *p*). The :c:data:`PY_BIG_ENDIAN` constant can be used to +(exponent first, at *p*). The :c:macro:`PY_BIG_ENDIAN` constant can be used to use the native endian: it is equal to ``1`` on big endian processor, or ``0`` on little endian processor. diff --git a/Doc/c-api/gcsupport.rst b/Doc/c-api/gcsupport.rst index 8c90d1e8991c10..fc690fd85c9f74 100644 --- a/Doc/c-api/gcsupport.rst +++ b/Doc/c-api/gcsupport.rst @@ -13,14 +13,12 @@ or strings), do not need to provide any explicit support for garbage collection. To create a container type, the :c:member:`~PyTypeObject.tp_flags` field of the type object must -include the :const:`Py_TPFLAGS_HAVE_GC` and provide an implementation of the +include the :c:macro:`Py_TPFLAGS_HAVE_GC` and provide an implementation of the :c:member:`~PyTypeObject.tp_traverse` handler. If instances of the type are mutable, a :c:member:`~PyTypeObject.tp_clear` implementation must also be provided. -.. data:: Py_TPFLAGS_HAVE_GC - :noindex: - +:c:macro:`Py_TPFLAGS_HAVE_GC` Objects with a type with this flag set must conform with the rules documented here. For convenience these objects will be referred to as container objects. @@ -52,18 +50,18 @@ rules: :c:member:`~PyTypeObject.tp_flags`, :c:member:`~PyTypeObject.tp_traverse` and :c:member:`~PyTypeObject.tp_clear` fields if the type inherits from a class that implements the garbage collector protocol and the child class - does *not* include the :const:`Py_TPFLAGS_HAVE_GC` flag. + does *not* include the :c:macro:`Py_TPFLAGS_HAVE_GC` flag. .. c:function:: TYPE* PyObject_GC_New(TYPE, PyTypeObject *type) Analogous to :c:func:`PyObject_New` but for container objects with the - :const:`Py_TPFLAGS_HAVE_GC` flag set. + :c:macro:`Py_TPFLAGS_HAVE_GC` flag set. .. c:function:: TYPE* PyObject_GC_NewVar(TYPE, PyTypeObject *type, Py_ssize_t size) Analogous to :c:func:`PyObject_NewVar` but for container objects with the - :const:`Py_TPFLAGS_HAVE_GC` flag set. + :c:macro:`Py_TPFLAGS_HAVE_GC` flag set. .. c:function:: TYPE* PyObject_GC_Resize(TYPE, PyVarObject *op, Py_ssize_t newsize) diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index dda87f4e31a426..9a42fc5a25dc22 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -1287,7 +1287,7 @@ All of the following functions must be called after :c:func:`Py_Initialize`. function does not steal any references to *exc*. To prevent naive misuse, you must write your own C extension to call this. Must be called with the GIL held. Returns the number of thread states modified; this is normally one, but will be - zero if the thread id isn't found. If *exc* is :const:`NULL`, the pending + zero if the thread id isn't found. If *exc* is ``NULL``, the pending exception (if any) for the thread is cleared. This raises no exceptions. .. versionchanged:: 3.7 @@ -1566,32 +1566,32 @@ Python-level trace functions in previous versions. The type of the trace function registered using :c:func:`PyEval_SetProfile` and :c:func:`PyEval_SetTrace`. The first parameter is the object passed to the registration function as *obj*, *frame* is the frame object to which the event - pertains, *what* is one of the constants :const:`PyTrace_CALL`, - :const:`PyTrace_EXCEPTION`, :const:`PyTrace_LINE`, :const:`PyTrace_RETURN`, - :const:`PyTrace_C_CALL`, :const:`PyTrace_C_EXCEPTION`, :const:`PyTrace_C_RETURN`, - or :const:`PyTrace_OPCODE`, and *arg* depends on the value of *what*: - - +------------------------------+----------------------------------------+ - | Value of *what* | Meaning of *arg* | - +==============================+========================================+ - | :const:`PyTrace_CALL` | Always :c:data:`Py_None`. | - +------------------------------+----------------------------------------+ - | :const:`PyTrace_EXCEPTION` | Exception information as returned by | - | | :func:`sys.exc_info`. | - +------------------------------+----------------------------------------+ - | :const:`PyTrace_LINE` | Always :c:data:`Py_None`. | - +------------------------------+----------------------------------------+ - | :const:`PyTrace_RETURN` | Value being returned to the caller, | - | | or ``NULL`` if caused by an exception. | - +------------------------------+----------------------------------------+ - | :const:`PyTrace_C_CALL` | Function object being called. | - +------------------------------+----------------------------------------+ - | :const:`PyTrace_C_EXCEPTION` | Function object being called. | - +------------------------------+----------------------------------------+ - | :const:`PyTrace_C_RETURN` | Function object being called. | - +------------------------------+----------------------------------------+ - | :const:`PyTrace_OPCODE` | Always :c:data:`Py_None`. | - +------------------------------+----------------------------------------+ + pertains, *what* is one of the constants :c:data:`PyTrace_CALL`, + :c:data:`PyTrace_EXCEPTION`, :c:data:`PyTrace_LINE`, :c:data:`PyTrace_RETURN`, + :c:data:`PyTrace_C_CALL`, :c:data:`PyTrace_C_EXCEPTION`, :c:data:`PyTrace_C_RETURN`, + or :c:data:`PyTrace_OPCODE`, and *arg* depends on the value of *what*: + + +-------------------------------+----------------------------------------+ + | Value of *what* | Meaning of *arg* | + +===============================+========================================+ + | :c:data:`PyTrace_CALL` | Always :c:data:`Py_None`. | + +-------------------------------+----------------------------------------+ + | :c:data:`PyTrace_EXCEPTION` | Exception information as returned by | + | | :func:`sys.exc_info`. | + +-------------------------------+----------------------------------------+ + | :c:data:`PyTrace_LINE` | Always :c:data:`Py_None`. | + +-------------------------------+----------------------------------------+ + | :c:data:`PyTrace_RETURN` | Value being returned to the caller, | + | | or ``NULL`` if caused by an exception. | + +-------------------------------+----------------------------------------+ + | :c:data:`PyTrace_C_CALL` | Function object being called. | + +-------------------------------+----------------------------------------+ + | :c:data:`PyTrace_C_EXCEPTION` | Function object being called. | + +-------------------------------+----------------------------------------+ + | :c:data:`PyTrace_C_RETURN` | Function object being called. | + +-------------------------------+----------------------------------------+ + | :c:data:`PyTrace_OPCODE` | Always :c:data:`Py_None`. | + +-------------------------------+----------------------------------------+ .. c:var:: int PyTrace_CALL @@ -1658,8 +1658,8 @@ Python-level trace functions in previous versions. function as its first parameter, and may be any Python object, or ``NULL``. If the profile function needs to maintain state, using a different value for *obj* for each thread provides a convenient and thread-safe place to store it. The - profile function is called for all monitored events except :const:`PyTrace_LINE` - :const:`PyTrace_OPCODE` and :const:`PyTrace_EXCEPTION`. + profile function is called for all monitored events except :c:data:`PyTrace_LINE` + :c:data:`PyTrace_OPCODE` and :c:data:`PyTrace_EXCEPTION`. See also the :func:`sys.setprofile` function. @@ -1672,8 +1672,8 @@ Python-level trace functions in previous versions. :c:func:`PyEval_SetProfile`, except the tracing function does receive line-number events and per-opcode events, but does not receive any event related to C function objects being called. Any trace function registered using :c:func:`PyEval_SetTrace` - will not receive :const:`PyTrace_C_CALL`, :const:`PyTrace_C_EXCEPTION` or - :const:`PyTrace_C_RETURN` as a value for the *what* parameter. + will not receive :c:data:`PyTrace_C_CALL`, :c:data:`PyTrace_C_EXCEPTION` or + :c:data:`PyTrace_C_RETURN` as a value for the *what* parameter. See also the :func:`sys.settrace` function. diff --git a/Doc/c-api/long.rst b/Doc/c-api/long.rst index de191581561fa8..874e96216097f4 100644 --- a/Doc/c-api/long.rst +++ b/Doc/c-api/long.rst @@ -141,8 +141,8 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. instance of :c:type:`PyLongObject`, first call its :meth:`~object.__index__` method (if present) to convert it to a :c:type:`PyLongObject`. - If the value of *obj* is greater than :const:`LONG_MAX` or less than - :const:`LONG_MIN`, set *\*overflow* to ``1`` or ``-1``, respectively, and + If the value of *obj* is greater than :c:macro:`LONG_MAX` or less than + :c:macro:`LONG_MIN`, set *\*overflow* to ``1`` or ``-1``, respectively, and return ``-1``; otherwise, set *\*overflow* to ``0``. If any other exception occurs set *\*overflow* to ``0`` and return ``-1`` as usual. @@ -182,8 +182,8 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. instance of :c:type:`PyLongObject`, first call its :meth:`~object.__index__` method (if present) to convert it to a :c:type:`PyLongObject`. - If the value of *obj* is greater than :const:`LLONG_MAX` or less than - :const:`LLONG_MIN`, set *\*overflow* to ``1`` or ``-1``, respectively, + If the value of *obj* is greater than :c:macro:`LLONG_MAX` or less than + :c:macro:`LLONG_MIN`, set *\*overflow* to ``1`` or ``-1``, respectively, and return ``-1``; otherwise, set *\*overflow* to ``0``. If any other exception occurs set *\*overflow* to ``0`` and return ``-1`` as usual. diff --git a/Doc/c-api/memory.rst b/Doc/c-api/memory.rst index 7041c15d23fb83..35c356f25c6c70 100644 --- a/Doc/c-api/memory.rst +++ b/Doc/c-api/memory.rst @@ -470,7 +470,7 @@ Customize Memory Allocators The new allocator must return a distinct non-``NULL`` pointer when requesting zero bytes. - For the :c:data:`PYMEM_DOMAIN_RAW` domain, the allocator must be + For the :c:macro:`PYMEM_DOMAIN_RAW` domain, the allocator must be thread-safe: the :term:`GIL ` is not held when the allocator is called. @@ -536,8 +536,8 @@ Runtime checks: - Detect write before the start of the buffer (buffer underflow). - Detect write after the end of the buffer (buffer overflow). - Check that the :term:`GIL ` is held when - allocator functions of :c:data:`PYMEM_DOMAIN_OBJ` (ex: - :c:func:`PyObject_Malloc`) and :c:data:`PYMEM_DOMAIN_MEM` (ex: + allocator functions of :c:macro:`PYMEM_DOMAIN_OBJ` (ex: + :c:func:`PyObject_Malloc`) and :c:macro:`PYMEM_DOMAIN_MEM` (ex: :c:func:`PyMem_Malloc`) domains are called. On error, the debug hooks use the :mod:`tracemalloc` module to get the @@ -557,9 +557,9 @@ that the treatment of negative indices differs from a Python slice): ``p[-S]`` API identifier (ASCII character): - * ``'r'`` for :c:data:`PYMEM_DOMAIN_RAW`. - * ``'m'`` for :c:data:`PYMEM_DOMAIN_MEM`. - * ``'o'`` for :c:data:`PYMEM_DOMAIN_OBJ`. + * ``'r'`` for :c:macro:`PYMEM_DOMAIN_RAW`. + * ``'m'`` for :c:macro:`PYMEM_DOMAIN_MEM`. + * ``'o'`` for :c:macro:`PYMEM_DOMAIN_OBJ`. ``p[-S+1:0]`` Copies of PYMEM_FORBIDDENBYTE. Used to catch under- writes and reads. @@ -601,7 +601,7 @@ PYMEM_CLEANBYTE (meaning uninitialized memory is getting used). compiled in release mode. On error, the debug hooks now use :mod:`tracemalloc` to get the traceback where a memory block was allocated. The debug hooks now also check if the GIL is held when functions of - :c:data:`PYMEM_DOMAIN_OBJ` and :c:data:`PYMEM_DOMAIN_MEM` domains are + :c:macro:`PYMEM_DOMAIN_OBJ` and :c:macro:`PYMEM_DOMAIN_MEM` domains are called. .. versionchanged:: 3.8 @@ -622,8 +622,8 @@ with a fixed size of 256 KiB. It falls back to :c:func:`PyMem_RawMalloc` and :c:func:`PyMem_RawRealloc` for allocations larger than 512 bytes. *pymalloc* is the :ref:`default allocator ` of the -:c:data:`PYMEM_DOMAIN_MEM` (ex: :c:func:`PyMem_Malloc`) and -:c:data:`PYMEM_DOMAIN_OBJ` (ex: :c:func:`PyObject_Malloc`) domains. +:c:macro:`PYMEM_DOMAIN_MEM` (ex: :c:func:`PyMem_Malloc`) and +:c:macro:`PYMEM_DOMAIN_OBJ` (ex: :c:func:`PyObject_Malloc`) domains. The arena allocator uses the following functions: diff --git a/Doc/c-api/module.rst b/Doc/c-api/module.rst index 230b471d473be7..bc8e3b23b99579 100644 --- a/Doc/c-api/module.rst +++ b/Doc/c-api/module.rst @@ -145,7 +145,7 @@ or request "multi-phase initialization" by returning the definition struct itsel .. c:member:: PyModuleDef_Base m_base - Always initialize this member to :const:`PyModuleDef_HEAD_INIT`. + Always initialize this member to :c:data:`PyModuleDef_HEAD_INIT`. .. c:member:: const char *m_name @@ -256,7 +256,7 @@ of the following two module creation functions: Create a new module object, given the definition in *def*. This behaves like :c:func:`PyModule_Create2` with *module_api_version* set to - :const:`PYTHON_API_VERSION`. + :c:macro:`PYTHON_API_VERSION`. .. c:function:: PyObject* PyModule_Create2(PyModuleDef *def, int module_api_version) @@ -390,7 +390,7 @@ objects dynamically. Note that both ``PyModule_FromDefAndSpec`` and Create a new module object, given the definition in *def* and the ModuleSpec *spec*. This behaves like :c:func:`PyModule_FromDefAndSpec2` - with *module_api_version* set to :const:`PYTHON_API_VERSION`. + with *module_api_version* set to :c:macro:`PYTHON_API_VERSION`. .. versionadded:: 3.5 diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index 7b8463e0873740..191a252808bda4 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -23,7 +23,7 @@ Object Protocol Print an object *o*, on file *fp*. Returns ``-1`` on error. The flags argument is used to enable certain printing options. The only option currently supported - is :const:`Py_PRINT_RAW`; if given, the :func:`str` of the object is written + is :c:macro:`Py_PRINT_RAW`; if given, the :func:`str` of the object is written instead of the :func:`repr`. @@ -162,8 +162,8 @@ Object Protocol .. c:function:: PyObject* PyObject_RichCompare(PyObject *o1, PyObject *o2, int opid) Compare the values of *o1* and *o2* using the operation specified by *opid*, - which must be one of :const:`Py_LT`, :const:`Py_LE`, :const:`Py_EQ`, - :const:`Py_NE`, :const:`Py_GT`, or :const:`Py_GE`, corresponding to ``<``, + which must be one of :c:macro:`Py_LT`, :c:macro:`Py_LE`, :c:macro:`Py_EQ`, + :c:macro:`Py_NE`, :c:macro:`Py_GT`, or :c:macro:`Py_GE`, corresponding to ``<``, ``<=``, ``==``, ``!=``, ``>``, or ``>=`` respectively. This is the equivalent of the Python expression ``o1 op o2``, where ``op`` is the operator corresponding to *opid*. Returns the value of the comparison on success, or ``NULL`` on failure. @@ -172,8 +172,8 @@ Object Protocol .. c:function:: int PyObject_RichCompareBool(PyObject *o1, PyObject *o2, int opid) Compare the values of *o1* and *o2* using the operation specified by *opid*, - which must be one of :const:`Py_LT`, :const:`Py_LE`, :const:`Py_EQ`, - :const:`Py_NE`, :const:`Py_GT`, or :const:`Py_GE`, corresponding to ``<``, + which must be one of :c:macro:`Py_LT`, :c:macro:`Py_LE`, :c:macro:`Py_EQ`, + :c:macro:`Py_NE`, :c:macro:`Py_GT`, or :c:macro:`Py_GE`, corresponding to ``<``, ``<=``, ``==``, ``!=``, ``>``, or ``>=`` respectively. Returns ``-1`` on error, ``0`` if the result is false, ``1`` otherwise. This is the equivalent of the Python expression ``o1 op o2``, where ``op`` is the operator corresponding to @@ -181,7 +181,7 @@ Object Protocol .. note:: If *o1* and *o2* are the same object, :c:func:`PyObject_RichCompareBool` - will always return ``1`` for :const:`Py_EQ` and ``0`` for :const:`Py_NE`. + will always return ``1`` for :c:macro:`Py_EQ` and ``0`` for :c:macro:`Py_NE`. .. c:function:: PyObject* PyObject_Format(PyObject *obj, PyObject *format_spec) diff --git a/Doc/c-api/slice.rst b/Doc/c-api/slice.rst index 33169ccce89043..af4c540068cbf4 100644 --- a/Doc/c-api/slice.rst +++ b/Doc/c-api/slice.rst @@ -34,7 +34,7 @@ Slice Objects *length* as errors. Returns ``0`` on success and ``-1`` on error with no exception set (unless one of - the indices was not :const:`None` and failed to be converted to an integer, + the indices was not ``None`` and failed to be converted to an integer, in which case ``-1`` is returned with an exception set). You probably do not want to use this function. diff --git a/Doc/c-api/stable.rst b/Doc/c-api/stable.rst index 4ae20e93e36785..a697f6cd3edd90 100644 --- a/Doc/c-api/stable.rst +++ b/Doc/c-api/stable.rst @@ -44,7 +44,7 @@ embedding Python.) Define this macro before including ``Python.h`` to opt in to only use the Limited API, and to select the Limited API version. - Define ``Py_LIMITED_API`` to the value of :c:data:`PY_VERSION_HEX` + Define ``Py_LIMITED_API`` to the value of :c:macro:`PY_VERSION_HEX` corresponding to the lowest Python version your extension supports. The extension will work without recompilation with all Python 3 releases from the specified one onward, and can use Limited API introduced up to that diff --git a/Doc/c-api/structures.rst b/Doc/c-api/structures.rst index cfd6d20a348990..e9f158c176924e 100644 --- a/Doc/c-api/structures.rst +++ b/Doc/c-api/structures.rst @@ -198,7 +198,7 @@ Implementing functions and methods .. c:type:: PyCFunctionWithKeywords Type of the functions used to implement Python callables in C - with signature :const:`METH_VARARGS | METH_KEYWORDS`. + with signature :ref:`METH_VARARGS | METH_KEYWORDS `. The function signature is:: PyObject *PyCFunctionWithKeywords(PyObject *self, @@ -209,7 +209,7 @@ Implementing functions and methods .. c:type:: _PyCFunctionFast Type of the functions used to implement Python callables in C - with signature :const:`METH_FASTCALL`. + with signature :c:macro:`METH_FASTCALL`. The function signature is:: PyObject *_PyCFunctionFast(PyObject *self, @@ -219,7 +219,7 @@ Implementing functions and methods .. c:type:: _PyCFunctionFastWithKeywords Type of the functions used to implement Python callables in C - with signature :const:`METH_FASTCALL | METH_KEYWORDS`. + with signature :ref:`METH_FASTCALL | METH_KEYWORDS `. The function signature is:: PyObject *_PyCFunctionFastWithKeywords(PyObject *self, @@ -230,7 +230,7 @@ Implementing functions and methods .. c:type:: PyCMethod Type of the functions used to implement Python callables in C - with signature :const:`METH_METHOD | METH_FASTCALL | METH_KEYWORDS`. + with signature :ref:`METH_METHOD | METH_FASTCALL | METH_KEYWORDS `. The function signature is:: PyObject *PyCMethod(PyObject *self, @@ -276,7 +276,7 @@ convention. There are these calling conventions: -.. data:: METH_VARARGS +.. c:macro:: METH_VARARGS This is the typical calling convention, where the methods have the type :c:type:`PyCFunction`. The function expects two :c:expr:`PyObject*` values. @@ -286,8 +286,17 @@ There are these calling conventions: using :c:func:`PyArg_ParseTuple` or :c:func:`PyArg_UnpackTuple`. -.. data:: METH_VARARGS | METH_KEYWORDS +.. c:macro:: METH_KEYWORDS + Can only be used in certain combinations with other flags: + :ref:`METH_VARARGS | METH_KEYWORDS `, + :ref:`METH_FASTCALL | METH_KEYWORDS ` and + :ref:`METH_METHOD | METH_FASTCALL | METH_KEYWORDS `. + + +.. _METH_VARARGS-METH_KEYWORDS: + +:c:expr:`METH_VARARGS | METH_KEYWORDS` Methods with these flags must be of type :c:type:`PyCFunctionWithKeywords`. The function expects three parameters: *self*, *args*, *kwargs* where *kwargs* is a dictionary of all the keyword arguments or possibly ``NULL`` @@ -295,7 +304,7 @@ There are these calling conventions: using :c:func:`PyArg_ParseTupleAndKeywords`. -.. data:: METH_FASTCALL +.. c:macro:: METH_FASTCALL Fast calling convention supporting only positional arguments. The methods have the type :c:type:`_PyCFunctionFast`. @@ -310,9 +319,10 @@ There are these calling conventions: ``METH_FASTCALL`` is now part of the stable ABI. -.. data:: METH_FASTCALL | METH_KEYWORDS +.. _METH_FASTCALL-METH_KEYWORDS: - Extension of :const:`METH_FASTCALL` supporting also keyword arguments, +:c:expr:`METH_FASTCALL | METH_KEYWORDS` + Extension of :c:macro:`METH_FASTCALL` supporting also keyword arguments, with methods of type :c:type:`_PyCFunctionFastWithKeywords`. Keyword arguments are passed the same way as in the :ref:`vectorcall protocol `: @@ -325,10 +335,18 @@ There are these calling conventions: .. versionadded:: 3.7 -.. data:: METH_METHOD | METH_FASTCALL | METH_KEYWORDS +.. c:macro:: METH_METHOD + + Can only be used in the combination with other flags: + :ref:`METH_METHOD | METH_FASTCALL | METH_KEYWORDS `. + + +.. _METH_METHOD-METH_FASTCALL-METH_KEYWORDS: - Extension of :const:`METH_FASTCALL | METH_KEYWORDS` supporting the *defining - class*, that is, the class that contains the method in question. +:c:expr:`METH_METHOD | METH_FASTCALL | METH_KEYWORDS` + Extension of :ref:`METH_FASTCALL | METH_KEYWORDS ` + supporting the *defining class*, that is, + the class that contains the method in question. The defining class might be a superclass of ``Py_TYPE(self)``. The method needs to be of type :c:type:`PyCMethod`, the same as for @@ -338,10 +356,10 @@ There are these calling conventions: .. versionadded:: 3.9 -.. data:: METH_NOARGS +.. c:macro:: METH_NOARGS Methods without parameters don't need to check whether arguments are given if - they are listed with the :const:`METH_NOARGS` flag. They need to be of type + they are listed with the :c:macro:`METH_NOARGS` flag. They need to be of type :c:type:`PyCFunction`. The first parameter is typically named *self* and will hold a reference to the module or object instance. In all cases the second parameter will be ``NULL``. @@ -350,9 +368,9 @@ There are these calling conventions: :c:macro:`Py_UNUSED` can be used to prevent a compiler warning. -.. data:: METH_O +.. c:macro:: METH_O - Methods with a single object argument can be listed with the :const:`METH_O` + Methods with a single object argument can be listed with the :c:macro:`METH_O` flag, instead of invoking :c:func:`PyArg_ParseTuple` with a ``"O"`` argument. They have the type :c:type:`PyCFunction`, with the *self* parameter, and a :c:expr:`PyObject*` parameter representing the single argument. @@ -364,7 +382,7 @@ defined for modules. At most one of these flags may be set for any given method. -.. data:: METH_CLASS +.. c:macro:: METH_CLASS .. index:: pair: built-in function; classmethod @@ -374,7 +392,7 @@ method. function. -.. data:: METH_STATIC +.. c:macro:: METH_STATIC .. index:: pair: built-in function; staticmethod @@ -386,7 +404,7 @@ One other constant controls whether a method is loaded in place of another definition with the same method name. -.. data:: METH_COEXIST +.. c:macro:: METH_COEXIST The method will be loaded in place of existing definitions. Without *METH_COEXIST*, the default is to skip repeated definitions. Since slot diff --git a/Doc/c-api/sys.rst b/Doc/c-api/sys.rst index 517b57b14768bf..305a315549d156 100644 --- a/Doc/c-api/sys.rst +++ b/Doc/c-api/sys.rst @@ -95,9 +95,9 @@ Operating System Utilities .. c:function:: int PyOS_CheckStack() Return true when the interpreter runs out of stack space. This is a reliable - check, but is only available when :const:`USE_STACKCHECK` is defined (currently + check, but is only available when :c:macro:`USE_STACKCHECK` is defined (currently on certain versions of Windows using the Microsoft Visual C++ compiler). - :const:`USE_STACKCHECK` will be defined automatically; you should never + :c:macro:`USE_STACKCHECK` will be defined automatically; you should never change the definition in your own code. diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst index 52eeef0a008639..a4ca724ad671a2 100644 --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -66,7 +66,7 @@ Type Objects .. c:function:: int PyType_IS_GC(PyTypeObject *o) Return true if the type object includes support for the cycle detector; this - tests the type flag :const:`Py_TPFLAGS_HAVE_GC`. + tests the type flag :c:macro:`Py_TPFLAGS_HAVE_GC`. .. c:function:: int PyType_IsSubtype(PyTypeObject *a, PyTypeObject *b) @@ -99,10 +99,10 @@ Type Objects .. note:: If some of the base classes implements the GC protocol and the provided - type does not include the :const:`Py_TPFLAGS_HAVE_GC` in its flags, then + type does not include the :c:macro:`Py_TPFLAGS_HAVE_GC` in its flags, then the GC protocol will be automatically implemented from its parents. On the contrary, if the type being created does include - :const:`Py_TPFLAGS_HAVE_GC` in its flags then it **must** implement the + :c:macro:`Py_TPFLAGS_HAVE_GC` in its flags then it **must** implement the GC protocol itself by at least implementing the :c:member:`~PyTypeObject.tp_traverse` handle. @@ -193,7 +193,7 @@ The following functions and structs are used to create .. c:function:: PyObject* PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) Creates and returns a :ref:`heap type ` from the *spec* - (:const:`Py_TPFLAGS_HEAPTYPE`). + (:c:macro:`Py_TPFLAGS_HEAPTYPE`). The *bases* argument can be used to specify base classes; it can either be only one class or a tuple of classes. diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index ce327166f3ab0e..32989841c5b1a8 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -667,7 +667,7 @@ and :c:type:`PyType_Type` effectively act as defaults.) memory buffers owned by the instance (using the freeing function corresponding to the allocation function used to allocate the buffer), and call the type's :c:member:`~PyTypeObject.tp_free` function. If the type is not subtypable - (doesn't have the :const:`Py_TPFLAGS_BASETYPE` flag bit set), it is + (doesn't have the :c:macro:`Py_TPFLAGS_BASETYPE` flag bit set), it is permissible to call the object deallocator directly instead of via :c:member:`~PyTypeObject.tp_free`. The object deallocator should be the one used to allocate the instance; this is normally :c:func:`PyObject_Del` if the instance was allocated @@ -675,7 +675,7 @@ and :c:type:`PyType_Type` effectively act as defaults.) :c:func:`PyObject_GC_Del` if the instance was allocated using :c:func:`PyObject_GC_New` or :c:func:`PyObject_GC_NewVar`. - If the type supports garbage collection (has the :const:`Py_TPFLAGS_HAVE_GC` + If the type supports garbage collection (has the :c:macro:`Py_TPFLAGS_HAVE_GC` flag bit set), the destructor should call :c:func:`PyObject_GC_UnTrack` before clearing any member fields. @@ -687,7 +687,7 @@ and :c:type:`PyType_Type` effectively act as defaults.) Py_TYPE(self)->tp_free((PyObject *)self); } - Finally, if the type is heap allocated (:const:`Py_TPFLAGS_HEAPTYPE`), the + Finally, if the type is heap allocated (:c:macro:`Py_TPFLAGS_HEAPTYPE`), the deallocator should decrement the reference count for its type object after calling the type deallocator. In order to avoid dangling pointers, the recommended way to achieve this is: @@ -714,12 +714,12 @@ and :c:type:`PyType_Type` effectively act as defaults.) a more efficient alternative of the simpler :c:member:`~PyTypeObject.tp_call`. - This field is only used if the flag :const:`Py_TPFLAGS_HAVE_VECTORCALL` + This field is only used if the flag :c:macro:`Py_TPFLAGS_HAVE_VECTORCALL` is set. If so, this must be a positive integer containing the offset in the instance of a :c:type:`vectorcallfunc` pointer. The *vectorcallfunc* pointer may be ``NULL``, in which case the instance behaves - as if :const:`Py_TPFLAGS_HAVE_VECTORCALL` was not set: calling the instance + as if :c:macro:`Py_TPFLAGS_HAVE_VECTORCALL` was not set: calling the instance falls back to :c:member:`~PyTypeObject.tp_call`. Any class that sets ``Py_TPFLAGS_HAVE_VECTORCALL`` must also set @@ -743,12 +743,12 @@ and :c:type:`PyType_Type` effectively act as defaults.) **Inheritance:** This field is always inherited. - However, the :const:`Py_TPFLAGS_HAVE_VECTORCALL` flag is not + However, the :c:macro:`Py_TPFLAGS_HAVE_VECTORCALL` flag is not always inherited. If it's not, then the subclass won't use :ref:`vectorcall `, except when :c:func:`PyVectorcall_Call` is explicitly called. This is in particular the case for types without the - :const:`Py_TPFLAGS_IMMUTABLETYPE` flag set (including subclasses defined in + :c:macro:`Py_TPFLAGS_IMMUTABLETYPE` flag set (including subclasses defined in Python). @@ -1020,9 +1020,9 @@ and :c:type:`PyType_Type` effectively act as defaults.) this flag bit. The flag bits that pertain to extension structures are strictly inherited if the extension structure is inherited, i.e. the base type's value of the flag bit is copied into the subtype together with a pointer to the extension - structure. The :const:`Py_TPFLAGS_HAVE_GC` flag bit is inherited together with + structure. The :c:macro:`Py_TPFLAGS_HAVE_GC` flag bit is inherited together with the :c:member:`~PyTypeObject.tp_traverse` and :c:member:`~PyTypeObject.tp_clear` fields, i.e. if the - :const:`Py_TPFLAGS_HAVE_GC` flag bit is clear in the subtype and the + :c:macro:`Py_TPFLAGS_HAVE_GC` flag bit is clear in the subtype and the :c:member:`~PyTypeObject.tp_traverse` and :c:member:`~PyTypeObject.tp_clear` fields in the subtype exist and have ``NULL`` values. @@ -1035,12 +1035,14 @@ and :c:type:`PyType_Type` effectively act as defaults.) **Bit Masks:** + .. c:namespace:: NULL + The following bit masks are currently defined; these can be ORed together using the ``|`` operator to form the value of the :c:member:`~PyTypeObject.tp_flags` field. The macro :c:func:`PyType_HasFeature` takes a type and a flags value, *tp* and *f*, and checks whether ``tp->tp_flags & f`` is non-zero. - .. data:: Py_TPFLAGS_HEAPTYPE + .. c:macro:: Py_TPFLAGS_HEAPTYPE This bit is set when the type object itself is allocated on the heap, for example, types created dynamically using :c:func:`PyType_FromSpec`. In this @@ -1055,7 +1057,7 @@ and :c:type:`PyType_Type` effectively act as defaults.) ??? - .. data:: Py_TPFLAGS_BASETYPE + .. c:macro:: Py_TPFLAGS_BASETYPE This bit is set when the type can be used as the base type of another type. If this bit is clear, the type cannot be subtyped (similar to a "final" class in @@ -1066,7 +1068,7 @@ and :c:type:`PyType_Type` effectively act as defaults.) ??? - .. data:: Py_TPFLAGS_READY + .. c:macro:: Py_TPFLAGS_READY This bit is set when the type object has been fully initialized by :c:func:`PyType_Ready`. @@ -1076,7 +1078,7 @@ and :c:type:`PyType_Type` effectively act as defaults.) ??? - .. data:: Py_TPFLAGS_READYING + .. c:macro:: Py_TPFLAGS_READYING This bit is set while :c:func:`PyType_Ready` is in the process of initializing the type object. @@ -1086,7 +1088,7 @@ and :c:type:`PyType_Type` effectively act as defaults.) ??? - .. data:: Py_TPFLAGS_HAVE_GC + .. c:macro:: Py_TPFLAGS_HAVE_GC This bit is set when the object supports garbage collection. If this bit is set, instances must be created using :c:func:`PyObject_GC_New` and @@ -1097,28 +1099,28 @@ and :c:type:`PyType_Type` effectively act as defaults.) **Inheritance:** - Group: :const:`Py_TPFLAGS_HAVE_GC`, :attr:`tp_traverse`, :attr:`tp_clear` + Group: :c:macro:`Py_TPFLAGS_HAVE_GC`, :attr:`tp_traverse`, :attr:`tp_clear` - The :const:`Py_TPFLAGS_HAVE_GC` flag bit is inherited + The :c:macro:`Py_TPFLAGS_HAVE_GC` flag bit is inherited together with the :attr:`tp_traverse` and :attr:`tp_clear` - fields, i.e. if the :const:`Py_TPFLAGS_HAVE_GC` flag bit is + fields, i.e. if the :c:macro:`Py_TPFLAGS_HAVE_GC` flag bit is clear in the subtype and the :attr:`tp_traverse` and :attr:`tp_clear` fields in the subtype exist and have ``NULL`` values. - .. data:: Py_TPFLAGS_DEFAULT + .. c:macro:: Py_TPFLAGS_DEFAULT This is a bitmask of all the bits that pertain to the existence of certain fields in the type object and its extension structures. Currently, it includes - the following bits: :const:`Py_TPFLAGS_HAVE_STACKLESS_EXTENSION`. + the following bits: :c:macro:`Py_TPFLAGS_HAVE_STACKLESS_EXTENSION`. **Inheritance:** ??? - .. data:: Py_TPFLAGS_METHOD_DESCRIPTOR + .. c:macro:: Py_TPFLAGS_METHOD_DESCRIPTOR This bit indicates that objects behave like unbound methods. @@ -1139,21 +1141,21 @@ and :c:type:`PyType_Type` effectively act as defaults.) **Inheritance:** This flag is never inherited by types without the - :const:`Py_TPFLAGS_IMMUTABLETYPE` flag set. For extension types, it is + :c:macro:`Py_TPFLAGS_IMMUTABLETYPE` flag set. For extension types, it is inherited whenever :c:member:`~PyTypeObject.tp_descr_get` is inherited. .. XXX Document more flags here? - .. data:: Py_TPFLAGS_LONG_SUBCLASS - .. data:: Py_TPFLAGS_LIST_SUBCLASS - .. data:: Py_TPFLAGS_TUPLE_SUBCLASS - .. data:: Py_TPFLAGS_BYTES_SUBCLASS - .. data:: Py_TPFLAGS_UNICODE_SUBCLASS - .. data:: Py_TPFLAGS_DICT_SUBCLASS - .. data:: Py_TPFLAGS_BASE_EXC_SUBCLASS - .. data:: Py_TPFLAGS_TYPE_SUBCLASS + .. c:macro:: Py_TPFLAGS_LONG_SUBCLASS + .. c:macro:: Py_TPFLAGS_LIST_SUBCLASS + .. c:macro:: Py_TPFLAGS_TUPLE_SUBCLASS + .. c:macro:: Py_TPFLAGS_BYTES_SUBCLASS + .. c:macro:: Py_TPFLAGS_UNICODE_SUBCLASS + .. c:macro:: Py_TPFLAGS_DICT_SUBCLASS + .. c:macro:: Py_TPFLAGS_BASE_EXC_SUBCLASS + .. c:macro:: Py_TPFLAGS_TYPE_SUBCLASS These flags are used by functions such as :c:func:`PyLong_Check` to quickly determine if a type is a subclass @@ -1164,7 +1166,7 @@ and :c:type:`PyType_Type` effectively act as defaults.) will behave differently depending on what kind of check is used. - .. data:: Py_TPFLAGS_HAVE_FINALIZE + .. c:macro:: Py_TPFLAGS_HAVE_FINALIZE This bit is set when the :c:member:`~PyTypeObject.tp_finalize` slot is present in the type structure. @@ -1177,7 +1179,7 @@ and :c:type:`PyType_Type` effectively act as defaults.) type structure. - .. data:: Py_TPFLAGS_HAVE_VECTORCALL + .. c:macro:: Py_TPFLAGS_HAVE_VECTORCALL This bit is set when the class implements the :ref:`vectorcall protocol `. @@ -1186,12 +1188,12 @@ and :c:type:`PyType_Type` effectively act as defaults.) **Inheritance:** This bit is inherited for types with the - :const:`Py_TPFLAGS_IMMUTABLETYPE` flag set, if + :c:macro:`Py_TPFLAGS_IMMUTABLETYPE` flag set, if :c:member:`~PyTypeObject.tp_call` is also inherited. .. versionadded:: 3.9 - .. data:: Py_TPFLAGS_IMMUTABLETYPE + .. c:macro:: Py_TPFLAGS_IMMUTABLETYPE This bit is set for type objects that are immutable: type attributes cannot be set nor deleted. @@ -1204,7 +1206,7 @@ and :c:type:`PyType_Type` effectively act as defaults.) .. versionadded:: 3.10 - .. data:: Py_TPFLAGS_DISALLOW_INSTANTIATION + .. c:macro:: Py_TPFLAGS_DISALLOW_INSTANTIATION Disallow creating instances of the type: set :c:member:`~PyTypeObject.tp_new` to NULL and don't create the ``__new__`` @@ -1235,7 +1237,7 @@ and :c:type:`PyType_Type` effectively act as defaults.) .. versionadded:: 3.10 - .. data:: Py_TPFLAGS_MAPPING + .. c:macro:: Py_TPFLAGS_MAPPING This bit indicates that instances of the class may match mapping patterns when used as the subject of a :keyword:`match` block. It is automatically @@ -1244,20 +1246,20 @@ and :c:type:`PyType_Type` effectively act as defaults.) .. note:: - :const:`Py_TPFLAGS_MAPPING` and :const:`Py_TPFLAGS_SEQUENCE` are + :c:macro:`Py_TPFLAGS_MAPPING` and :c:macro:`Py_TPFLAGS_SEQUENCE` are mutually exclusive; it is an error to enable both flags simultaneously. **Inheritance:** This flag is inherited by types that do not already set - :const:`Py_TPFLAGS_SEQUENCE`. + :c:macro:`Py_TPFLAGS_SEQUENCE`. .. seealso:: :pep:`634` -- Structural Pattern Matching: Specification .. versionadded:: 3.10 - .. data:: Py_TPFLAGS_SEQUENCE + .. c:macro:: Py_TPFLAGS_SEQUENCE This bit indicates that instances of the class may match sequence patterns when used as the subject of a :keyword:`match` block. It is automatically @@ -1266,13 +1268,13 @@ and :c:type:`PyType_Type` effectively act as defaults.) .. note:: - :const:`Py_TPFLAGS_MAPPING` and :const:`Py_TPFLAGS_SEQUENCE` are + :c:macro:`Py_TPFLAGS_MAPPING` and :c:macro:`Py_TPFLAGS_SEQUENCE` are mutually exclusive; it is an error to enable both flags simultaneously. **Inheritance:** This flag is inherited by types that do not already set - :const:`Py_TPFLAGS_MAPPING`. + :c:macro:`Py_TPFLAGS_MAPPING`. .. seealso:: :pep:`634` -- Structural Pattern Matching: Specification @@ -1293,7 +1295,7 @@ and :c:type:`PyType_Type` effectively act as defaults.) .. c:member:: traverseproc PyTypeObject.tp_traverse An optional pointer to a traversal function for the garbage collector. This is - only used if the :const:`Py_TPFLAGS_HAVE_GC` flag bit is set. The signature is:: + only used if the :c:macro:`Py_TPFLAGS_HAVE_GC` flag bit is set. The signature is:: int tp_traverse(PyObject *self, visitproc visit, void *arg); @@ -1355,10 +1357,10 @@ and :c:type:`PyType_Type` effectively act as defaults.) **Inheritance:** - Group: :const:`Py_TPFLAGS_HAVE_GC`, :attr:`tp_traverse`, :attr:`tp_clear` + Group: :c:macro:`Py_TPFLAGS_HAVE_GC`, :attr:`tp_traverse`, :attr:`tp_clear` This field is inherited by subtypes together with :c:member:`~PyTypeObject.tp_clear` and the - :const:`Py_TPFLAGS_HAVE_GC` flag bit: the flag bit, :c:member:`~PyTypeObject.tp_traverse`, and + :c:macro:`Py_TPFLAGS_HAVE_GC` flag bit: the flag bit, :c:member:`~PyTypeObject.tp_traverse`, and :c:member:`~PyTypeObject.tp_clear` are all inherited from the base type if they are all zero in the subtype. @@ -1366,7 +1368,7 @@ and :c:type:`PyType_Type` effectively act as defaults.) .. c:member:: inquiry PyTypeObject.tp_clear An optional pointer to a clear function for the garbage collector. This is only - used if the :const:`Py_TPFLAGS_HAVE_GC` flag bit is set. The signature is:: + used if the :c:macro:`Py_TPFLAGS_HAVE_GC` flag bit is set. The signature is:: int tp_clear(PyObject *); @@ -1422,10 +1424,10 @@ and :c:type:`PyType_Type` effectively act as defaults.) **Inheritance:** - Group: :const:`Py_TPFLAGS_HAVE_GC`, :attr:`tp_traverse`, :attr:`tp_clear` + Group: :c:macro:`Py_TPFLAGS_HAVE_GC`, :attr:`tp_traverse`, :attr:`tp_clear` This field is inherited by subtypes together with :c:member:`~PyTypeObject.tp_traverse` and the - :const:`Py_TPFLAGS_HAVE_GC` flag bit: the flag bit, :c:member:`~PyTypeObject.tp_traverse`, and + :c:macro:`Py_TPFLAGS_HAVE_GC` flag bit: the flag bit, :c:member:`~PyTypeObject.tp_traverse`, and :c:member:`~PyTypeObject.tp_clear` are all inherited from the base type if they are all zero in the subtype. @@ -1447,21 +1449,21 @@ and :c:type:`PyType_Type` effectively act as defaults.) The following constants are defined to be used as the third argument for :c:member:`~PyTypeObject.tp_richcompare` and for :c:func:`PyObject_RichCompare`: - +----------------+------------+ - | Constant | Comparison | - +================+============+ - | :const:`Py_LT` | ``<`` | - +----------------+------------+ - | :const:`Py_LE` | ``<=`` | - +----------------+------------+ - | :const:`Py_EQ` | ``==`` | - +----------------+------------+ - | :const:`Py_NE` | ``!=`` | - +----------------+------------+ - | :const:`Py_GT` | ``>`` | - +----------------+------------+ - | :const:`Py_GE` | ``>=`` | - +----------------+------------+ + +------------------+------------+ + | Constant | Comparison | + +==================+============+ + | :c:macro:`Py_LT` | ``<`` | + +------------------+------------+ + | :c:macro:`Py_LE` | ``<=`` | + +------------------+------------+ + | :c:macro:`Py_EQ` | ``==`` | + +------------------+------------+ + | :c:macro:`Py_NE` | ``!=`` | + +------------------+------------+ + | :c:macro:`Py_GT` | ``>`` | + +------------------+------------+ + | :c:macro:`Py_GE` | ``>=`` | + +------------------+------------+ The following macro is defined to ease writing rich comparison functions: @@ -1839,7 +1841,7 @@ and :c:type:`PyType_Type` effectively act as defaults.) in :c:member:`~PyTypeObject.tp_new`, while for mutable types, most initialization should be deferred to :c:member:`~PyTypeObject.tp_init`. - Set the :const:`Py_TPFLAGS_DISALLOW_INSTANTIATION` flag to disallow creating + Set the :c:macro:`Py_TPFLAGS_DISALLOW_INSTANTIATION` flag to disallow creating instances of the type in Python. **Inheritance:** @@ -1873,7 +1875,7 @@ and :c:type:`PyType_Type` effectively act as defaults.) In dynamic subtypes, this field is set to a deallocator suitable to match :c:func:`PyType_GenericAlloc` and the value of the - :const:`Py_TPFLAGS_HAVE_GC` flag bit. + :c:macro:`Py_TPFLAGS_HAVE_GC` flag bit. For static subtypes, :c:type:`PyBaseObject_Type` uses PyObject_Del. @@ -1884,7 +1886,7 @@ and :c:type:`PyType_Type` effectively act as defaults.) The garbage collector needs to know whether a particular object is collectible or not. Normally, it is sufficient to look at the object's type's - :c:member:`~PyTypeObject.tp_flags` field, and check the :const:`Py_TPFLAGS_HAVE_GC` flag bit. But + :c:member:`~PyTypeObject.tp_flags` field, and check the :c:macro:`Py_TPFLAGS_HAVE_GC` flag bit. But some types have a mixture of statically and dynamically allocated instances, and the statically allocated instances are not collectible. Such types should define this function; it should return ``1`` for a collectible instance, and @@ -1903,7 +1905,7 @@ and :c:type:`PyType_Type` effectively act as defaults.) **Default:** This slot has no default. If this field is ``NULL``, - :const:`Py_TPFLAGS_HAVE_GC` is used as the functional equivalent. + :c:macro:`Py_TPFLAGS_HAVE_GC` is used as the functional equivalent. .. c:member:: PyObject* PyTypeObject.tp_bases @@ -2035,7 +2037,7 @@ and :c:type:`PyType_Type` effectively act as defaults.) .. versionchanged:: 3.8 Before version 3.8 it was necessary to set the - :const:`Py_TPFLAGS_HAVE_FINALIZE` flags bit in order for this field to be + :c:macro:`Py_TPFLAGS_HAVE_FINALIZE` flags bit in order for this field to be used. This is no longer required. .. seealso:: "Safe object finalization" (:pep:`442`) @@ -2087,7 +2089,7 @@ Heap Types An alternative to :ref:`static types ` is *heap-allocated types*, or *heap types* for short, which correspond closely to classes created by -Python's ``class`` statement. Heap types have the :const:`Py_TPFLAGS_HEAPTYPE` +Python's ``class`` statement. Heap types have the :c:macro:`Py_TPFLAGS_HEAPTYPE` flag set. This is done by filling a :c:type:`PyType_Spec` structure and calling @@ -2697,7 +2699,7 @@ A type that supports weakrefs, instance dicts, and hashing:: A str subclass that cannot be subclassed and cannot be called to create instances (e.g. uses a separate factory func) using -:c:data:`Py_TPFLAGS_DISALLOW_INSTANTIATION` flag:: +:c:macro:`Py_TPFLAGS_DISALLOW_INSTANTIATION` flag:: typedef struct { PyUnicodeObject raw; diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst index 12f95a2e8a39d0..b2241ea8bd2e1b 100644 --- a/Doc/c-api/unicode.rst +++ b/Doc/c-api/unicode.rst @@ -1391,7 +1391,7 @@ the user settings on the machine running the codec. Encode the Unicode object using the specified code page and return a Python bytes object. Return ``NULL`` if an exception was raised by the codec. Use - :c:data:`CP_ACP` code page to get the MBCS encoder. + :c:macro:`CP_ACP` code page to get the MBCS encoder. .. versionadded:: 3.3 @@ -1510,11 +1510,11 @@ They all return ``NULL`` or ``-1`` if an exception occurs. Rich compare two Unicode strings and return one of the following: * ``NULL`` in case an exception was raised - * :const:`Py_True` or :const:`Py_False` for successful comparisons - * :const:`Py_NotImplemented` in case the type combination is unknown + * :c:data:`Py_True` or :c:data:`Py_False` for successful comparisons + * :c:data:`Py_NotImplemented` in case the type combination is unknown - Possible values for *op* are :const:`Py_GT`, :const:`Py_GE`, :const:`Py_EQ`, - :const:`Py_NE`, :const:`Py_LT`, and :const:`Py_LE`. + Possible values for *op* are :c:macro:`Py_GT`, :c:macro:`Py_GE`, :c:macro:`Py_EQ`, + :c:macro:`Py_NE`, :c:macro:`Py_LT`, and :c:macro:`Py_LE`. .. c:function:: PyObject* PyUnicode_Format(PyObject *format, PyObject *args) diff --git a/Doc/c-api/veryhigh.rst b/Doc/c-api/veryhigh.rst index bfb14ac912fcac..cfbb44f4eb3a67 100644 --- a/Doc/c-api/veryhigh.rst +++ b/Doc/c-api/veryhigh.rst @@ -12,8 +12,8 @@ file or a buffer, but they will not let you interact in a more detailed way with the interpreter. Several of these functions accept a start symbol from the grammar as a -parameter. The available start symbols are :const:`Py_eval_input`, -:const:`Py_file_input`, and :const:`Py_single_input`. These are described +parameter. The available start symbols are :c:data:`Py_eval_input`, +:c:data:`Py_file_input`, and :c:data:`Py_single_input`. These are described following the functions which accept them as parameters. Note also that several of these functions take :c:expr:`FILE*` parameters. One @@ -248,8 +248,8 @@ the same library that the Python runtime is using. Parse and compile the Python source code in *str*, returning the resulting code object. The start token is given by *start*; this can be used to constrain the - code which can be compiled and should be :const:`Py_eval_input`, - :const:`Py_file_input`, or :const:`Py_single_input`. The filename specified by + code which can be compiled and should be :c:data:`Py_eval_input`, + :c:data:`Py_file_input`, or :c:data:`Py_single_input`. The filename specified by *filename* is used to construct the code object and may appear in tracebacks or :exc:`SyntaxError` exception messages. This returns ``NULL`` if the code cannot be parsed or compiled. diff --git a/Doc/extending/extending.rst b/Doc/extending/extending.rst index d9bf4fd6c7ae0e..76e0490d0d22df 100644 --- a/Doc/extending/extending.rst +++ b/Doc/extending/extending.rst @@ -335,7 +335,7 @@ When using only ``METH_VARARGS``, the function should expect the Python-level parameters to be passed in as a tuple acceptable for parsing via :c:func:`PyArg_ParseTuple`; more information on this function is provided below. -The :const:`METH_KEYWORDS` bit may be set in the third field if keyword +The :c:macro:`METH_KEYWORDS` bit may be set in the third field if keyword arguments should be passed to the function. In this case, the C function should accept a third ``PyObject *`` parameter which will be a dictionary of keywords. Use :c:func:`PyArg_ParseTupleAndKeywords` to parse the arguments to such a @@ -527,7 +527,7 @@ be part of a module definition:: } This function must be registered with the interpreter using the -:const:`METH_VARARGS` flag; this is described in section :ref:`methodtable`. The +:c:macro:`METH_VARARGS` flag; this is described in section :ref:`methodtable`. The :c:func:`PyArg_ParseTuple` function and its arguments are documented in section :ref:`parsetuple`. diff --git a/Doc/extending/newtypes_tutorial.rst b/Doc/extending/newtypes_tutorial.rst index 5d4a3f06dd5402..b1bf0535048ebd 100644 --- a/Doc/extending/newtypes_tutorial.rst +++ b/Doc/extending/newtypes_tutorial.rst @@ -151,7 +151,7 @@ only used for variable-sized objects and should otherwise be zero. base type will be :class:`object`, or else you will be adding data members to your base type, and therefore increasing its size. -We set the class flags to :const:`Py_TPFLAGS_DEFAULT`. :: +We set the class flags to :c:macro:`Py_TPFLAGS_DEFAULT`. :: .tp_flags = Py_TPFLAGS_DEFAULT, @@ -505,7 +505,7 @@ definitions:: {NULL} /* Sentinel */ }; -(note that we used the :const:`METH_NOARGS` flag to indicate that the method +(note that we used the :c:macro:`METH_NOARGS` flag to indicate that the method is expecting no arguments other than *self*) and assign it to the :c:member:`~PyTypeObject.tp_methods` slot:: @@ -515,7 +515,7 @@ and assign it to the :c:member:`~PyTypeObject.tp_methods` slot:: Finally, we'll make our type usable as a base class for subclassing. We've written our methods carefully so far so that they don't make any assumptions about the type of the object being created or used, so all we need to do is -to add the :const:`Py_TPFLAGS_BASETYPE` to our class flag definition:: +to add the :c:macro:`Py_TPFLAGS_BASETYPE` to our class flag definition:: .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, @@ -781,7 +781,7 @@ and ``Custom_clear``:: Py_TYPE(self)->tp_free((PyObject *) self); } -Finally, we add the :const:`Py_TPFLAGS_HAVE_GC` flag to the class flags:: +Finally, we add the :c:macro:`Py_TPFLAGS_HAVE_GC` flag to the class flags:: .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, diff --git a/Doc/howto/isolating-extensions.rst b/Doc/howto/isolating-extensions.rst index 2eddb582da7c24..522e0853b496a1 100644 --- a/Doc/howto/isolating-extensions.rst +++ b/Doc/howto/isolating-extensions.rst @@ -298,10 +298,10 @@ Watch out for the following two points in particular (but note that this is not a comprehensive list): * Unlike static types, heap type objects are mutable by default. - Use the :c:data:`Py_TPFLAGS_IMMUTABLETYPE` flag to prevent mutability. + Use the :c:macro:`Py_TPFLAGS_IMMUTABLETYPE` flag to prevent mutability. * Heap types inherit :c:member:`~PyTypeObject.tp_new` by default, so it may become possible to instantiate them from Python code. - You can prevent this with the :c:data:`Py_TPFLAGS_DISALLOW_INSTANTIATION` flag. + You can prevent this with the :c:macro:`Py_TPFLAGS_DISALLOW_INSTANTIATION` flag. Defining Heap Types @@ -333,12 +333,12 @@ To avoid memory leaks, instances of heap types must implement the garbage collection protocol. That is, heap types should: -- Have the :c:data:`Py_TPFLAGS_HAVE_GC` flag. +- Have the :c:macro:`Py_TPFLAGS_HAVE_GC` flag. - Define a traverse function using ``Py_tp_traverse``, which visits the type (e.g. using :c:expr:`Py_VISIT(Py_TYPE(self))`). Please refer to the :ref:`the documentation ` of -:c:data:`Py_TPFLAGS_HAVE_GC` and :c:member:`~PyTypeObject.tp_traverse` +:c:macro:`Py_TPFLAGS_HAVE_GC` and :c:member:`~PyTypeObject.tp_traverse` for additional considerations. If your traverse function delegates to the ``tp_traverse`` of its base class @@ -411,7 +411,7 @@ that subclass, which may be defined in different module than yours. pass For a method to get its "defining class", it must use the -:data:`METH_METHOD | METH_FASTCALL | METH_KEYWORDS` +:ref:`METH_METHOD | METH_FASTCALL | METH_KEYWORDS ` :c:type:`calling convention ` and the corresponding :c:type:`PyCMethod` signature:: diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index 7467f5620d425b..f56784e769a3ee 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -747,7 +747,7 @@ iterations of the loop. .. opcode:: MATCH_MAPPING If TOS is an instance of :class:`collections.abc.Mapping` (or, more technically: if - it has the :const:`Py_TPFLAGS_MAPPING` flag set in its + it has the :c:macro:`Py_TPFLAGS_MAPPING` flag set in its :c:member:`~PyTypeObject.tp_flags`), push ``True`` onto the stack. Otherwise, push ``False``. @@ -758,7 +758,7 @@ iterations of the loop. If TOS is an instance of :class:`collections.abc.Sequence` and is *not* an instance of :class:`str`/:class:`bytes`/:class:`bytearray` (or, more technically: if it has - the :const:`Py_TPFLAGS_SEQUENCE` flag set in its :c:member:`~PyTypeObject.tp_flags`), + the :c:macro:`Py_TPFLAGS_SEQUENCE` flag set in its :c:member:`~PyTypeObject.tp_flags`), push ``True`` onto the stack. Otherwise, push ``False``. .. versionadded:: 3.10 diff --git a/Doc/library/os.rst b/Doc/library/os.rst index b4812e25256b6a..dcd4d9e4ae2d08 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -4068,7 +4068,7 @@ written in Python, such as a mail server's external command delivery program. specified. If the value specified is 0, the child's process group ID will be made the same as its process ID. If the value of *setpgroup* is not set, the child will inherit the parent's process group ID. This argument corresponds - to the C library :c:data:`POSIX_SPAWN_SETPGROUP` flag. + to the C library :c:macro:`POSIX_SPAWN_SETPGROUP` flag. If the *resetids* argument is ``True`` it will reset the effective UID and GID of the child to the real UID and GID of the parent process. If the @@ -4076,27 +4076,27 @@ written in Python, such as a mail server's external command delivery program. the parent. In either case, if the set-user-ID and set-group-ID permission bits are enabled on the executable file, their effect will override the setting of the effective UID and GID. This argument corresponds to the C - library :c:data:`POSIX_SPAWN_RESETIDS` flag. + library :c:macro:`POSIX_SPAWN_RESETIDS` flag. If the *setsid* argument is ``True``, it will create a new session ID - for ``posix_spawn``. *setsid* requires :c:data:`POSIX_SPAWN_SETSID` - or :c:data:`POSIX_SPAWN_SETSID_NP` flag. Otherwise, :exc:`NotImplementedError` + for ``posix_spawn``. *setsid* requires :c:macro:`POSIX_SPAWN_SETSID` + or :c:macro:`POSIX_SPAWN_SETSID_NP` flag. Otherwise, :exc:`NotImplementedError` is raised. The *setsigmask* argument will set the signal mask to the signal set specified. If the parameter is not used, then the child inherits the parent's signal mask. This argument corresponds to the C library - :c:data:`POSIX_SPAWN_SETSIGMASK` flag. + :c:macro:`POSIX_SPAWN_SETSIGMASK` flag. The *sigdef* argument will reset the disposition of all signals in the set specified. This argument corresponds to the C library - :c:data:`POSIX_SPAWN_SETSIGDEF` flag. + :c:macro:`POSIX_SPAWN_SETSIGDEF` flag. The *scheduler* argument must be a tuple containing the (optional) scheduler policy and an instance of :class:`sched_param` with the scheduler parameters. A value of ``None`` in the place of the scheduler policy indicates that is not being provided. This argument is a combination of the C library - :c:data:`POSIX_SPAWN_SETSCHEDPARAM` and :c:data:`POSIX_SPAWN_SETSCHEDULER` + :c:macro:`POSIX_SPAWN_SETSCHEDPARAM` and :c:macro:`POSIX_SPAWN_SETSCHEDULER` flags. .. audit-event:: os.posix_spawn path,argv,env os.posix_spawn diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index d6492ac18f9250..fb0daf89357f90 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -1602,7 +1602,7 @@ body of a coroutine function. * a class that inherits from :class:`collections.abc.Sequence` * a Python class that has been registered as :class:`collections.abc.Sequence` - * a builtin class that has its (CPython) :data:`Py_TPFLAGS_SEQUENCE` bit set + * a builtin class that has its (CPython) :c:macro:`Py_TPFLAGS_SEQUENCE` bit set * a class that inherits from any of the above The following standard library classes are sequences: @@ -1621,7 +1621,7 @@ body of a coroutine function. * a class that inherits from :class:`collections.abc.Mapping` * a Python class that has been registered as :class:`collections.abc.Mapping` - * a builtin class that has its (CPython) :data:`Py_TPFLAGS_MAPPING` bit set + * a builtin class that has its (CPython) :c:macro:`Py_TPFLAGS_MAPPING` bit set * a class that inherits from any of the above The standard library classes :class:`dict` and :class:`types.MappingProxyType` diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst index ecc877f5020d50..c42ef78c6b09d3 100644 --- a/Doc/using/cmdline.rst +++ b/Doc/using/cmdline.rst @@ -883,11 +883,11 @@ conflict. * ``default``: use the :ref:`default memory allocators `. * ``malloc``: use the :c:func:`malloc` function of the C library - for all domains (:c:data:`PYMEM_DOMAIN_RAW`, :c:data:`PYMEM_DOMAIN_MEM`, - :c:data:`PYMEM_DOMAIN_OBJ`). + for all domains (:c:macro:`PYMEM_DOMAIN_RAW`, :c:macro:`PYMEM_DOMAIN_MEM`, + :c:macro:`PYMEM_DOMAIN_OBJ`). * ``pymalloc``: use the :ref:`pymalloc allocator ` for - :c:data:`PYMEM_DOMAIN_MEM` and :c:data:`PYMEM_DOMAIN_OBJ` domains and use - the :c:func:`malloc` function for the :c:data:`PYMEM_DOMAIN_RAW` domain. + :c:macro:`PYMEM_DOMAIN_MEM` and :c:macro:`PYMEM_DOMAIN_OBJ` domains and use + the :c:func:`malloc` function for the :c:macro:`PYMEM_DOMAIN_RAW` domain. Install :ref:`debug hooks `: diff --git a/Doc/whatsnew/2.2.rst b/Doc/whatsnew/2.2.rst index 82aff0be1ed3b3..44e9bd8d492bfc 100644 --- a/Doc/whatsnew/2.2.rst +++ b/Doc/whatsnew/2.2.rst @@ -1105,11 +1105,11 @@ code, none of the changes described here will affect you very much. expected, and a set of pointers to :c:expr:`PyObject*` variables that will be filled in with argument values. -* Two new flags :const:`METH_NOARGS` and :const:`METH_O` are available in method +* Two new flags :c:macro:`METH_NOARGS` and :c:macro:`METH_O` are available in method definition tables to simplify implementation of methods with no arguments or a single untyped argument. Calling such methods is more efficient than calling a - corresponding method that uses :const:`METH_VARARGS`. Also, the old - :const:`METH_OLDARGS` style of writing C methods is now officially deprecated. + corresponding method that uses :c:macro:`METH_VARARGS`. Also, the old + :c:macro:`METH_OLDARGS` style of writing C methods is now officially deprecated. * Two new wrapper functions, :c:func:`PyOS_snprintf` and :c:func:`PyOS_vsnprintf` were added to provide cross-platform implementations for the relatively new diff --git a/Doc/whatsnew/2.3.rst b/Doc/whatsnew/2.3.rst index af489d7cb45c2a..8d5603073005b0 100644 --- a/Doc/whatsnew/2.3.rst +++ b/Doc/whatsnew/2.3.rst @@ -1474,7 +1474,7 @@ complete list of changes, or look through the CVS logs for all the details. * On Windows, the :mod:`socket` module now ships with Secure Sockets Layer (SSL) support. -* The value of the C :const:`PYTHON_API_VERSION` macro is now exposed at the +* The value of the C :c:macro:`PYTHON_API_VERSION` macro is now exposed at the Python level as ``sys.api_version``. The current exception can be cleared by calling the new :func:`sys.exc_clear` function. @@ -1899,10 +1899,10 @@ Changes to Python's build process and to the C API include: * The :c:func:`PyArg_NoArgs` macro is now deprecated, and code that uses it should be changed. For Python 2.2 and later, the method definition table can - specify the :const:`METH_NOARGS` flag, signalling that there are no arguments, + specify the :c:macro:`METH_NOARGS` flag, signalling that there are no arguments, and the argument checking can then be removed. If compatibility with pre-2.2 versions of Python is important, the code could use ``PyArg_ParseTuple(args, - "")`` instead, but this will be slower than using :const:`METH_NOARGS`. + "")`` instead, but this will be slower than using :c:macro:`METH_NOARGS`. * :c:func:`PyArg_ParseTuple` accepts new format characters for various sizes of unsigned integers: ``B`` for :c:expr:`unsigned char`, ``H`` for :c:expr:`unsigned @@ -1918,7 +1918,7 @@ Changes to Python's build process and to the C API include: seconds, according to one measurement). * It's now possible to define class and static methods for a C extension type by - setting either the :const:`METH_CLASS` or :const:`METH_STATIC` flags in a + setting either the :c:macro:`METH_CLASS` or :c:macro:`METH_STATIC` flags in a method's :c:type:`PyMethodDef` structure. * Python now includes a copy of the Expat XML parser's source code, removing any diff --git a/Doc/whatsnew/2.4.rst b/Doc/whatsnew/2.4.rst index 98dc83fe935d5e..d68f600ba44f65 100644 --- a/Doc/whatsnew/2.4.rst +++ b/Doc/whatsnew/2.4.rst @@ -1476,7 +1476,7 @@ Some of the changes to Python's build process and to the C API are: :c:func:`PyArg_ParseTupleAndKeywords` but takes a :c:type:`va_list` instead of a number of arguments. (Contributed by Greg Chapman.) -* A new method flag, :const:`METH_COEXISTS`, allows a function defined in slots +* A new method flag, :c:macro:`METH_COEXISTS`, allows a function defined in slots to co-exist with a :c:type:`PyCFunction` having the same name. This can halve the access time for a method such as :meth:`set.__contains__`. (Contributed by Raymond Hettinger.) diff --git a/Doc/whatsnew/2.6.rst b/Doc/whatsnew/2.6.rst index 84bb651e68eed5..72a273fdd8d122 100644 --- a/Doc/whatsnew/2.6.rst +++ b/Doc/whatsnew/2.6.rst @@ -1138,11 +1138,11 @@ indicate that the external caller is done. The *flags* argument to :c:func:`PyObject_GetBuffer` specifies constraints upon the memory returned. Some examples are: - * :const:`PyBUF_WRITABLE` indicates that the memory must be writable. + * :c:macro:`PyBUF_WRITABLE` indicates that the memory must be writable. - * :const:`PyBUF_LOCK` requests a read-only or exclusive lock on the memory. + * :c:macro:`PyBUF_LOCK` requests a read-only or exclusive lock on the memory. - * :const:`PyBUF_C_CONTIGUOUS` and :const:`PyBUF_F_CONTIGUOUS` + * :c:macro:`PyBUF_C_CONTIGUOUS` and :c:macro:`PyBUF_F_CONTIGUOUS` requests a C-contiguous (last dimension varies the fastest) or Fortran-contiguous (first dimension varies the fastest) array layout. diff --git a/Doc/whatsnew/2.7.rst b/Doc/whatsnew/2.7.rst index 1acb09cf925716..3cbab8d567bfac 100644 --- a/Doc/whatsnew/2.7.rst +++ b/Doc/whatsnew/2.7.rst @@ -2230,7 +2230,7 @@ Changes to Python's build process and to the C API include: * When using the :c:type:`PyMemberDef` structure to define attributes of a type, Python will no longer let you try to delete or set a - :const:`T_STRING_INPLACE` attribute. + :c:macro:`T_STRING_INPLACE` attribute. .. rev 79644 diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index ed72af717f6667..b0b570be8947b5 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -2123,11 +2123,11 @@ New Features These functions allow to activate, deactivate and query the state of the garbage collector from C code without having to import the :mod:`gc` module. -* Add a new :c:data:`Py_TPFLAGS_DISALLOW_INSTANTIATION` type flag to disallow +* Add a new :c:macro:`Py_TPFLAGS_DISALLOW_INSTANTIATION` type flag to disallow creating type instances. (Contributed by Victor Stinner in :issue:`43916`.) -* Add a new :c:data:`Py_TPFLAGS_IMMUTABLETYPE` type flag for creating immutable +* Add a new :c:macro:`Py_TPFLAGS_IMMUTABLETYPE` type flag for creating immutable type objects: type attributes cannot be set nor deleted. (Contributed by Victor Stinner and Erlend E. Aasland in :issue:`43908`.) @@ -2186,9 +2186,9 @@ Porting to Python 3.10 been included directly, consider including ``Python.h`` instead. (Contributed by Nicholas Sim in :issue:`35134`.) -* Use the :c:data:`Py_TPFLAGS_IMMUTABLETYPE` type flag to create immutable type - objects. Do not rely on :c:data:`Py_TPFLAGS_HEAPTYPE` to decide if a type - object is mutable or not; check if :c:data:`Py_TPFLAGS_IMMUTABLETYPE` is set +* Use the :c:macro:`Py_TPFLAGS_IMMUTABLETYPE` type flag to create immutable type + objects. Do not rely on :c:macro:`Py_TPFLAGS_HEAPTYPE` to decide if a type + object is mutable or not; check if :c:macro:`Py_TPFLAGS_IMMUTABLETYPE` is set instead. (Contributed by Victor Stinner and Erlend E. Aasland in :issue:`43908`.) diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 4e944484da86e0..b912b94ebe5a44 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -2349,11 +2349,11 @@ Porting to Python 3.11 #endif * The :c:func:`PyType_Ready` function now raises an error if a type is defined - with the :const:`Py_TPFLAGS_HAVE_GC` flag set but has no traverse function + with the :c:macro:`Py_TPFLAGS_HAVE_GC` flag set but has no traverse function (:c:member:`PyTypeObject.tp_traverse`). (Contributed by Victor Stinner in :issue:`44263`.) -* Heap types with the :const:`Py_TPFLAGS_IMMUTABLETYPE` flag can now inherit +* Heap types with the :c:macro:`Py_TPFLAGS_IMMUTABLETYPE` flag can now inherit the :pep:`590` vectorcall protocol. Previously, this was only possible for :ref:`static types `. (Contributed by Erlend E. Aasland in :issue:`43908`) diff --git a/Doc/whatsnew/3.6.rst b/Doc/whatsnew/3.6.rst index e303e6cba966f4..79cceec543dcce 100644 --- a/Doc/whatsnew/3.6.rst +++ b/Doc/whatsnew/3.6.rst @@ -650,8 +650,8 @@ compiled in release mode using ``PYTHONMALLOC=debug``. Effects of debug hooks: * Detect writes before the start of a buffer (buffer underflows) * Detect writes after the end of a buffer (buffer overflows) * Check that the :term:`GIL ` is held when allocator - functions of :c:data:`PYMEM_DOMAIN_OBJ` (ex: :c:func:`PyObject_Malloc`) and - :c:data:`PYMEM_DOMAIN_MEM` (ex: :c:func:`PyMem_Malloc`) domains are called. + functions of :c:macro:`PYMEM_DOMAIN_OBJ` (ex: :c:func:`PyObject_Malloc`) and + :c:macro:`PYMEM_DOMAIN_MEM` (ex: :c:func:`PyMem_Malloc`) domains are called. Checking if the GIL is held is also a new feature of Python 3.6. @@ -1822,7 +1822,7 @@ Optimizations up to 80% faster. (Contributed by Josh Snider in :issue:`26574`). * Allocator functions of the :c:func:`PyMem_Malloc` domain - (:c:data:`PYMEM_DOMAIN_MEM`) now use the :ref:`pymalloc memory allocator + (:c:macro:`PYMEM_DOMAIN_MEM`) now use the :ref:`pymalloc memory allocator ` instead of :c:func:`malloc` function of the C library. The pymalloc allocator is optimized for objects smaller or equal to 512 bytes with a short lifetime, and use :c:func:`malloc` for larger memory blocks. @@ -1874,8 +1874,8 @@ Build and C API Changes (Original patch by Alecsandru Patrascu of Intel in :issue:`26359`.) * The :term:`GIL ` must now be held when allocator - functions of :c:data:`PYMEM_DOMAIN_OBJ` (ex: :c:func:`PyObject_Malloc`) and - :c:data:`PYMEM_DOMAIN_MEM` (ex: :c:func:`PyMem_Malloc`) domains are called. + functions of :c:macro:`PYMEM_DOMAIN_OBJ` (ex: :c:func:`PyObject_Malloc`) and + :c:macro:`PYMEM_DOMAIN_MEM` (ex: :c:func:`PyMem_Malloc`) domains are called. * New :c:func:`Py_FinalizeEx` API which indicates if flushing buffered data failed. diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index e99a9f7634c546..54b2a4f8ed84d3 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -2113,7 +2113,7 @@ Changes in the C API extension types across feature releases, anymore. A :c:type:`PyTypeObject` exported by a third-party extension module is supposed to have all the slots expected in the current Python version, including - :c:member:`~PyTypeObject.tp_finalize` (:const:`Py_TPFLAGS_HAVE_FINALIZE` + :c:member:`~PyTypeObject.tp_finalize` (:c:macro:`Py_TPFLAGS_HAVE_FINALIZE` is not checked anymore before reading :c:member:`~PyTypeObject.tp_finalize`). (Contributed by Antoine Pitrou in :issue:`32388`.) diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index fd86db96302356..49c8bd2f3e07d5 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -1276,7 +1276,7 @@ New Features * :pep:`573`: Added :c:func:`PyType_FromModuleAndSpec` to associate a module with a class; :c:func:`PyType_GetModule` and :c:func:`PyType_GetModuleState` to retrieve the module and its state; and - :c:data:`PyCMethod` and :c:data:`METH_METHOD` to allow a method to + :c:data:`PyCMethod` and :c:macro:`METH_METHOD` to allow a method to access the class it was defined in. (Contributed by Marcel Plch and Petr Viktorin in :issue:`38787`.) diff --git a/Misc/NEWS.d/3.10.0a3.rst b/Misc/NEWS.d/3.10.0a3.rst index 699a0dd9e8d7c4..70b79f5f250a18 100644 --- a/Misc/NEWS.d/3.10.0a3.rst +++ b/Misc/NEWS.d/3.10.0a3.rst @@ -1466,7 +1466,7 @@ success. Patch by Victor Stinner. .. nonce: S3FWTP .. section: C API -The :c:data:`METH_FASTCALL` calling convention is added to the limited API. +The :c:macro:`METH_FASTCALL` calling convention is added to the limited API. The functions :c:func:`PyModule_AddType`, :c:func:`PyType_FromModuleAndSpec`, :c:func:`PyType_GetModule` and :c:func:`PyType_GetModuleState` are added to the limited API on Windows. diff --git a/Misc/NEWS.d/3.10.0b1.rst b/Misc/NEWS.d/3.10.0b1.rst index 2a3d358edde902..cf89a550d38546 100644 --- a/Misc/NEWS.d/3.10.0b1.rst +++ b/Misc/NEWS.d/3.10.0b1.rst @@ -1375,8 +1375,8 @@ Add "Annotations Best Practices" document as a new HOWTO. .. nonce: K5aSl1 .. section: Documentation -Document the new :const:`Py_TPFLAGS_MAPPING` and -:const:`Py_TPFLAGS_SEQUENCE` type flags. +Document the new :c:macro:`Py_TPFLAGS_MAPPING` and +:c:macro:`Py_TPFLAGS_SEQUENCE` type flags. .. @@ -1711,7 +1711,7 @@ IDLE's shell now shows prompts in a separate side-bar. .. nonce: wvWt23 .. section: C API -Add a new :c:data:`Py_TPFLAGS_DISALLOW_INSTANTIATION` type flag to disallow +Add a new :c:macro:`Py_TPFLAGS_DISALLOW_INSTANTIATION` type flag to disallow creating type instances. Patch by Victor Stinner. .. @@ -1759,7 +1759,7 @@ module. .. nonce: Co3YhZ .. section: C API -Introduce :const:`Py_TPFLAGS_IMMUTABLETYPE` flag for immutable type objects, +Introduce :c:macro:`Py_TPFLAGS_IMMUTABLETYPE` flag for immutable type objects, and modify :c:func:`PyType_Ready` to set it for static types. Patch by Erlend E. Aasland. diff --git a/Misc/NEWS.d/3.11.0a1.rst b/Misc/NEWS.d/3.11.0a1.rst index 33841d9e4e39bb..e5827e2f23b1f4 100644 --- a/Misc/NEWS.d/3.11.0a1.rst +++ b/Misc/NEWS.d/3.11.0a1.rst @@ -888,7 +888,7 @@ zlib.decompress on input data that expands that large. .. nonce: YHuV_s .. section: Core and Builtins -Heap types with the :const:`Py_TPFLAGS_IMMUTABLETYPE` flag can now inherit +Heap types with the :c:macro:`Py_TPFLAGS_IMMUTABLETYPE` flag can now inherit the :pep:`590` vectorcall protocol. Previously, this was only possible for :ref:`static types `. Patch by Erlend E. Aasland. @@ -2575,7 +2575,7 @@ E. Aasland. .. nonce: bamAGF .. section: Library -Set the proper :const:`Py_TPFLAGS_MAPPING` and :const:`Py_TPFLAGS_SEQUENCE` +Set the proper :c:macro:`Py_TPFLAGS_MAPPING` and :c:macro:`Py_TPFLAGS_SEQUENCE` flags for subclasses created before a parent has been registered as a :class:`collections.abc.Mapping` or :class:`collections.abc.Sequence`. @@ -2693,7 +2693,7 @@ libgcc_s.so file (ex: EMFILE error). Patch by Victor Stinner. .. section: Library The _thread.RLock type now fully implement the GC protocol: add a traverse -function and the :const:`Py_TPFLAGS_HAVE_GC` flag. Patch by Victor Stinner. +function and the :c:macro:`Py_TPFLAGS_HAVE_GC` flag. Patch by Victor Stinner. .. @@ -5014,7 +5014,7 @@ must now be used to set an object type and size. Patch by Victor Stinner. .. section: C API The :c:func:`PyType_Ready` function now raises an error if a type is defined -with the :const:`Py_TPFLAGS_HAVE_GC` flag set but has no traverse function +with the :c:macro:`Py_TPFLAGS_HAVE_GC` flag set but has no traverse function (:c:member:`PyTypeObject.tp_traverse`). Patch by Victor Stinner. .. diff --git a/Misc/NEWS.d/3.11.0a7.rst b/Misc/NEWS.d/3.11.0a7.rst index 8e7ccd4d6771ee..2172c02e2fc847 100644 --- a/Misc/NEWS.d/3.11.0a7.rst +++ b/Misc/NEWS.d/3.11.0a7.rst @@ -275,7 +275,7 @@ initializing to ``list_extend``. Patch by Jeremiah Pascual. .. nonce: cnaIK3 .. section: Core and Builtins -Speed up throwing exception in generator with :const:`METH_FASTCALL` calling +Speed up throwing exception in generator with :c:macro:`METH_FASTCALL` calling convention. Patch by Kumar Aditya. .. diff --git a/Misc/NEWS.d/3.6.0a1.rst b/Misc/NEWS.d/3.6.0a1.rst index 53f09b3dfe3363..98f1215fb91873 100644 --- a/Misc/NEWS.d/3.6.0a1.rst +++ b/Misc/NEWS.d/3.6.0a1.rst @@ -125,7 +125,7 @@ Setuptools 19.0. .. section: Core and Builtins Memory functions of the :c:func:`PyMem_Malloc` domain -(:c:data:`PYMEM_DOMAIN_MEM`) now use the :ref:`pymalloc allocator +(:c:macro:`PYMEM_DOMAIN_MEM`) now use the :ref:`pymalloc allocator ` rather than system :c:func:`malloc`. Applications calling :c:func:`PyMem_Malloc` without holding the GIL can now crash: use ``PYTHONMALLOC=debug`` environment variable to validate the usage of memory diff --git a/Misc/NEWS.d/3.9.0a1.rst b/Misc/NEWS.d/3.9.0a1.rst index 2525696c58a4d1..5a929d481a3d55 100644 --- a/Misc/NEWS.d/3.9.0a1.rst +++ b/Misc/NEWS.d/3.9.0a1.rst @@ -5706,7 +5706,7 @@ and :c:func:`_PyObject_CallMethodOneArg`. .. nonce: qZC0N_ .. section: C API -The :const:`METH_FASTCALL` calling convention has been documented. +The :c:macro:`METH_FASTCALL` calling convention has been documented. .. From cc76113cf823ebff76d346665bbf1218a40ed4c8 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 21 Jul 2023 14:49:21 +0300 Subject: [PATCH 026/632] [3.11] gh-106892: Use roles :data: and :const: for referencing module variables (GH-106894) (GH-106955) (cherry picked from commit d036db728ea3d54509cbad06df74e2d9a31fbec8) --- Doc/c-api/import.rst | 6 ++--- Doc/library/__main__.rst | 4 ++-- Doc/library/asyncio-subprocess.rst | 4 ++-- Doc/library/compileall.rst | 4 ++-- Doc/library/devmode.rst | 2 +- Doc/library/filecmp.rst | 2 +- Doc/library/ftplib.rst | 2 +- Doc/library/functions.rst | 2 +- Doc/library/gc.rst | 2 +- Doc/library/gzip.rst | 2 +- Doc/library/importlib.resources.abc.rst | 2 +- Doc/library/importlib.rst | 18 +++++++------- Doc/library/json.rst | 6 ++--- Doc/library/logging.handlers.rst | 4 ++-- Doc/library/os.path.rst | 2 +- Doc/library/os.rst | 2 +- Doc/library/platform.rst | 2 +- Doc/library/shutil.rst | 2 +- Doc/library/stdtypes.rst | 2 +- Doc/library/subprocess.rst | 4 ++-- Doc/library/sys.rst | 10 ++++---- Doc/library/tarfile.rst | 10 ++++---- Doc/library/test.rst | 2 +- Doc/library/tkinter.rst | 2 +- Doc/library/unittest.rst | 4 ++-- Doc/reference/datamodel.rst | 4 ++-- Doc/using/windows.rst | 2 +- Doc/whatsnew/2.5.rst | 8 +++---- Doc/whatsnew/3.1.rst | 4 ++-- Doc/whatsnew/3.11.rst | 2 +- Doc/whatsnew/3.2.rst | 32 ++++++++++++------------- Doc/whatsnew/3.3.rst | 30 +++++++++++------------ Doc/whatsnew/3.4.rst | 14 +++++------ Doc/whatsnew/3.6.rst | 4 ++-- Doc/whatsnew/3.8.rst | 2 +- Doc/whatsnew/3.9.rst | 8 +++---- Misc/NEWS.d/3.8.0a4.rst | 2 +- Misc/NEWS.d/3.9.0a5.rst | 4 ++-- 38 files changed, 109 insertions(+), 109 deletions(-) diff --git a/Doc/c-api/import.rst b/Doc/c-api/import.rst index 57328fc16198c5..4ca1517efcb38d 100644 --- a/Doc/c-api/import.rst +++ b/Doc/c-api/import.rst @@ -126,9 +126,9 @@ Importing Modules read from a Python bytecode file or obtained from the built-in function :func:`compile`, load the module. Return a new reference to the module object, or ``NULL`` with an exception set if an error occurred. *name* - is removed from :attr:`sys.modules` in error cases, even if *name* was already - in :attr:`sys.modules` on entry to :c:func:`PyImport_ExecCodeModule`. Leaving - incompletely initialized modules in :attr:`sys.modules` is dangerous, as imports of + is removed from :data:`sys.modules` in error cases, even if *name* was already + in :data:`sys.modules` on entry to :c:func:`PyImport_ExecCodeModule`. Leaving + incompletely initialized modules in :data:`sys.modules` is dangerous, as imports of such modules have no way to know that the module object is an unknown (and probably damaged with respect to the module author's intents) state. diff --git a/Doc/library/__main__.rst b/Doc/library/__main__.rst index 363596782d8e3e..90973d0058e883 100644 --- a/Doc/library/__main__.rst +++ b/Doc/library/__main__.rst @@ -336,12 +336,12 @@ Note that importing ``__main__`` doesn't cause any issues with unintentionally running top-level code meant for script use which is put in the ``if __name__ == "__main__"`` block of the ``start`` module. Why does this work? -Python inserts an empty ``__main__`` module in :attr:`sys.modules` at +Python inserts an empty ``__main__`` module in :data:`sys.modules` at interpreter startup, and populates it by running top-level code. In our example this is the ``start`` module which runs line by line and imports ``namely``. In turn, ``namely`` imports ``__main__`` (which is really ``start``). That's an import cycle! Fortunately, since the partially populated ``__main__`` -module is present in :attr:`sys.modules`, Python passes that to ``namely``. +module is present in :data:`sys.modules`, Python passes that to ``namely``. See :ref:`Special considerations for __main__ ` in the import system's reference for details on how this works. diff --git a/Doc/library/asyncio-subprocess.rst b/Doc/library/asyncio-subprocess.rst index 4274638c5e8625..6e04817b1818d9 100644 --- a/Doc/library/asyncio-subprocess.rst +++ b/Doc/library/asyncio-subprocess.rst @@ -68,7 +68,7 @@ Creating Subprocesses The *limit* argument sets the buffer limit for :class:`StreamReader` wrappers for :attr:`Process.stdout` and :attr:`Process.stderr` - (if :attr:`subprocess.PIPE` is passed to *stdout* and *stderr* arguments). + (if :const:`subprocess.PIPE` is passed to *stdout* and *stderr* arguments). Return a :class:`~asyncio.subprocess.Process` instance. @@ -86,7 +86,7 @@ Creating Subprocesses The *limit* argument sets the buffer limit for :class:`StreamReader` wrappers for :attr:`Process.stdout` and :attr:`Process.stderr` - (if :attr:`subprocess.PIPE` is passed to *stdout* and *stderr* arguments). + (if :const:`subprocess.PIPE` is passed to *stdout* and *stderr* arguments). Return a :class:`~asyncio.subprocess.Process` instance. diff --git a/Doc/library/compileall.rst b/Doc/library/compileall.rst index 180f5b81c2b615..4226348a17240a 100644 --- a/Doc/library/compileall.rst +++ b/Doc/library/compileall.rst @@ -141,9 +141,9 @@ There is no command-line option to control the optimization level used by the :func:`compile` function, because the Python interpreter itself already provides the option: :program:`python -O -m compileall`. -Similarly, the :func:`compile` function respects the :attr:`sys.pycache_prefix` +Similarly, the :func:`compile` function respects the :data:`sys.pycache_prefix` setting. The generated bytecode cache will only be useful if :func:`compile` is -run with the same :attr:`sys.pycache_prefix` (if any) that will be used at +run with the same :data:`sys.pycache_prefix` (if any) that will be used at runtime. Public functions diff --git a/Doc/library/devmode.rst b/Doc/library/devmode.rst index 90138dd2a75f99..bc66815d57f34d 100644 --- a/Doc/library/devmode.rst +++ b/Doc/library/devmode.rst @@ -81,7 +81,7 @@ Effects of the Python Development Mode: ignored for empty strings. * The :class:`io.IOBase` destructor logs ``close()`` exceptions. -* Set the :attr:`~sys.flags.dev_mode` attribute of :attr:`sys.flags` to +* Set the :attr:`~sys.flags.dev_mode` attribute of :data:`sys.flags` to ``True``. The Python Development Mode does not enable the :mod:`tracemalloc` module by diff --git a/Doc/library/filecmp.rst b/Doc/library/filecmp.rst index 83e9e14ddcacd8..0efb4897a1eb86 100644 --- a/Doc/library/filecmp.rst +++ b/Doc/library/filecmp.rst @@ -74,7 +74,7 @@ The :class:`dircmp` class Construct a new directory comparison object, to compare the directories *a* and *b*. *ignore* is a list of names to ignore, and defaults to - :attr:`filecmp.DEFAULT_IGNORES`. *hide* is a list of names to hide, and + :const:`filecmp.DEFAULT_IGNORES`. *hide* is a list of names to hide, and defaults to ``[os.curdir, os.pardir]``. The :class:`dircmp` class compares files by doing *shallow* comparisons diff --git a/Doc/library/ftplib.rst b/Doc/library/ftplib.rst index e5ba9eef4074b9..517ea4796c0a4e 100644 --- a/Doc/library/ftplib.rst +++ b/Doc/library/ftplib.rst @@ -438,7 +438,7 @@ FTP_TLS Objects .. attribute:: FTP_TLS.ssl_version - The SSL version to use (defaults to :attr:`ssl.PROTOCOL_SSLv23`). + The SSL version to use (defaults to :data:`ssl.PROTOCOL_SSLv23`). .. method:: FTP_TLS.auth() diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 3d7e6603a7f35c..e00d079ce43875 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -1230,7 +1230,7 @@ are always available. They are listed here in alphabetical order. * Binary files are buffered in fixed-size chunks; the size of the buffer is chosen using a heuristic trying to determine the underlying device's "block - size" and falling back on :attr:`io.DEFAULT_BUFFER_SIZE`. On many systems, + size" and falling back on :const:`io.DEFAULT_BUFFER_SIZE`. On many systems, the buffer will typically be 4096 or 8192 bytes long. * "Interactive" text files (files for which :meth:`~io.IOBase.isatty` diff --git a/Doc/library/gc.rst b/Doc/library/gc.rst index 5c2d0bbe0035a1..0ecd93813c9193 100644 --- a/Doc/library/gc.rst +++ b/Doc/library/gc.rst @@ -260,7 +260,7 @@ values but should not rebind them): .. versionchanged:: 3.4 Following :pep:`442`, objects with a :meth:`__del__` method don't end - up in :attr:`gc.garbage` anymore. + up in :data:`gc.garbage` anymore. .. data:: callbacks diff --git a/Doc/library/gzip.rst b/Doc/library/gzip.rst index 9afaf86cb13281..2f26295d057f86 100644 --- a/Doc/library/gzip.rst +++ b/Doc/library/gzip.rst @@ -264,7 +264,7 @@ Command line options .. cmdoption:: file - If *file* is not specified, read from :attr:`sys.stdin`. + If *file* is not specified, read from :data:`sys.stdin`. .. cmdoption:: --fast diff --git a/Doc/library/importlib.resources.abc.rst b/Doc/library/importlib.resources.abc.rst index 4a563c8a5390d1..b91e37510fe113 100644 --- a/Doc/library/importlib.resources.abc.rst +++ b/Doc/library/importlib.resources.abc.rst @@ -124,7 +124,7 @@ suitable for reading (same as :attr:`pathlib.Path.open`). When opening as text, accepts encoding parameters such as those - accepted by :attr:`io.TextIOWrapper`. + accepted by :class:`io.TextIOWrapper`. .. method:: read_bytes() diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index 55668dacfe49fd..386a47b05f93da 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -466,7 +466,7 @@ ABC hierarchy:: The list of locations where the package's submodules will be found. Most of the time this is a single directory. The import system passes this attribute to ``__import__()`` and to finders - in the same way as :attr:`sys.path` but just for the package. + in the same way as :data:`sys.path` but just for the package. It is not set on non-package modules so it can be used as an indicator that the module is a package. @@ -717,7 +717,7 @@ ABC hierarchy:: automatically. When writing to the path fails because the path is read-only - (:attr:`errno.EACCES`/:exc:`PermissionError`), do not propagate the + (:const:`errno.EACCES`/:exc:`PermissionError`), do not propagate the exception. .. versionchanged:: 3.4 @@ -965,7 +965,7 @@ find and load modules. .. classmethod:: path_hook(*loader_details) - A class method which returns a closure for use on :attr:`sys.path_hooks`. + A class method which returns a closure for use on :data:`sys.path_hooks`. An instance of :class:`FileFinder` is returned by the closure using the path argument given to the closure directly and *loader_details* indirectly. @@ -1306,10 +1306,10 @@ an :term:`importer`. .. function:: find_spec(name, package=None) Find the :term:`spec ` for a module, optionally relative to - the specified **package** name. If the module is in :attr:`sys.modules`, + the specified **package** name. If the module is in :data:`sys.modules`, then ``sys.modules[name].__spec__`` is returned (unless the spec would be ``None`` or is not set, in which case :exc:`ValueError` is raised). - Otherwise a search using :attr:`sys.meta_path` is done. ``None`` is + Otherwise a search using :data:`sys.meta_path` is done. ``None`` is returned if no spec is found. If **name** is for a submodule (contains a dot), the parent module is @@ -1442,7 +1442,7 @@ an :term:`importer`. :meth:`~importlib.abc.Loader.create_module` method must return ``None`` or a type for which its ``__class__`` attribute can be mutated along with not using :term:`slots <__slots__>`. Finally, modules which substitute the object - placed into :attr:`sys.modules` will not work as there is no way to properly + placed into :data:`sys.modules` will not work as there is no way to properly replace the module references throughout the interpreter safely; :exc:`ValueError` is raised if such a substitution is detected. @@ -1566,9 +1566,9 @@ For deep customizations of import, you typically want to implement an :term:`importer`. This means managing both the :term:`finder` and :term:`loader` side of things. For finders there are two flavours to choose from depending on your needs: a :term:`meta path finder` or a :term:`path entry finder`. The -former is what you would put on :attr:`sys.meta_path` while the latter is what -you create using a :term:`path entry hook` on :attr:`sys.path_hooks` which works -with :attr:`sys.path` entries to potentially create a finder. This example will +former is what you would put on :data:`sys.meta_path` while the latter is what +you create using a :term:`path entry hook` on :data:`sys.path_hooks` which works +with :data:`sys.path` entries to potentially create a finder. This example will show you how to register your own importers so that import will use them (for creating an importer for yourself, read the documentation for the appropriate classes defined within this package):: diff --git a/Doc/library/json.rst b/Doc/library/json.rst index 5383614575c213..35a08995487c1b 100644 --- a/Doc/library/json.rst +++ b/Doc/library/json.rst @@ -683,7 +683,7 @@ The :mod:`json.tool` module provides a simple command line interface to validate and pretty-print JSON objects. If the optional ``infile`` and ``outfile`` arguments are not -specified, :attr:`sys.stdin` and :attr:`sys.stdout` will be used respectively: +specified, :data:`sys.stdin` and :data:`sys.stdout` will be used respectively: .. code-block:: shell-session @@ -721,12 +721,12 @@ Command line options } ] - If *infile* is not specified, read from :attr:`sys.stdin`. + If *infile* is not specified, read from :data:`sys.stdin`. .. cmdoption:: outfile Write the output of the *infile* to the given *outfile*. Otherwise, write it - to :attr:`sys.stdout`. + to :data:`sys.stdout`. .. cmdoption:: --sort-keys diff --git a/Doc/library/logging.handlers.rst b/Doc/library/logging.handlers.rst index b6455298278743..7f638251602c72 100644 --- a/Doc/library/logging.handlers.rst +++ b/Doc/library/logging.handlers.rst @@ -1051,8 +1051,8 @@ possible, while any potentially slow operations (such as sending an email via occur (e.g. because a bounded queue has filled up), the :meth:`~logging.Handler.handleError` method is called to handle the error. This can result in the record silently being dropped (if - :attr:`logging.raiseExceptions` is ``False``) or a message printed to - ``sys.stderr`` (if :attr:`logging.raiseExceptions` is ``True``). + :data:`logging.raiseExceptions` is ``False``) or a message printed to + ``sys.stderr`` (if :data:`logging.raiseExceptions` is ``True``). .. method:: prepare(record) diff --git a/Doc/library/os.path.rst b/Doc/library/os.path.rst index 1a6b121de186a1..8f6dffd04fa54a 100644 --- a/Doc/library/os.path.rst +++ b/Doc/library/os.path.rst @@ -383,7 +383,7 @@ the :mod:`glob` module.) *start*. On Windows, :exc:`ValueError` is raised when *path* and *start* are on different drives. - *start* defaults to :attr:`os.curdir`. + *start* defaults to :data:`os.curdir`. .. availability:: Unix, Windows. diff --git a/Doc/library/os.rst b/Doc/library/os.rst index dcd4d9e4ae2d08..31946f1a12b9da 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -60,7 +60,7 @@ Notes on the availability of these functions: ``'java'``. .. seealso:: - :attr:`sys.platform` has a finer granularity. :func:`os.uname` gives + :data:`sys.platform` has a finer granularity. :func:`os.uname` gives system-dependent version information. The :mod:`platform` module provides detailed checks for the diff --git a/Doc/library/platform.rst b/Doc/library/platform.rst index dc2d871b47d5ef..60cd6b880847ef 100644 --- a/Doc/library/platform.rst +++ b/Doc/library/platform.rst @@ -46,7 +46,7 @@ Cross Platform universal files containing multiple architectures. To get at the "64-bitness" of the current interpreter, it is more - reliable to query the :attr:`sys.maxsize` attribute:: + reliable to query the :data:`sys.maxsize` attribute:: is_64bits = sys.maxsize > 2**32 diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst index ef3fd26518010a..26a5cd531fa14f 100644 --- a/Doc/library/shutil.rst +++ b/Doc/library/shutil.rst @@ -425,7 +425,7 @@ Directory and files operations determining if the file exists and executable. When no *path* is specified, the results of :func:`os.environ` are used, - returning either the "PATH" value or a fallback of :attr:`os.defpath`. + returning either the "PATH" value or a fallback of :data:`os.defpath`. On Windows, the current directory is always prepended to the *path* whether or not you use the default or provide your own, which is the behavior the diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index f47ed686136ffc..4437bc496ab9b9 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -5591,7 +5591,7 @@ From code, you can inspect the current limit and set a new one using these a getter and setter for the interpreter-wide limit. Subinterpreters have their own limit. -Information about the default and minimum can be found in :attr:`sys.int_info`: +Information about the default and minimum can be found in :data:`sys.int_info`: * :data:`sys.int_info.default_max_str_digits ` is the compiled-in default limit. diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst index 4c7fb276311469..78718f7983e041 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -1609,7 +1609,7 @@ improves performance. If you ever encounter a presumed highly unusual situation where you need to prevent ``vfork()`` from being used by Python, you can set the -:attr:`subprocess._USE_VFORK` attribute to a false value. +:const:`subprocess._USE_VFORK` attribute to a false value. :: @@ -1617,7 +1617,7 @@ prevent ``vfork()`` from being used by Python, you can set the Setting this has no impact on use of ``posix_spawn()`` which could use ``vfork()`` internally within its libc implementation. There is a similar -:attr:`subprocess._USE_POSIX_SPAWN` attribute if you need to prevent use of +:const:`subprocess._USE_POSIX_SPAWN` attribute if you need to prevent use of that. :: diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 32713b2a8879d1..643cdb876ee3c4 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -166,7 +166,7 @@ always available. Python interpreter. (This information is not available in any other way --- ``modules.keys()`` only lists the imported modules.) - See also the :attr:`sys.stdlib_module_names` list. + See also the :data:`sys.stdlib_module_names` list. .. function:: call_tracing(func, args) @@ -1261,20 +1261,20 @@ always available. ================ =========================== .. versionchanged:: 3.3 - On Linux, :attr:`sys.platform` doesn't contain the major version anymore. + On Linux, :data:`sys.platform` doesn't contain the major version anymore. It is always ``'linux'``, instead of ``'linux2'`` or ``'linux3'``. Since older Python versions include the version number, it is recommended to always use the ``startswith`` idiom presented above. .. versionchanged:: 3.8 - On AIX, :attr:`sys.platform` doesn't contain the major version anymore. + On AIX, :data:`sys.platform` doesn't contain the major version anymore. It is always ``'aix'``, instead of ``'aix5'`` or ``'aix7'``. Since older Python versions include the version number, it is recommended to always use the ``startswith`` idiom presented above. .. seealso:: - :attr:`os.name` has a coarser granularity. :func:`os.uname` gives + :data:`os.name` has a coarser granularity. :func:`os.uname` gives system-dependent version information. The :mod:`platform` module provides detailed checks for the @@ -1685,7 +1685,7 @@ always available. ``email.mime`` sub-package and the ``email.message`` sub-module are not listed. - See also the :attr:`sys.builtin_module_names` list. + See also the :data:`sys.builtin_module_names` list. .. versionadded:: 3.10 diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst index 41428740ba3b25..b7b089a73e6483 100644 --- a/Doc/library/tarfile.rst +++ b/Doc/library/tarfile.rst @@ -931,7 +931,7 @@ reused in custom filters: Implements the ``'tar'`` filter. - - Strip leading slashes (``/`` and :attr:`os.sep`) from filenames. + - Strip leading slashes (``/`` and :data:`os.sep`) from filenames. - :ref:`Refuse ` to extract files with absolute paths (in case the name is absolute even after stripping slashes, e.g. ``C:/foo`` on Windows). @@ -940,7 +940,7 @@ reused in custom filters: path (after following symlinks) would end up outside the destination. This raises :class:`~tarfile.OutsideDestinationError`. - Clear high mode bits (setuid, setgid, sticky) and group/other write bits - (:attr:`~stat.S_IWGRP`|:attr:`~stat.S_IWOTH`). + (:const:`~stat.S_IWGRP`|:const:`~stat.S_IWOTH`). Return the modified ``TarInfo`` member. @@ -965,10 +965,10 @@ reused in custom filters: - For regular files, including hard links: - Set the owner read and write permissions - (:attr:`~stat.S_IRUSR`|:attr:`~stat.S_IWUSR`). + (:const:`~stat.S_IRUSR`|:const:`~stat.S_IWUSR`). - Remove the group & other executable permission - (:attr:`~stat.S_IXGRP`|:attr:`~stat.S_IXOTH`) - if the owner doesn’t have it (:attr:`~stat.S_IXUSR`). + (:const:`~stat.S_IXGRP`|:const:`~stat.S_IXOTH`) + if the owner doesn’t have it (:const:`~stat.S_IXUSR`). - For other files (directories), set ``mode`` to ``None``, so that extraction methods skip applying permission bits. diff --git a/Doc/library/test.rst b/Doc/library/test.rst index 0bddd31ae259d6..73d1178906a8cd 100644 --- a/Doc/library/test.rst +++ b/Doc/library/test.rst @@ -973,7 +973,7 @@ The :mod:`test.support` module defines the following classes: `SetErrorMode `_. On UNIX, :func:`resource.setrlimit` is used to set - :attr:`resource.RLIMIT_CORE`'s soft limit to 0 to prevent coredump file + :const:`resource.RLIMIT_CORE`'s soft limit to 0 to prevent coredump file creation. On both platforms, the old value is restored by :meth:`__exit__`. diff --git a/Doc/library/tkinter.rst b/Doc/library/tkinter.rst index c8e4317be75879..2aa34422703872 100644 --- a/Doc/library/tkinter.rst +++ b/Doc/library/tkinter.rst @@ -163,7 +163,7 @@ the modern themed widget set and API:: interpreter and calls :func:`exec` on the contents of :file:`.{className}.py` and :file:`.{baseName}.py`. The path for the profile files is the :envvar:`HOME` environment variable or, if that - isn't defined, then :attr:`os.curdir`. + isn't defined, then :data:`os.curdir`. .. attribute:: tk diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index c2ed7acaf5f0dd..2c6316d66eadfc 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -1128,7 +1128,7 @@ Test cases If given, *level* should be either a numeric logging level or its string equivalent (for example either ``"ERROR"`` or - :attr:`logging.ERROR`). The default is :attr:`logging.INFO`. + :const:`logging.ERROR`). The default is :const:`logging.INFO`. The test passes if at least one message emitted inside the ``with`` block matches the *logger* and *level* conditions, otherwise it fails. @@ -1169,7 +1169,7 @@ Test cases If given, *level* should be either a numeric logging level or its string equivalent (for example either ``"ERROR"`` or - :attr:`logging.ERROR`). The default is :attr:`logging.INFO`. + :const:`logging.ERROR`). The default is :const:`logging.INFO`. Unlike :meth:`assertLogs`, nothing will be returned by the context manager. diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 112d5f8e8a84df..d2a39c57adfc54 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2482,8 +2482,8 @@ through the object's keys; for sequences, it should iterate through the values. .. impl-detail:: - In CPython, the length is required to be at most :attr:`sys.maxsize`. - If the length is larger than :attr:`!sys.maxsize` some features (such as + In CPython, the length is required to be at most :data:`sys.maxsize`. + If the length is larger than :data:`!sys.maxsize` some features (such as :func:`len`) may raise :exc:`OverflowError`. To prevent raising :exc:`!OverflowError` by truth value testing, an object must define a :meth:`__bool__` method. diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst index f51a5247b6392f..2a07e42f8443ce 100644 --- a/Doc/using/windows.rst +++ b/Doc/using/windows.rst @@ -1188,7 +1188,7 @@ non-standard paths in the registry and user site-packages. Modules specified in the registry under ``Modules`` (not ``PythonPath``) may be imported by :class:`importlib.machinery.WindowsRegistryFinder`. This finder is enabled on Windows in 3.6.0 and earlier, but may need to - be explicitly added to :attr:`sys.meta_path` in the future. + be explicitly added to :data:`sys.meta_path` in the future. Additional modules ================== diff --git a/Doc/whatsnew/2.5.rst b/Doc/whatsnew/2.5.rst index dcfaef6ed29494..b410fe1ce3f084 100644 --- a/Doc/whatsnew/2.5.rst +++ b/Doc/whatsnew/2.5.rst @@ -1448,10 +1448,10 @@ complete list of changes, or look through the SVN logs for all the details. return times that are precise to fractions of a second; not all systems support such precision.) - Constants named :attr:`os.SEEK_SET`, :attr:`os.SEEK_CUR`, and - :attr:`os.SEEK_END` have been added; these are the parameters to the + Constants named :const:`os.SEEK_SET`, :const:`os.SEEK_CUR`, and + :const:`os.SEEK_END` have been added; these are the parameters to the :func:`os.lseek` function. Two new constants for locking are - :attr:`os.O_SHLOCK` and :attr:`os.O_EXLOCK`. + :const:`os.O_SHLOCK` and :const:`os.O_EXLOCK`. Two new functions, :func:`wait3` and :func:`wait4`, were added. They're similar the :func:`waitpid` function which waits for a child process to exit and returns @@ -1602,7 +1602,7 @@ complete list of changes, or look through the SVN logs for all the details. * The :mod:`unicodedata` module has been updated to use version 4.1.0 of the Unicode character database. Version 3.2.0 is required by some specifications, - so it's still available as :attr:`unicodedata.ucd_3_2_0`. + so it's still available as :data:`unicodedata.ucd_3_2_0`. * New module: the :mod:`uuid` module generates universally unique identifiers (UUIDs) according to :rfc:`4122`. The RFC defines several different UUID diff --git a/Doc/whatsnew/3.1.rst b/Doc/whatsnew/3.1.rst index fba8816bb243a3..e4365d44928b5b 100644 --- a/Doc/whatsnew/3.1.rst +++ b/Doc/whatsnew/3.1.rst @@ -370,7 +370,7 @@ New, Improved, and Deprecated Modules * The :mod:`io` module has three new constants for the :meth:`seek` method :data:`SEEK_SET`, :data:`SEEK_CUR`, and :data:`SEEK_END`. -* The :attr:`sys.version_info` tuple is now a named tuple:: +* The :data:`sys.version_info` tuple is now a named tuple:: >>> sys.version_info sys.version_info(major=3, minor=1, micro=0, releaselevel='alpha', serial=2) @@ -486,7 +486,7 @@ Changes to Python's build process and to the C API include: Apart from the performance improvements this change should be invisible to end users, with one exception: for testing and debugging purposes there's a - new :attr:`sys.int_info` that provides information about the + new :data:`sys.int_info` that provides information about the internal format, giving the number of bits per digit and the size in bytes of the C type used to store each digit:: diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index b912b94ebe5a44..973ce154bbdade 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -642,7 +642,7 @@ dataclasses datetime -------- -* Add :attr:`datetime.UTC`, a convenience alias for +* Add :const:`datetime.UTC`, a convenience alias for :attr:`datetime.timezone.utc`. (Contributed by Kabir Kwatra in :gh:`91973`.) * :meth:`datetime.date.fromisoformat`, :meth:`datetime.time.fromisoformat` and diff --git a/Doc/whatsnew/3.2.rst b/Doc/whatsnew/3.2.rst index 8e2f9e2716c4fb..2e4c8ee45df84b 100644 --- a/Doc/whatsnew/3.2.rst +++ b/Doc/whatsnew/3.2.rst @@ -424,7 +424,7 @@ protocols, the users must to be able access the environment using native strings even though the underlying platform may have a different convention. To bridge this gap, the :mod:`wsgiref` module has a new function, :func:`wsgiref.handlers.read_environ` for transcoding CGI variables from -:attr:`os.environ` into native strings and returning a new dictionary. +:data:`os.environ` into native strings and returning a new dictionary. .. seealso:: @@ -483,7 +483,7 @@ Some smaller changes made to the core Python language are: * The interpreter can now be started with a quiet option, ``-q``, to prevent the copyright and version information from being displayed in the interactive - mode. The option can be introspected using the :attr:`sys.flags` attribute: + mode. The option can be introspected using the :data:`sys.flags` attribute: .. code-block:: shell-session @@ -566,7 +566,7 @@ Some smaller changes made to the core Python language are: * The internal :c:type:`structsequence` tool now creates subclasses of tuple. This means that C structures like those returned by :func:`os.stat`, - :func:`time.gmtime`, and :attr:`sys.version_info` now work like a + :func:`time.gmtime`, and :data:`sys.version_info` now work like a :term:`named tuple` and now work with functions and methods that expect a tuple as an argument. This is a big step forward in making the C structures as flexible as their pure Python counterparts: @@ -596,7 +596,7 @@ Some smaller changes made to the core Python language are: module, or on the command line. A :exc:`ResourceWarning` is issued at interpreter shutdown if the - :data:`gc.garbage` list isn't empty, and if :attr:`gc.DEBUG_UNCOLLECTABLE` is + :data:`gc.garbage` list isn't empty, and if :const:`gc.DEBUG_UNCOLLECTABLE` is set, all uncollectable objects are printed. This is meant to make the programmer aware that their code contains object finalization issues. @@ -621,7 +621,7 @@ Some smaller changes made to the core Python language are: :class:`collections.Sequence` :term:`abstract base class`. As a result, the language will have a more uniform API. In addition, :class:`range` objects now support slicing and negative indices, even with values larger than - :attr:`sys.maxsize`. This makes *range* more interoperable with lists:: + :data:`sys.maxsize`. This makes *range* more interoperable with lists:: >>> range(0, 100, 2).count(10) 1 @@ -1005,13 +1005,13 @@ datetime and time after 1900. The new supported year range is from 1000 to 9999 inclusive. * Whenever a two-digit year is used in a time tuple, the interpretation has been - governed by :attr:`time.accept2dyear`. The default is ``True`` which means that + governed by :data:`time.accept2dyear`. The default is ``True`` which means that for a two-digit year, the century is guessed according to the POSIX rules governing the ``%y`` strptime format. Starting with Py3.2, use of the century guessing heuristic will emit a :exc:`DeprecationWarning`. Instead, it is recommended that - :attr:`time.accept2dyear` be set to ``False`` so that large date ranges + :data:`time.accept2dyear` be set to ``False`` so that large date ranges can be used without guesswork:: >>> import time, warnings @@ -1029,7 +1029,7 @@ datetime and time 'Fri Jan 1 12:34:56 11' Several functions now have significantly expanded date ranges. When - :attr:`time.accept2dyear` is false, the :func:`time.asctime` function will + :data:`time.accept2dyear` is false, the :func:`time.asctime` function will accept any year that fits in a C int, while the :func:`time.mktime` and :func:`time.strftime` functions will accept the full range supported by the corresponding operating system functions. @@ -1192,11 +1192,11 @@ can be set to "$" for the shell-style formatting provided by If no configuration is set-up before a logging event occurs, there is now a default configuration using a :class:`~logging.StreamHandler` directed to -:attr:`sys.stderr` for events of ``WARNING`` level or higher. Formerly, an +:data:`sys.stderr` for events of ``WARNING`` level or higher. Formerly, an event occurring before a configuration was set-up would either raise an exception or silently drop the event depending on the value of -:attr:`logging.raiseExceptions`. The new default handler is stored in -:attr:`logging.lastResort`. +:data:`logging.raiseExceptions`. The new default handler is stored in +:data:`logging.lastResort`. The use of filters has been simplified. Instead of creating a :class:`~logging.Filter` object, the predicate can be any Python callable that @@ -1298,7 +1298,7 @@ values are equal (:issue:`8188`):: hash(Decimal("1.5")) == hash(complex(1.5, 0)) Some of the hashing details are exposed through a new attribute, -:attr:`sys.hash_info`, which describes the bit width of the hash value, the +:data:`sys.hash_info`, which describes the bit width of the hash value, the prime modulus, the hash values for *infinity* and *nan*, and the multiplier used for the imaginary part of a number: @@ -1386,7 +1386,7 @@ select ------ The :mod:`select` module now exposes a new, constant attribute, -:attr:`~select.PIPE_BUF`, which gives the minimum number of bytes which are +:const:`~select.PIPE_BUF`, which gives the minimum number of bytes which are guaranteed not to block when :func:`select.select` says a pipe is ready for writing. @@ -1527,7 +1527,7 @@ filenames: b'Sehensw\xc3\xbcrdigkeiten' Some operating systems allow direct access to encoded bytes in the -environment. If so, the :attr:`os.supports_bytes_environ` constant will be +environment. If so, the :const:`os.supports_bytes_environ` constant will be true. For direct access to encoded environment variables (if available), @@ -2300,7 +2300,7 @@ turtledemo The demonstration code for the :mod:`turtle` module was moved from the *Demo* directory to main library. It includes over a dozen sample scripts with -lively displays. Being on :attr:`sys.path`, it can now be run directly +lively displays. Being on :data:`sys.path`, it can now be run directly from the command-line: .. code-block:: shell-session @@ -2564,7 +2564,7 @@ Changes to Python's build process and to the C API include: (:issue:`2443`). * A new C API function :c:func:`PySys_SetArgvEx` allows an embedded interpreter - to set :attr:`sys.argv` without also modifying :attr:`sys.path` + to set :data:`sys.argv` without also modifying :data:`sys.path` (:issue:`5753`). * :c:macro:`PyEval_CallObject` is now only available in macro form. The diff --git a/Doc/whatsnew/3.3.rst b/Doc/whatsnew/3.3.rst index 74cfa8385c4c91..9bbf52c5551782 100644 --- a/Doc/whatsnew/3.3.rst +++ b/Doc/whatsnew/3.3.rst @@ -647,7 +647,7 @@ PEP 421: Adding sys.implementation A new attribute on the :mod:`sys` module exposes details specific to the implementation of the currently running interpreter. The initial set of -attributes on :attr:`sys.implementation` are ``name``, ``version``, +attributes on :data:`sys.implementation` are ``name``, ``version``, ``hexversion``, and ``cache_tag``. The intention of ``sys.implementation`` is to consolidate into one namespace @@ -718,7 +718,7 @@ and does not enforce any method requirements. In terms of finders, :class:`importlib.machinery.FileFinder` exposes the mechanism used to search for source and bytecode files of a module. Previously -this class was an implicit member of :attr:`sys.path_hooks`. +this class was an implicit member of :data:`sys.path_hooks`. For loaders, the new abstract base class :class:`importlib.abc.FileLoader` helps write a loader that uses the file system as the storage mechanism for a module's @@ -734,7 +734,7 @@ provide the full name of the module now instead of just the tail end of the module's name. The :func:`importlib.invalidate_caches` function will now call the method with -the same name on all finders cached in :attr:`sys.path_importer_cache` to help +the same name on all finders cached in :data:`sys.path_importer_cache` to help clean up any stored state as necessary. Visible Changes @@ -744,8 +744,8 @@ For potential required changes to code, see the `Porting Python code`_ section. Beyond the expanse of what :mod:`importlib` now exposes, there are other -visible changes to import. The biggest is that :attr:`sys.meta_path` and -:attr:`sys.path_hooks` now store all of the meta path finders and path entry +visible changes to import. The biggest is that :data:`sys.meta_path` and +:data:`sys.path_hooks` now store all of the meta path finders and path entry hooks used by import. Previously the finders were implicit and hidden within the C code of import instead of being directly exposed. This means that one can now easily remove or change the order of the various finders to fit one's needs. @@ -760,9 +760,9 @@ Loaders are also now expected to set the ``__package__`` attribute from :pep:`366`. Once again, import itself is already setting this on all loaders from :mod:`importlib` and import itself is setting the attribute post-load. -``None`` is now inserted into :attr:`sys.path_importer_cache` when no finder -can be found on :attr:`sys.path_hooks`. Since :class:`imp.NullImporter` is not -directly exposed on :attr:`sys.path_hooks` it could no longer be relied upon to +``None`` is now inserted into :data:`sys.path_importer_cache` when no finder +can be found on :data:`sys.path_hooks`. Since :class:`imp.NullImporter` is not +directly exposed on :data:`sys.path_hooks` it could no longer be relied upon to always be available to use as a value representing no finder found. All other changes relate to semantic changes which should be taken into @@ -1951,7 +1951,7 @@ ssl * You can query the SSL compression algorithm used by an SSL socket, thanks to its new :meth:`~ssl.SSLSocket.compression` method. The new attribute - :attr:`~ssl.OP_NO_COMPRESSION` can be used to disable compression. + :const:`~ssl.OP_NO_COMPRESSION` can be used to disable compression. (Contributed by Antoine Pitrou in :issue:`13634`.) * Support has been added for the Next Protocol Negotiation extension using @@ -1965,7 +1965,7 @@ ssl * The :func:`~ssl.get_server_certificate` function now supports IPv6. (Contributed by Charles-François Natali in :issue:`11811`.) -* New attribute :attr:`~ssl.OP_CIPHER_SERVER_PREFERENCE` allows setting +* New attribute :const:`~ssl.OP_CIPHER_SERVER_PREFERENCE` allows setting SSLv3 server sockets to use the server's cipher ordering preference rather than the client's (:issue:`13635`). @@ -2140,7 +2140,7 @@ New attribute :attr:`zlib.Decompress.eof` makes it possible to distinguish between a properly formed compressed stream and an incomplete or truncated one. (Contributed by Nadeem Vawda in :issue:`12646`.) -New attribute :attr:`zlib.ZLIB_RUNTIME_VERSION` reports the version string of +New attribute :const:`zlib.ZLIB_RUNTIME_VERSION` reports the version string of the underlying ``zlib`` library that is loaded at runtime. (Contributed by Torsten Landschoff in :issue:`12306`.) @@ -2377,16 +2377,16 @@ Porting Python code * :func:`__import__` no longer allows one to use an index value other than 0 for top-level modules. E.g. ``__import__('sys', level=1)`` is now an error. -* Because :attr:`sys.meta_path` and :attr:`sys.path_hooks` now have finders on +* Because :data:`sys.meta_path` and :data:`sys.path_hooks` now have finders on them by default, you will most likely want to use :meth:`list.insert` instead of :meth:`list.append` to add to those lists. -* Because ``None`` is now inserted into :attr:`sys.path_importer_cache`, if you +* Because ``None`` is now inserted into :data:`sys.path_importer_cache`, if you are clearing out entries in the dictionary of paths that do not have a finder, you will need to remove keys paired with values of ``None`` **and** :class:`imp.NullImporter` to be backwards-compatible. This will lead to extra overhead on older versions of Python that re-insert ``None`` into - :attr:`sys.path_importer_cache` where it represents the use of implicit + :data:`sys.path_importer_cache` where it represents the use of implicit finders, but semantically it should not change anything. * :class:`importlib.abc.Finder` no longer specifies a ``find_module()`` abstract @@ -2444,7 +2444,7 @@ Porting Python code error instead of sleeping forever. It has always raised an error on posix. * The ``ast.__version__`` constant has been removed. If you need to - make decisions affected by the AST version, use :attr:`sys.version_info` + make decisions affected by the AST version, use :data:`sys.version_info` to make the decision. * Code that used to work around the fact that the :mod:`threading` module used diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst index 9f894b40e2396a..7436a552f529e5 100644 --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -1323,14 +1323,14 @@ ability to query or set the resource limits for processes other than the one making the call. (Contributed by Christian Heimes in :issue:`16595`.) On Linux kernel version 2.6.36 or later, there are also some new -Linux specific constants: :attr:`~resource.RLIMIT_MSGQUEUE`, -:attr:`~resource.RLIMIT_NICE`, :attr:`~resource.RLIMIT_RTPRIO`, -:attr:`~resource.RLIMIT_RTTIME`, and :attr:`~resource.RLIMIT_SIGPENDING`. +Linux specific constants: :const:`~resource.RLIMIT_MSGQUEUE`, +:const:`~resource.RLIMIT_NICE`, :const:`~resource.RLIMIT_RTPRIO`, +:const:`~resource.RLIMIT_RTTIME`, and :const:`~resource.RLIMIT_SIGPENDING`. (Contributed by Christian Heimes in :issue:`19324`.) On FreeBSD version 9 and later, there some new FreeBSD specific constants: -:attr:`~resource.RLIMIT_SBSIZE`, :attr:`~resource.RLIMIT_SWAP`, and -:attr:`~resource.RLIMIT_NPTS`. (Contributed by Claudiu Popa in +:const:`~resource.RLIMIT_SBSIZE`, :const:`~resource.RLIMIT_SWAP`, and +:const:`~resource.RLIMIT_NPTS`. (Contributed by Claudiu Popa in :issue:`19343`.) @@ -1500,7 +1500,7 @@ implementation is required as most of the values aren't standardized and are platform-dependent. (Contributed by Christian Heimes in :issue:`11016`.) The module supports new :mod:`~stat.ST_MODE` flags, :mod:`~stat.S_IFDOOR`, -:attr:`~stat.S_IFPORT`, and :attr:`~stat.S_IFWHT`. (Contributed by +:const:`~stat.S_IFPORT`, and :const:`~stat.S_IFWHT`. (Contributed by Christian Hiemes in :issue:`11016`.) @@ -1849,7 +1849,7 @@ Python's default implementation to a SipHash implementation on platforms that have a 64 bit data type. Any performance differences in comparison with the older FNV algorithm are trivial. -The PEP adds additional fields to the :attr:`sys.hash_info` named tuple to +The PEP adds additional fields to the :data:`sys.hash_info` named tuple to describe the hash algorithm in use by the currently executing binary. Otherwise, the PEP does not alter any existing CPython APIs. diff --git a/Doc/whatsnew/3.6.rst b/Doc/whatsnew/3.6.rst index 79cceec543dcce..20c43e0e04a3cf 100644 --- a/Doc/whatsnew/3.6.rst +++ b/Doc/whatsnew/3.6.rst @@ -1388,7 +1388,7 @@ are treated as punctuation. site ---- -When specifying paths to add to :attr:`sys.path` in a ``.pth`` file, +When specifying paths to add to :data:`sys.path` in a ``.pth`` file, you may now specify file paths on top of directories (e.g. zip files). (Contributed by Wolfgang Langner in :issue:`26587`). @@ -2010,7 +2010,7 @@ been deprecated in previous versions of Python in favour of :meth:`importlib.abc.Loader.exec_module`. The :class:`importlib.machinery.WindowsRegistryFinder` class is now -deprecated. As of 3.6.0, it is still added to :attr:`sys.meta_path` by +deprecated. As of 3.6.0, it is still added to :data:`sys.meta_path` by default (on Windows), but this may change in future releases. os diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index 54b2a4f8ed84d3..90220dfb0c054c 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -1836,7 +1836,7 @@ Changes in Python behavior classes will affect their string representation. (Contributed by Serhiy Storchaka in :issue:`36793`.) -* On AIX, :attr:`sys.platform` doesn't contain the major version anymore. +* On AIX, :data:`sys.platform` doesn't contain the major version anymore. It is always ``'aix'``, instead of ``'aix3'`` .. ``'aix7'``. Since older Python versions include the version number, so it is recommended to always use ``sys.platform.startswith('aix')``. diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index 49c8bd2f3e07d5..c7466c13a40486 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -692,13 +692,13 @@ which has nanosecond resolution, rather than sys --- -Added a new :attr:`sys.platlibdir` attribute: name of the platform-specific +Added a new :data:`sys.platlibdir` attribute: name of the platform-specific library directory. It is used to build the path of standard library and the paths of installed extension modules. It is equal to ``"lib"`` on most platforms. On Fedora and SuSE, it is equal to ``"lib64"`` on 64-bit platforms. (Contributed by Jan Matějek, Matěj Cepl, Charalampos Stratakis and Victor Stinner in :issue:`1294959`.) -Previously, :attr:`sys.stderr` was block-buffered when non-interactive. Now +Previously, :data:`sys.stderr` was block-buffered when non-interactive. Now ``stderr`` defaults to always being line-buffered. (Contributed by Jendrik Seipp in :issue:`13601`.) @@ -1226,8 +1226,8 @@ Build Changes ============= * Added ``--with-platlibdir`` option to the ``configure`` script: name of the - platform-specific library directory, stored in the new :attr:`sys.platlibdir` - attribute. See :attr:`sys.platlibdir` attribute for more information. + platform-specific library directory, stored in the new :data:`sys.platlibdir` + attribute. See :data:`sys.platlibdir` attribute for more information. (Contributed by Jan Matějek, Matěj Cepl, Charalampos Stratakis and Victor Stinner in :issue:`1294959`.) diff --git a/Misc/NEWS.d/3.8.0a4.rst b/Misc/NEWS.d/3.8.0a4.rst index 5250d82f65e7bc..d6fde41b7b11cd 100644 --- a/Misc/NEWS.d/3.8.0a4.rst +++ b/Misc/NEWS.d/3.8.0a4.rst @@ -92,7 +92,7 @@ the field. .. nonce: wejLoC .. section: Core and Builtins -On AIX, :attr:`sys.platform` doesn't contain the major version anymore. +On AIX, :data:`sys.platform` doesn't contain the major version anymore. Always return ``'aix'``, instead of ``'aix3'`` .. ``'aix7'``. Since older Python versions include the version number, it is recommended to always use ``sys.platform.startswith('aix')``. Contributed by M. Felt. diff --git a/Misc/NEWS.d/3.9.0a5.rst b/Misc/NEWS.d/3.9.0a5.rst index 49a118ad7e4308..a129e721a4cf90 100644 --- a/Misc/NEWS.d/3.9.0a5.rst +++ b/Misc/NEWS.d/3.9.0a5.rst @@ -582,7 +582,7 @@ Fix :mod:`json.tool` to catch :exc:`BrokenPipeError`. Patch by Dong-hee Na. Avoid a possible *"RuntimeError: dictionary changed size during iteration"* from :func:`inspect.getmodule` when it tried to loop through -:attr:`sys.modules`. +:data:`sys.modules`. .. @@ -989,7 +989,7 @@ modules are built. Add ``--with-platlibdir`` option to the configure script: name of the platform-specific library directory, stored in the new -:attr:`sys.platlibdir` attribute. It is used to build the path of +:data:`sys.platlibdir` attribute. It is used to build the path of platform-specific extension modules and the path of the standard library. It is equal to ``"lib"`` on most platforms. On Fedora and SuSE, it is equal to ``"lib64"`` on 64-bit platforms. Patch by Jan Matějek, Matěj Cepl, From b338ac75405be26b1df6c35e1360a23f0e88f591 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 21 Jul 2023 14:49:57 +0300 Subject: [PATCH 027/632] [3.11] gh-106909: Use role :const: for referencing module constants (GH-106910) (GH-106957) (cherry picked from commit 4b9948617f91175783609769aa6160e5b49b9ccc) --- Doc/c-api/exceptions.rst | 2 +- Doc/faq/library.rst | 2 +- Doc/install/index.rst | 4 +- Doc/library/_thread.rst | 4 +- Doc/library/asyncio-dev.rst | 4 +- Doc/library/asyncio-eventloop.rst | 36 ++++++------- Doc/library/asyncio-platforms.rst | 2 +- Doc/library/asyncio-subprocess.rst | 2 +- Doc/library/exceptions.rst | 34 ++++++------ Doc/library/fcntl.rst | 8 +-- Doc/library/ftplib.rst | 4 +- Doc/library/http.client.rst | 2 +- Doc/library/imaplib.rst | 4 +- Doc/library/io.rst | 2 +- Doc/library/multiprocessing.rst | 2 +- Doc/library/optparse.rst | 8 +-- Doc/library/os.rst | 84 +++++++++++++++--------------- Doc/library/poplib.rst | 4 +- Doc/library/shelve.rst | 8 +-- Doc/library/smtplib.rst | 4 +- Doc/library/socket.rst | 2 +- Doc/library/sqlite3.rst | 6 +-- Doc/library/ssl.rst | 8 +-- Doc/library/sys.rst | 4 +- Doc/library/tempfile.rst | 4 +- Doc/library/test.rst | 2 +- Doc/library/unittest.mock.rst | 2 +- Doc/library/urllib.request.rst | 2 +- Doc/library/xml.rst | 2 +- Doc/using/configure.rst | 4 +- Doc/whatsnew/2.7.rst | 6 +-- Doc/whatsnew/3.10.rst | 16 +++--- Doc/whatsnew/3.11.rst | 8 +-- Doc/whatsnew/3.2.rst | 6 +-- Doc/whatsnew/3.3.rst | 16 +++--- Doc/whatsnew/3.4.rst | 18 +++---- Doc/whatsnew/3.5.rst | 10 ++-- Doc/whatsnew/3.6.rst | 4 +- Doc/whatsnew/3.7.rst | 18 +++---- Doc/whatsnew/3.8.rst | 2 +- Doc/whatsnew/3.9.rst | 14 ++--- Misc/NEWS.d/3.10.0a1.rst | 4 +- Misc/NEWS.d/3.10.0a2.rst | 2 +- Misc/NEWS.d/3.10.0a6.rst | 6 +-- Misc/NEWS.d/3.10.0a7.rst | 2 +- Misc/NEWS.d/3.10.0b1.rst | 2 +- Misc/NEWS.d/3.11.0a1.rst | 8 +-- Misc/NEWS.d/3.11.0a4.rst | 2 +- Misc/NEWS.d/3.11.0b1.rst | 4 +- Misc/NEWS.d/3.6.0rc1.rst | 4 +- Misc/NEWS.d/3.7.0a1.rst | 6 +-- Misc/NEWS.d/3.7.0a3.rst | 4 +- Misc/NEWS.d/3.8.0a1.rst | 2 +- Misc/NEWS.d/3.8.0a4.rst | 2 +- Misc/NEWS.d/3.9.0a1.rst | 14 ++--- Misc/NEWS.d/3.9.0a6.rst | 4 +- 56 files changed, 220 insertions(+), 220 deletions(-) diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst index 76d32fd1763f57..5dad596f6197fc 100644 --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -602,7 +602,7 @@ Signal Handling to interrupt an operation). If the given signal isn't handled by Python (it was set to - :data:`signal.SIG_DFL` or :data:`signal.SIG_IGN`), it will be ignored. + :py:const:`signal.SIG_DFL` or :py:const:`signal.SIG_IGN`), it will be ignored. If *signum* is outside of the allowed range of signal numbers, ``-1`` is returned. Otherwise, ``0`` is returned. The error indicator is diff --git a/Doc/faq/library.rst b/Doc/faq/library.rst index 597caaa778e1c8..b43c7505c0401c 100644 --- a/Doc/faq/library.rst +++ b/Doc/faq/library.rst @@ -566,7 +566,7 @@ use ``p.read(n)``. Note on a bug in popen2: unless your program calls ``wait()`` or ``waitpid()``, finished child processes are never removed, and eventually calls to popen2 will fail because of a limit on the number of child - processes. Calling :func:`os.waitpid` with the :data:`os.WNOHANG` option can + processes. Calling :func:`os.waitpid` with the :const:`os.WNOHANG` option can prevent this; a good place to insert such a call would be before calling ``popen2`` again. diff --git a/Doc/install/index.rst b/Doc/install/index.rst index b9588df611c597..6c401c4c13d1ca 100644 --- a/Doc/install/index.rst +++ b/Doc/install/index.rst @@ -309,9 +309,9 @@ install into it. It is enabled with a simple option:: python setup.py install --user -Files will be installed into subdirectories of :data:`site.USER_BASE` (written +Files will be installed into subdirectories of :const:`site.USER_BASE` (written as :file:`{userbase}` hereafter). This scheme installs pure Python modules and -extension modules in the same location (also known as :data:`site.USER_SITE`). +extension modules in the same location (also known as :const:`site.USER_SITE`). Here are the values for UNIX, including macOS: =============== =========================================================== diff --git a/Doc/library/_thread.rst b/Doc/library/_thread.rst index 3d7e354d1e368d..21f7847d679af2 100644 --- a/Doc/library/_thread.rst +++ b/Doc/library/_thread.rst @@ -68,10 +68,10 @@ This module defines the following constants and functions: there is no guarantee that the interruption will happen immediately. If given, *signum* is the number of the signal to simulate. - If *signum* is not given, :data:`signal.SIGINT` is simulated. + If *signum* is not given, :const:`signal.SIGINT` is simulated. If the given signal isn't handled by Python (it was set to - :data:`signal.SIG_DFL` or :data:`signal.SIG_IGN`), this function does + :const:`signal.SIG_DFL` or :const:`signal.SIG_IGN`), this function does nothing. .. versionchanged:: 3.10 diff --git a/Doc/library/asyncio-dev.rst b/Doc/library/asyncio-dev.rst index 921a394a59fec7..c7d97008fb490e 100644 --- a/Doc/library/asyncio-dev.rst +++ b/Doc/library/asyncio-dev.rst @@ -34,7 +34,7 @@ There are several ways to enable asyncio debug mode: In addition to enabling the debug mode, consider also: * setting the log level of the :ref:`asyncio logger ` to - :py:data:`logging.DEBUG`, for example the following snippet of code + :py:const:`logging.DEBUG`, for example the following snippet of code can be run at startup of the application:: logging.basicConfig(level=logging.DEBUG) @@ -142,7 +142,7 @@ Logging asyncio uses the :mod:`logging` module and all logging is performed via the ``"asyncio"`` logger. -The default log level is :py:data:`logging.INFO`, which can be easily +The default log level is :py:const:`logging.INFO`, which can be easily adjusted:: logging.getLogger("asyncio").setLevel(logging.WARNING) diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index 83fb27060b214d..07be0d0eea0888 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -397,11 +397,11 @@ Opening network connections Open a streaming transport connection to a given address specified by *host* and *port*. - The socket family can be either :py:data:`~socket.AF_INET` or - :py:data:`~socket.AF_INET6` depending on *host* (or the *family* + The socket family can be either :py:const:`~socket.AF_INET` or + :py:const:`~socket.AF_INET6` depending on *host* (or the *family* argument, if provided). - The socket type will be :py:data:`~socket.SOCK_STREAM`. + The socket type will be :py:const:`~socket.SOCK_STREAM`. *protocol_factory* must be a callable returning an :ref:`asyncio protocol ` implementation. @@ -495,7 +495,7 @@ Opening network connections .. versionchanged:: 3.6 - The socket option :py:data:`~socket.TCP_NODELAY` is set by default + The socket option :py:const:`~socket.TCP_NODELAY` is set by default for all TCP connections. .. versionchanged:: 3.7 @@ -535,11 +535,11 @@ Opening network connections Create a datagram connection. - The socket family can be either :py:data:`~socket.AF_INET`, - :py:data:`~socket.AF_INET6`, or :py:data:`~socket.AF_UNIX`, + The socket family can be either :py:const:`~socket.AF_INET`, + :py:const:`~socket.AF_INET6`, or :py:const:`~socket.AF_UNIX`, depending on *host* (or the *family* argument, if provided). - The socket type will be :py:data:`~socket.SOCK_DGRAM`. + The socket type will be :py:const:`~socket.SOCK_DGRAM`. *protocol_factory* must be a callable returning a :ref:`protocol ` implementation. @@ -564,7 +564,7 @@ Opening network connections * *reuse_port* tells the kernel to allow this endpoint to be bound to the same port as other existing endpoints are bound to, so long as they all set this flag when being created. This option is not supported on Windows - and some Unixes. If the :py:data:`~socket.SO_REUSEPORT` constant is not + and some Unixes. If the :py:const:`~socket.SO_REUSEPORT` constant is not defined then this capability is unsupported. * *allow_broadcast* tells the kernel to allow this endpoint to send @@ -590,7 +590,7 @@ Opening network connections .. versionchanged:: 3.8.1 The *reuse_address* parameter is no longer supported, as using - :py:data:`~sockets.SO_REUSEADDR` poses a significant security concern for + :py:const:`~sockets.SO_REUSEADDR` poses a significant security concern for UDP. Explicitly passing ``reuse_address=True`` will raise an exception. When multiple processes with differing UIDs assign sockets to an @@ -599,7 +599,7 @@ Opening network connections For supported platforms, *reuse_port* can be used as a replacement for similar functionality. With *reuse_port*, - :py:data:`~sockets.SO_REUSEPORT` is used instead, which specifically + :py:const:`~sockets.SO_REUSEPORT` is used instead, which specifically prevents processes with differing UIDs from assigning sockets to the same socket address. @@ -617,8 +617,8 @@ Opening network connections Create a Unix connection. - The socket family will be :py:data:`~socket.AF_UNIX`; socket - type will be :py:data:`~socket.SOCK_STREAM`. + The socket family will be :py:const:`~socket.AF_UNIX`; socket + type will be :py:const:`~socket.SOCK_STREAM`. A tuple of ``(transport, protocol)`` is returned on success. @@ -654,7 +654,7 @@ Creating network servers ssl_shutdown_timeout=None, \ start_serving=True) - Create a TCP server (socket type :data:`~socket.SOCK_STREAM`) listening + Create a TCP server (socket type :const:`~socket.SOCK_STREAM`) listening on *port* of the *host* address. Returns a :class:`Server` object. @@ -682,10 +682,10 @@ Creating network servers be selected (note that if *host* resolves to multiple network interfaces, a different random port will be selected for each interface). - * *family* can be set to either :data:`socket.AF_INET` or - :data:`~socket.AF_INET6` to force the socket to use IPv4 or IPv6. + * *family* can be set to either :const:`socket.AF_INET` or + :const:`~socket.AF_INET6` to force the socket to use IPv4 or IPv6. If not set, the *family* will be determined from host name - (defaults to :data:`~socket.AF_UNSPEC`). + (defaults to :const:`~socket.AF_UNSPEC`). * *flags* is a bitmask for :meth:`getaddrinfo`. @@ -739,7 +739,7 @@ Creating network servers .. versionchanged:: 3.6 Added *ssl_handshake_timeout* and *start_serving* parameters. - The socket option :py:data:`~socket.TCP_NODELAY` is set by default + The socket option :py:const:`~socket.TCP_NODELAY` is set by default for all TCP connections. .. versionchanged:: 3.11 @@ -760,7 +760,7 @@ Creating network servers start_serving=True) Similar to :meth:`loop.create_server` but works with the - :py:data:`~socket.AF_UNIX` socket family. + :py:const:`~socket.AF_UNIX` socket family. *path* is the name of a Unix domain socket, and is required, unless a *sock* argument is provided. Abstract Unix sockets, diff --git a/Doc/library/asyncio-platforms.rst b/Doc/library/asyncio-platforms.rst index 50ad8a2ab70324..19ec726c1be060 100644 --- a/Doc/library/asyncio-platforms.rst +++ b/Doc/library/asyncio-platforms.rst @@ -37,7 +37,7 @@ All event loops on Windows do not support the following methods: * :meth:`loop.create_unix_connection` and :meth:`loop.create_unix_server` are not supported. - The :data:`socket.AF_UNIX` socket family is specific to Unix. + The :const:`socket.AF_UNIX` socket family is specific to Unix. * :meth:`loop.add_signal_handler` and :meth:`loop.remove_signal_handler` are not supported. diff --git a/Doc/library/asyncio-subprocess.rst b/Doc/library/asyncio-subprocess.rst index 6e04817b1818d9..77c3aae43099c3 100644 --- a/Doc/library/asyncio-subprocess.rst +++ b/Doc/library/asyncio-subprocess.rst @@ -244,7 +244,7 @@ their completion. Stop the child process. - On POSIX systems this method sends :py:data:`signal.SIGTERM` to the + On POSIX systems this method sends :py:const:`signal.SIGTERM` to the child process. On Windows the Win32 API function :c:func:`TerminateProcess` is diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst index d54c49fff6b62a..dcfbc486eeb358 100644 --- a/Doc/library/exceptions.rst +++ b/Doc/library/exceptions.rst @@ -659,8 +659,8 @@ depending on the system error code. Raised when an operation would block on an object (e.g. socket) set for non-blocking operation. - Corresponds to :c:data:`errno` :py:data:`~errno.EAGAIN`, :py:data:`~errno.EALREADY`, - :py:data:`~errno.EWOULDBLOCK` and :py:data:`~errno.EINPROGRESS`. + Corresponds to :c:data:`errno` :py:const:`~errno.EAGAIN`, :py:const:`~errno.EALREADY`, + :py:const:`~errno.EWOULDBLOCK` and :py:const:`~errno.EINPROGRESS`. In addition to those of :exc:`OSError`, :exc:`BlockingIOError` can have one more attribute: @@ -674,7 +674,7 @@ depending on the system error code. .. exception:: ChildProcessError Raised when an operation on a child process failed. - Corresponds to :c:data:`errno` :py:data:`~errno.ECHILD`. + Corresponds to :c:data:`errno` :py:const:`~errno.ECHILD`. .. exception:: ConnectionError @@ -688,40 +688,40 @@ depending on the system error code. A subclass of :exc:`ConnectionError`, raised when trying to write on a pipe while the other end has been closed, or trying to write on a socket which has been shutdown for writing. - Corresponds to :c:data:`errno` :py:data:`~errno.EPIPE` and :py:data:`~errno.ESHUTDOWN`. + Corresponds to :c:data:`errno` :py:const:`~errno.EPIPE` and :py:const:`~errno.ESHUTDOWN`. .. exception:: ConnectionAbortedError A subclass of :exc:`ConnectionError`, raised when a connection attempt is aborted by the peer. - Corresponds to :c:data:`errno` :py:data:`~errno.ECONNABORTED`. + Corresponds to :c:data:`errno` :py:const:`~errno.ECONNABORTED`. .. exception:: ConnectionRefusedError A subclass of :exc:`ConnectionError`, raised when a connection attempt is refused by the peer. - Corresponds to :c:data:`errno` :py:data:`~errno.ECONNREFUSED`. + Corresponds to :c:data:`errno` :py:const:`~errno.ECONNREFUSED`. .. exception:: ConnectionResetError A subclass of :exc:`ConnectionError`, raised when a connection is reset by the peer. - Corresponds to :c:data:`errno` :py:data:`~errno.ECONNRESET`. + Corresponds to :c:data:`errno` :py:const:`~errno.ECONNRESET`. .. exception:: FileExistsError Raised when trying to create a file or directory which already exists. - Corresponds to :c:data:`errno` :py:data:`~errno.EEXIST`. + Corresponds to :c:data:`errno` :py:const:`~errno.EEXIST`. .. exception:: FileNotFoundError Raised when a file or directory is requested but doesn't exist. - Corresponds to :c:data:`errno` :py:data:`~errno.ENOENT`. + Corresponds to :c:data:`errno` :py:const:`~errno.ENOENT`. .. exception:: InterruptedError Raised when a system call is interrupted by an incoming signal. - Corresponds to :c:data:`errno` :py:data:`~errno.EINTR`. + Corresponds to :c:data:`errno` :py:const:`~errno.EINTR`. .. versionchanged:: 3.5 Python now retries system calls when a syscall is interrupted by a @@ -732,7 +732,7 @@ depending on the system error code. Raised when a file operation (such as :func:`os.remove`) is requested on a directory. - Corresponds to :c:data:`errno` :py:data:`~errno.EISDIR`. + Corresponds to :c:data:`errno` :py:const:`~errno.EISDIR`. .. exception:: NotADirectoryError @@ -740,28 +740,28 @@ depending on the system error code. something which is not a directory. On most POSIX platforms, it may also be raised if an operation attempts to open or traverse a non-directory file as if it were a directory. - Corresponds to :c:data:`errno` :py:data:`~errno.ENOTDIR`. + Corresponds to :c:data:`errno` :py:const:`~errno.ENOTDIR`. .. exception:: PermissionError Raised when trying to run an operation without the adequate access rights - for example filesystem permissions. - Corresponds to :c:data:`errno` :py:data:`~errno.EACCES`, - :py:data:`~errno.EPERM`, and :py:data:`~errno.ENOTCAPABLE`. + Corresponds to :c:data:`errno` :py:const:`~errno.EACCES`, + :py:const:`~errno.EPERM`, and :py:const:`~errno.ENOTCAPABLE`. .. versionchanged:: 3.11.1 - WASI's :py:data:`~errno.ENOTCAPABLE` is now mapped to + WASI's :py:const:`~errno.ENOTCAPABLE` is now mapped to :exc:`PermissionError`. .. exception:: ProcessLookupError Raised when a given process doesn't exist. - Corresponds to :c:data:`errno` :py:data:`~errno.ESRCH`. + Corresponds to :c:data:`errno` :py:const:`~errno.ESRCH`. .. exception:: TimeoutError Raised when a system function timed out at the system level. - Corresponds to :c:data:`errno` :py:data:`~errno.ETIMEDOUT`. + Corresponds to :c:data:`errno` :py:const:`~errno.ETIMEDOUT`. .. versionadded:: 3.3 All the above :exc:`OSError` subclasses were added. diff --git a/Doc/library/fcntl.rst b/Doc/library/fcntl.rst index c5a7ba0150eb7e..13de0c7bc33bf6 100644 --- a/Doc/library/fcntl.rst +++ b/Doc/library/fcntl.rst @@ -166,9 +166,9 @@ The module defines the following functions: which the lock starts, relative to *whence*, and *whence* is as with :func:`io.IOBase.seek`, specifically: - * :const:`0` -- relative to the start of the file (:data:`os.SEEK_SET`) - * :const:`1` -- relative to the current buffer position (:data:`os.SEEK_CUR`) - * :const:`2` -- relative to the end of the file (:data:`os.SEEK_END`) + * :const:`0` -- relative to the start of the file (:const:`os.SEEK_SET`) + * :const:`1` -- relative to the current buffer position (:const:`os.SEEK_CUR`) + * :const:`2` -- relative to the end of the file (:const:`os.SEEK_END`) The default for *start* is 0, which means to start at the beginning of the file. The default for *len* is 0 which means to lock to the end of the file. The @@ -195,7 +195,7 @@ using the :func:`flock` call may be better. .. seealso:: Module :mod:`os` - If the locking flags :data:`~os.O_SHLOCK` and :data:`~os.O_EXLOCK` are + If the locking flags :const:`~os.O_SHLOCK` and :const:`~os.O_EXLOCK` are present in the :mod:`os` module (on BSD only), the :func:`os.open` function provides an alternative to the :func:`lockf` and :func:`flock` functions. diff --git a/Doc/library/ftplib.rst b/Doc/library/ftplib.rst index 517ea4796c0a4e..dcac83bbe777f6 100644 --- a/Doc/library/ftplib.rst +++ b/Doc/library/ftplib.rst @@ -108,7 +108,7 @@ The module defines the following items: .. versionchanged:: 3.4 The class now supports hostname check with :attr:`ssl.SSLContext.check_hostname` and *Server Name Indication* (see - :data:`ssl.HAS_SNI`). + :const:`ssl.HAS_SNI`). .. deprecated:: 3.6 @@ -448,7 +448,7 @@ FTP_TLS Objects .. versionchanged:: 3.4 The method now supports hostname check with :attr:`ssl.SSLContext.check_hostname` and *Server Name Indication* (see - :data:`ssl.HAS_SNI`). + :const:`ssl.HAS_SNI`). .. method:: FTP_TLS.ccc() diff --git a/Doc/library/http.client.rst b/Doc/library/http.client.rst index df99ab99826e99..87433ddbd74971 100644 --- a/Doc/library/http.client.rst +++ b/Doc/library/http.client.rst @@ -84,7 +84,7 @@ The module provides the following classes: .. versionchanged:: 3.2 This class now supports HTTPS virtual hosts if possible (that is, - if :data:`ssl.HAS_SNI` is true). + if :const:`ssl.HAS_SNI` is true). .. versionchanged:: 3.4 The *strict* parameter was removed. HTTP 0.9-style "Simple Responses" are diff --git a/Doc/library/imaplib.rst b/Doc/library/imaplib.rst index 0c10e7afee401f..594c6b1a3803a8 100644 --- a/Doc/library/imaplib.rst +++ b/Doc/library/imaplib.rst @@ -112,7 +112,7 @@ There's also a subclass for secure connections: .. versionchanged:: 3.4 The class now supports hostname check with :attr:`ssl.SSLContext.check_hostname` and *Server Name Indication* (see - :data:`ssl.HAS_SNI`). + :const:`ssl.HAS_SNI`). .. deprecated:: 3.6 @@ -513,7 +513,7 @@ An :class:`IMAP4` instance has the following methods: .. versionchanged:: 3.4 The method now supports hostname check with :attr:`ssl.SSLContext.check_hostname` and *Server Name Indication* (see - :data:`ssl.HAS_SNI`). + :const:`ssl.HAS_SNI`). .. method:: IMAP4.status(mailbox, names) diff --git a/Doc/library/io.rst b/Doc/library/io.rst index c9249da1c3c3d2..7eec1f87583b87 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -423,7 +423,7 @@ I/O Base Classes .. versionadded:: 3.3 Some operating systems could support additional values, like - :data:`os.SEEK_HOLE` or :data:`os.SEEK_DATA`. The valid values + :const:`os.SEEK_HOLE` or :const:`os.SEEK_DATA`. The valid values for a file could depend on it being open in text or binary mode. .. method:: seekable() diff --git a/Doc/library/multiprocessing.rst b/Doc/library/multiprocessing.rst index 26b057f297bf71..6cbbcbf1b804a5 100644 --- a/Doc/library/multiprocessing.rst +++ b/Doc/library/multiprocessing.rst @@ -2682,7 +2682,7 @@ handler type) for messages from different processes to get mixed up. Returns the logger used by :mod:`multiprocessing`. If necessary, a new one will be created. - When first created the logger has level :data:`logging.NOTSET` and no + When first created the logger has level :const:`logging.NOTSET` and no default handler. Messages sent to this logger will not by default propagate to the root logger. diff --git a/Doc/library/optparse.rst b/Doc/library/optparse.rst index f3b3a43f3c44a6..6ab61ba1d13e02 100644 --- a/Doc/library/optparse.rst +++ b/Doc/library/optparse.rst @@ -812,7 +812,7 @@ The first step in using :mod:`optparse` is to create an OptionParser instance. help option. When :mod:`optparse` prints the usage string, it expands ``%prog`` to ``os.path.basename(sys.argv[0])`` (or to ``prog`` if you passed that keyword argument). To suppress a usage message, pass the - special value :data:`optparse.SUPPRESS_USAGE`. + special value :const:`optparse.SUPPRESS_USAGE`. ``option_list`` (default: ``[]``) A list of Option objects to populate the parser with. The options in @@ -1078,7 +1078,7 @@ relevant to a particular option, or fail to pass a required option attribute, Help text to print for this option when listing all available options after the user supplies a :attr:`~Option.help` option (such as ``--help``). If no help text is supplied, the option will be listed without help text. To - hide this option, use the special value :data:`optparse.SUPPRESS_HELP`. + hide this option, use the special value :const:`optparse.SUPPRESS_HELP`. .. attribute:: Option.metavar @@ -1250,7 +1250,7 @@ must specify for any option using that action. If no :attr:`~Option.help` string is supplied for an option, it will still be listed in the help message. To omit an option entirely, use the special value - :data:`optparse.SUPPRESS_HELP`. + :const:`optparse.SUPPRESS_HELP`. :mod:`optparse` automatically adds a :attr:`~Option.help` option to all OptionParsers, so you do not normally need to create one. @@ -1521,7 +1521,7 @@ OptionParser supports several other public methods: Set the usage string according to the rules described above for the ``usage`` constructor keyword argument. Passing ``None`` sets the default usage - string; use :data:`optparse.SUPPRESS_USAGE` to suppress a usage message. + string; use :const:`optparse.SUPPRESS_USAGE` to suppress a usage message. .. method:: OptionParser.print_usage(file=None) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 31946f1a12b9da..283dc5ad1541a1 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -233,7 +233,7 @@ process and user. :data:`environ` and :data:`environb` are synchronized (modifying :data:`environb` updates :data:`environ`, and vice versa). - :data:`environb` is only available if :data:`supports_bytes_environ` is + :data:`environb` is only available if :const:`supports_bytes_environ` is ``True``. .. versionadded:: 3.2 @@ -331,7 +331,7 @@ process and user. future environment changes. - :func:`getenvb` is only available if :data:`supports_bytes_environ` + :func:`getenvb` is only available if :const:`supports_bytes_environ` is ``True``. .. availability:: Unix. @@ -829,7 +829,7 @@ as internal buffering of data. If *offset_src* is None, then *src* is read from the current position; respectively for *offset_dst*. The files pointed by *src* and *dst* must reside in the same filesystem, otherwise an :exc:`OSError` is - raised with :attr:`~OSError.errno` set to :data:`errno.EXDEV`. + raised with :attr:`~OSError.errno` set to :const:`errno.EXDEV`. This copy is done without the additional cost of transferring data from the kernel to user space and then back into the kernel. Additionally, @@ -1071,7 +1071,7 @@ as internal buffering of data. .. versionadded:: 3.3 Some operating systems could support additional values, like - :data:`os.SEEK_HOLE` or :data:`os.SEEK_DATA`. + :const:`os.SEEK_HOLE` or :const:`os.SEEK_DATA`. .. function:: open(path, flags, mode=0o777, *, dir_fd=None) @@ -1312,7 +1312,7 @@ or `the MSDN `_ on Windo If some data was successfully read, it will return the number of bytes read. If no bytes were read, it will return ``-1`` and set errno to - :data:`errno.EAGAIN`. + :const:`errno.EAGAIN`. .. availability:: Linux >= 4.14. @@ -1513,7 +1513,7 @@ or `the MSDN `_ on Windo *offset_dst*. The offset associated to the file descriptor that refers to a pipe must be ``None``. The files pointed by *src* and *dst* must reside in the same filesystem, otherwise an :exc:`OSError` is raised with - :attr:`~OSError.errno` set to :data:`errno.EXDEV`. + :attr:`~OSError.errno` set to :const:`errno.EXDEV`. This copy is done without the additional cost of transferring data from the kernel to user space and then back into the kernel. Additionally, @@ -1846,18 +1846,18 @@ features: Set the flags of *path* to the numeric *flags*. *flags* may take a combination (bitwise OR) of the following values (as defined in the :mod:`stat` module): - * :data:`stat.UF_NODUMP` - * :data:`stat.UF_IMMUTABLE` - * :data:`stat.UF_APPEND` - * :data:`stat.UF_OPAQUE` - * :data:`stat.UF_NOUNLINK` - * :data:`stat.UF_COMPRESSED` - * :data:`stat.UF_HIDDEN` - * :data:`stat.SF_ARCHIVED` - * :data:`stat.SF_IMMUTABLE` - * :data:`stat.SF_APPEND` - * :data:`stat.SF_NOUNLINK` - * :data:`stat.SF_SNAPSHOT` + * :const:`stat.UF_NODUMP` + * :const:`stat.UF_IMMUTABLE` + * :const:`stat.UF_APPEND` + * :const:`stat.UF_OPAQUE` + * :const:`stat.UF_NOUNLINK` + * :const:`stat.UF_COMPRESSED` + * :const:`stat.UF_HIDDEN` + * :const:`stat.SF_ARCHIVED` + * :const:`stat.SF_IMMUTABLE` + * :const:`stat.SF_APPEND` + * :const:`stat.SF_NOUNLINK` + * :const:`stat.SF_SNAPSHOT` This function can support :ref:`not following symlinks `. @@ -1878,25 +1878,25 @@ features: following values (as defined in the :mod:`stat` module) or bitwise ORed combinations of them: - * :data:`stat.S_ISUID` - * :data:`stat.S_ISGID` - * :data:`stat.S_ENFMT` - * :data:`stat.S_ISVTX` - * :data:`stat.S_IREAD` - * :data:`stat.S_IWRITE` - * :data:`stat.S_IEXEC` - * :data:`stat.S_IRWXU` - * :data:`stat.S_IRUSR` - * :data:`stat.S_IWUSR` - * :data:`stat.S_IXUSR` - * :data:`stat.S_IRWXG` - * :data:`stat.S_IRGRP` - * :data:`stat.S_IWGRP` - * :data:`stat.S_IXGRP` - * :data:`stat.S_IRWXO` - * :data:`stat.S_IROTH` - * :data:`stat.S_IWOTH` - * :data:`stat.S_IXOTH` + * :const:`stat.S_ISUID` + * :const:`stat.S_ISGID` + * :const:`stat.S_ENFMT` + * :const:`stat.S_ISVTX` + * :const:`stat.S_IREAD` + * :const:`stat.S_IWRITE` + * :const:`stat.S_IEXEC` + * :const:`stat.S_IRWXU` + * :const:`stat.S_IRUSR` + * :const:`stat.S_IWUSR` + * :const:`stat.S_IXUSR` + * :const:`stat.S_IRWXG` + * :const:`stat.S_IRGRP` + * :const:`stat.S_IWGRP` + * :const:`stat.S_IXGRP` + * :const:`stat.S_IRWXO` + * :const:`stat.S_IROTH` + * :const:`stat.S_IWOTH` + * :const:`stat.S_IXOTH` This function can support :ref:`specifying a file descriptor `, :ref:`paths relative to directory descriptors ` and :ref:`not @@ -3920,8 +3920,8 @@ written in Python, such as a mail server's external command delivery program. Send signal *sig* to the process *pid*. Constants for the specific signals available on the host platform are defined in the :mod:`signal` module. - Windows: The :data:`signal.CTRL_C_EVENT` and - :data:`signal.CTRL_BREAK_EVENT` signals are special signals which can + Windows: The :const:`signal.CTRL_C_EVENT` and + :const:`signal.CTRL_BREAK_EVENT` signals are special signals which can only be sent to console processes which share a common console window, e.g., some subprocesses. Any other value for *sig* will cause the process to be unconditionally killed by the TerminateProcess API, and the exit code @@ -4414,7 +4414,7 @@ written in Python, such as a mail server's external command delivery program. * :attr:`!si_pid` (process ID) * :attr:`!si_uid` (real user ID of the child) - * :attr:`!si_signo` (always :data:`~signal.SIGCHLD`) + * :attr:`!si_signo` (always :const:`~signal.SIGCHLD`) * :attr:`!si_status` (the exit status or signal number, depending on :attr:`!si_code`) * :attr:`!si_code` (see :data:`CLD_EXITED` for possible values) @@ -4652,7 +4652,7 @@ used to determine the disposition of a process. .. function:: WIFCONTINUED(status) Return ``True`` if a stopped child has been resumed by delivery of - :data:`~signal.SIGCONT` (if the process has been continued from a job + :const:`~signal.SIGCONT` (if the process has been continued from a job control stop), otherwise return ``False``. See :data:`WCONTINUED` option. @@ -5024,7 +5024,7 @@ Random numbers ``/dev/urandom`` devices. The flags argument is a bit mask that can contain zero or more of the - following values ORed together: :py:data:`os.GRND_RANDOM` and + following values ORed together: :py:const:`os.GRND_RANDOM` and :py:data:`GRND_NONBLOCK`. See also the `Linux getrandom() manual page diff --git a/Doc/library/poplib.rst b/Doc/library/poplib.rst index e22a2e1455e7fc..c9ada42447e8aa 100644 --- a/Doc/library/poplib.rst +++ b/Doc/library/poplib.rst @@ -81,7 +81,7 @@ The :mod:`poplib` module provides two classes: .. versionchanged:: 3.4 The class now supports hostname check with :attr:`ssl.SSLContext.check_hostname` and *Server Name Indication* (see - :data:`ssl.HAS_SNI`). + :const:`ssl.HAS_SNI`). .. deprecated:: 3.6 @@ -248,7 +248,7 @@ A :class:`POP3` instance has the following methods: This method supports hostname checking via :attr:`ssl.SSLContext.check_hostname` and *Server Name Indication* (see - :data:`ssl.HAS_SNI`). + :const:`ssl.HAS_SNI`). .. versionadded:: 3.4 diff --git a/Doc/library/shelve.rst b/Doc/library/shelve.rst index dc87af398ed757..01314f491f47a7 100644 --- a/Doc/library/shelve.rst +++ b/Doc/library/shelve.rst @@ -25,7 +25,7 @@ lots of shared sub-objects. The keys are ordinary strings. database file is opened for reading and writing. The optional *flag* parameter has the same interpretation as the *flag* parameter of :func:`dbm.open`. - By default, pickles created with :data:`pickle.DEFAULT_PROTOCOL` are used + By default, pickles created with :const:`pickle.DEFAULT_PROTOCOL` are used to serialize values. The version of the pickle protocol can be specified with the *protocol* parameter. @@ -42,7 +42,7 @@ lots of shared sub-objects. The keys are ordinary strings. mutated). .. versionchanged:: 3.10 - :data:`pickle.DEFAULT_PROTOCOL` is now used as the default pickle + :const:`pickle.DEFAULT_PROTOCOL` is now used as the default pickle protocol. .. versionchanged:: 3.11 @@ -119,7 +119,7 @@ Restrictions A subclass of :class:`collections.abc.MutableMapping` which stores pickled values in the *dict* object. - By default, pickles created with :data:`pickle.DEFAULT_PROTOCOL` are used + By default, pickles created with :const:`pickle.DEFAULT_PROTOCOL` are used to serialize values. The version of the pickle protocol can be specified with the *protocol* parameter. See the :mod:`pickle` documentation for a discussion of the pickle protocols. @@ -143,7 +143,7 @@ Restrictions Added context manager support. .. versionchanged:: 3.10 - :data:`pickle.DEFAULT_PROTOCOL` is now used as the default pickle + :const:`pickle.DEFAULT_PROTOCOL` is now used as the default pickle protocol. diff --git a/Doc/library/smtplib.rst b/Doc/library/smtplib.rst index a2815059ad66e4..67cd5b000afb70 100644 --- a/Doc/library/smtplib.rst +++ b/Doc/library/smtplib.rst @@ -103,7 +103,7 @@ Protocol) and :rfc:`1869` (SMTP Service Extensions). .. versionchanged:: 3.4 The class now supports hostname check with :attr:`ssl.SSLContext.check_hostname` and *Server Name Indication* (see - :data:`ssl.HAS_SNI`). + :const:`ssl.HAS_SNI`). .. deprecated:: 3.6 @@ -431,7 +431,7 @@ An :class:`SMTP` instance has the following methods: .. versionchanged:: 3.4 The method now supports hostname check with :attr:`SSLContext.check_hostname` and *Server Name Indicator* (see - :data:`~ssl.HAS_SNI`). + :const:`~ssl.HAS_SNI`). .. versionchanged:: 3.5 The error raised for lack of STARTTLS support is now the diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index 19b4a1ee59c282..2e129175636f27 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -2153,7 +2153,7 @@ This is because the previous execution has left the socket in a ``TIME_WAIT`` state, and can't be immediately reused. There is a :mod:`socket` flag to set, in order to prevent this, -:data:`socket.SO_REUSEADDR`:: +:const:`socket.SO_REUSEADDR`:: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 06f5293fbdbd1d..13e61b3d68006b 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -1643,9 +1643,9 @@ Blob objects .. method:: seek(offset, origin=os.SEEK_SET, /) Set the current access position of the blob to *offset*. The *origin* - argument defaults to :data:`os.SEEK_SET` (absolute blob positioning). - Other values for *origin* are :data:`os.SEEK_CUR` (seek relative to the - current position) and :data:`os.SEEK_END` (seek relative to the blob’s + argument defaults to :const:`os.SEEK_SET` (absolute blob positioning). + Other values for *origin* are :const:`os.SEEK_CUR` (seek relative to the + current position) and :const:`os.SEEK_END` (seek relative to the blob’s end). diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 289dd0b51dea19..a0936747db5a08 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -142,7 +142,7 @@ purposes. The settings are: :data:`PROTOCOL_TLS_CLIENT` or :data:`PROTOCOL_TLS_SERVER`, :data:`OP_NO_SSLv2`, and :data:`OP_NO_SSLv3` with high encryption cipher suites without RC4 and - without unauthenticated cipher suites. Passing :data:`~Purpose.SERVER_AUTH` + without unauthenticated cipher suites. Passing :const:`~Purpose.SERVER_AUTH` as *purpose* sets :data:`~SSLContext.verify_mode` to :data:`CERT_REQUIRED` and either loads CA certificates (when at least one of *cafile*, *capath* or *cadata* is given) or uses :meth:`SSLContext.load_default_certs` to load @@ -1579,9 +1579,9 @@ to speed up repeated connections from the same clients. load CA certificates from other locations, too. The *purpose* flag specifies what kind of CA certificates are loaded. The - default settings :data:`Purpose.SERVER_AUTH` loads certificates, that are + default settings :const:`Purpose.SERVER_AUTH` loads certificates, that are flagged and trusted for TLS web server authentication (client side - sockets). :data:`Purpose.CLIENT_AUTH` loads CA certificates for client + sockets). :const:`Purpose.CLIENT_AUTH` loads CA certificates for client certificate verification on the server side. .. versionadded:: 3.4 @@ -1824,7 +1824,7 @@ to speed up repeated connections from the same clients. Wrap an existing Python socket *sock* and return an instance of :attr:`SSLContext.sslsocket_class` (default :class:`SSLSocket`). The returned SSL socket is tied to the context, its settings and certificates. - *sock* must be a :data:`~socket.SOCK_STREAM` socket; other + *sock* must be a :const:`~socket.SOCK_STREAM` socket; other socket types are unsupported. The parameter ``server_side`` is a boolean which identifies whether diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 643cdb876ee3c4..718dc48e804c01 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -686,7 +686,7 @@ always available. Return the current value of the flags that are used for :c:func:`dlopen` calls. Symbolic names for the flag values can be found in the :mod:`os` module (``RTLD_xxx`` constants, e.g. - :data:`os.RTLD_LAZY`). + :const:`os.RTLD_LAZY`). .. availability:: Unix. @@ -1342,7 +1342,7 @@ always available. ``sys.setdlopenflags(0)``. To share symbols across extension modules, call as ``sys.setdlopenflags(os.RTLD_GLOBAL)``. Symbolic names for the flag values can be found in the :mod:`os` module (``RTLD_xxx`` constants, e.g. - :data:`os.RTLD_LAZY`). + :const:`os.RTLD_LAZY`). .. availability:: Unix. diff --git a/Doc/library/tempfile.rst b/Doc/library/tempfile.rst index c17ead1510e7ef..84a6f8887954cc 100644 --- a/Doc/library/tempfile.rst +++ b/Doc/library/tempfile.rst @@ -59,7 +59,7 @@ The module defines the following user-callable items: platforms, it is a file-like object whose :attr:`!file` attribute is the underlying true file object. - The :py:data:`os.O_TMPFILE` flag is used if it is available and works + The :py:const:`os.O_TMPFILE` flag is used if it is available and works (Linux-specific, requires Linux kernel 3.11 or later). On platforms that are neither Posix nor Cygwin, TemporaryFile is an alias @@ -69,7 +69,7 @@ The module defines the following user-callable items: .. versionchanged:: 3.5 - The :py:data:`os.O_TMPFILE` flag is now used if available. + The :py:const:`os.O_TMPFILE` flag is now used if available. .. versionchanged:: 3.8 Added *errors* parameter. diff --git a/Doc/library/test.rst b/Doc/library/test.rst index 73d1178906a8cd..427953e0077aab 100644 --- a/Doc/library/test.rst +++ b/Doc/library/test.rst @@ -418,7 +418,7 @@ The :mod:`test.support` module defines the following functions: .. function:: with_pymalloc() - Return :data:`_testcapi.WITH_PYMALLOC`. + Return :const:`_testcapi.WITH_PYMALLOC`. .. function:: requires(resource, msg=None) diff --git a/Doc/library/unittest.mock.rst b/Doc/library/unittest.mock.rst index 4f06e836d57281..8d877165a09740 100644 --- a/Doc/library/unittest.mock.rst +++ b/Doc/library/unittest.mock.rst @@ -2436,7 +2436,7 @@ behaviour you can switch it off by setting the module level switch Alternatively you can just use ``vars(my_mock)`` (instance members) and ``dir(type(my_mock))`` (type members) to bypass the filtering irrespective of -:data:`mock.FILTER_DIR`. +:const:`mock.FILTER_DIR`. mock_open diff --git a/Doc/library/urllib.request.rst b/Doc/library/urllib.request.rst index 1b05458280d896..35b8f5b471dd96 100644 --- a/Doc/library/urllib.request.rst +++ b/Doc/library/urllib.request.rst @@ -99,7 +99,7 @@ The :mod:`urllib.request` module defines the following functions: .. versionchanged:: 3.2 HTTPS virtual hosts are now supported if possible (that is, if - :data:`ssl.HAS_SNI` is true). + :const:`ssl.HAS_SNI` is true). .. versionadded:: 3.2 *data* can be an iterable object. diff --git a/Doc/library/xml.rst b/Doc/library/xml.rst index 20b0905bb1093a..cf30de67719b93 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 - :data:`pyexpat.EXPAT_VERSION`. + :const:`pyexpat.EXPAT_VERSION`. 2. :mod:`xml.etree.ElementTree` doesn't expand external entities and raises a :exc:`ParserError` when an entity occurs. 3. :mod:`xml.dom.minidom` doesn't expand external entities and simply returns diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst index 2f31910a5958c2..468228f12cfa0a 100644 --- a/Doc/using/configure.rst +++ b/Doc/using/configure.rst @@ -62,7 +62,7 @@ General Options .. cmdoption:: --with-tzpath= - Select the default time zone search path for :data:`zoneinfo.TZPATH`. + Select the default time zone search path for :const:`zoneinfo.TZPATH`. See the :ref:`Compile-time configuration ` of the :mod:`zoneinfo` module. @@ -77,7 +77,7 @@ General Options Build the ``_decimal`` extension module using a thread-local context rather than a coroutine-local context (default), see the :mod:`decimal` module. - See :data:`decimal.HAVE_CONTEXTVAR` and the :mod:`contextvars` module. + See :const:`decimal.HAVE_CONTEXTVAR` and the :mod:`contextvars` module. .. versionadded:: 3.9 diff --git a/Doc/whatsnew/2.7.rst b/Doc/whatsnew/2.7.rst index 3cbab8d567bfac..ba6aebafd59d9f 100644 --- a/Doc/whatsnew/2.7.rst +++ b/Doc/whatsnew/2.7.rst @@ -1555,9 +1555,9 @@ changes, or look through the Subversion logs for all the details. :issue:`8484`.) The version of OpenSSL being used is now available as the module - attributes :data:`ssl.OPENSSL_VERSION` (a string), - :data:`ssl.OPENSSL_VERSION_INFO` (a 5-tuple), and - :data:`ssl.OPENSSL_VERSION_NUMBER` (an integer). (Added by Antoine + attributes :const:`ssl.OPENSSL_VERSION` (a string), + :const:`ssl.OPENSSL_VERSION_INFO` (a 5-tuple), and + :const:`ssl.OPENSSL_VERSION_NUMBER` (an integer). (Added by Antoine Pitrou; :issue:`8321`.) * The :mod:`struct` module will no longer silently ignore overflow diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index b0b570be8947b5..56b8ff36bee774 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -1252,8 +1252,8 @@ descriptors without copying between kernel address space and user address space, where one of the file descriptors must refer to a pipe. (Contributed by Pablo Galindo in :issue:`41625`.) -Add :data:`~os.O_EVTONLY`, :data:`~os.O_FSYNC`, :data:`~os.O_SYMLINK` -and :data:`~os.O_NOFOLLOW_ANY` for macOS. +Add :const:`~os.O_EVTONLY`, :const:`~os.O_FSYNC`, :const:`~os.O_SYMLINK` +and :const:`~os.O_NOFOLLOW_ANY` for macOS. (Contributed by Dong-hee Na in :issue:`43106`.) os.path @@ -1318,7 +1318,7 @@ objects in the tree returned by :func:`pyclbr.readline` and shelve ------ -The :mod:`shelve` module now uses :data:`pickle.DEFAULT_PROTOCOL` by default +The :mod:`shelve` module now uses :const:`pickle.DEFAULT_PROTOCOL` by default instead of :mod:`pickle` protocol ``3`` when creating shelves. (Contributed by Zackery Spytz in :issue:`34204`.) @@ -1355,7 +1355,7 @@ The ssl module requires OpenSSL 1.1.1 or newer. (Contributed by Christian Heimes in :pep:`644` and :issue:`43669`.) The ssl module has preliminary support for OpenSSL 3.0.0 and new option -:data:`~ssl.OP_IGNORE_UNEXPECTED_EOF`. +:const:`~ssl.OP_IGNORE_UNEXPECTED_EOF`. (Contributed by Christian Heimes in :issue:`38820`, :issue:`43794`, :issue:`43788`, :issue:`43791`, :issue:`43799`, :issue:`43920`, :issue:`43789`, and :issue:`43811`.) @@ -1386,7 +1386,7 @@ Add a *timeout* parameter to the :func:`ssl.get_server_certificate` function. The ssl module uses heap-types and multi-phase initialization. (Contributed by Christian Heimes in :issue:`42333`.) -A new verify flag :data:`~ssl.VERIFY_X509_PARTIAL_CHAIN` has been added. +A new verify flag :const:`~ssl.VERIFY_X509_PARTIAL_CHAIN` has been added. (Contributed by l0x in :issue:`40849`.) sqlite3 @@ -1412,7 +1412,7 @@ _thread ------- :func:`_thread.interrupt_main` now takes an optional signal number to -simulate (the default is still :data:`signal.SIGINT`). +simulate (the default is still :const:`signal.SIGINT`). (Contributed by Antoine Pitrou in :issue:`43356`.) threading @@ -1756,8 +1756,8 @@ Deprecated * :data:`~ssl.PROTOCOL_SSLv2`, :data:`~ssl.PROTOCOL_SSLv3`, :data:`~ssl.PROTOCOL_SSLv23`, :data:`~ssl.PROTOCOL_TLSv1`, :data:`~ssl.PROTOCOL_TLSv1_1`, :data:`~ssl.PROTOCOL_TLSv1_2`, and - :data:`~ssl.PROTOCOL_TLS` are deprecated in favor of - :data:`~ssl.PROTOCOL_TLS_CLIENT` and :data:`~ssl.PROTOCOL_TLS_SERVER` + :const:`~ssl.PROTOCOL_TLS` are deprecated in favor of + :const:`~ssl.PROTOCOL_TLS_CLIENT` and :const:`~ssl.PROTOCOL_TLS_SERVER` * :func:`~ssl.wrap_socket` is replaced by :meth:`ssl.SSLContext.wrap_socket` diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 973ce154bbdade..c437c5bfb6120f 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -692,7 +692,7 @@ enum * Added the :func:`~enum.global_enum` enum decorator, which adjusts :meth:`~object.__repr__` and :meth:`~object.__str__` to show values as members of their module rather than the enum class. - For example, ``'re.ASCII'`` for the :data:`~re.ASCII` member + For example, ``'re.ASCII'`` for the :const:`~re.ASCII` member of :class:`re.RegexFlag` rather than ``'RegexFlag.ASCII'``. * Enhanced :class:`~enum.Flag` to support @@ -1065,8 +1065,8 @@ threading * On Unix, if the ``sem_clockwait()`` function is available in the C library (glibc 2.30 and newer), the :meth:`threading.Lock.acquire` method now uses - the monotonic clock (:data:`time.CLOCK_MONOTONIC`) for the timeout, rather - than using the system clock (:data:`time.CLOCK_REALTIME`), to not be affected + the monotonic clock (:const:`time.CLOCK_MONOTONIC`) for the timeout, rather + than using the system clock (:const:`time.CLOCK_REALTIME`), to not be affected by system clock changes. (Contributed by Victor Stinner in :issue:`41710`.) @@ -1814,7 +1814,7 @@ Standard Library (Contributed by Serhiy Storchaka in :gh:`91760`.) * In the :mod:`re` module, the :func:`!re.template` function - and the corresponding :data:`!re.TEMPLATE` and :data:`!re.T` flags + and the corresponding :const:`!re.TEMPLATE` and :const:`!re.T` flags are deprecated, as they were undocumented and lacked an obvious purpose. They will be removed in Python 3.13. (Contributed by Serhiy Storchaka and Miro Hrončok in :gh:`92728`.) diff --git a/Doc/whatsnew/3.2.rst b/Doc/whatsnew/3.2.rst index 2e4c8ee45df84b..2c176926e55e1f 100644 --- a/Doc/whatsnew/3.2.rst +++ b/Doc/whatsnew/3.2.rst @@ -1664,9 +1664,9 @@ for secure (encrypted, authenticated) internet connections: algorithm" error. * The version of OpenSSL being used is now accessible using the module - attributes :data:`ssl.OPENSSL_VERSION` (a string), - :data:`ssl.OPENSSL_VERSION_INFO` (a 5-tuple), and - :data:`ssl.OPENSSL_VERSION_NUMBER` (an integer). + attributes :const:`ssl.OPENSSL_VERSION` (a string), + :const:`ssl.OPENSSL_VERSION_INFO` (a 5-tuple), and + :const:`ssl.OPENSSL_VERSION_NUMBER` (an integer). (Contributed by Antoine Pitrou in :issue:`8850`, :issue:`1589`, :issue:`8322`, :issue:`5639`, :issue:`4870`, :issue:`8484`, and :issue:`8321`.) diff --git a/Doc/whatsnew/3.3.rst b/Doc/whatsnew/3.3.rst index 9bbf52c5551782..782048c397f973 100644 --- a/Doc/whatsnew/3.3.rst +++ b/Doc/whatsnew/3.3.rst @@ -841,7 +841,7 @@ Builtin functions and types * :func:`open` gets a new *opener* parameter: the underlying file descriptor for the file object is then obtained by calling *opener* with (*file*, - *flags*). It can be used to use custom flags like :data:`os.O_CLOEXEC` for + *flags*). It can be used to use custom flags like :const:`os.O_CLOEXEC` for example. The ``'x'`` mode was added: open for exclusive creation, failing if the file already exists. * :func:`print`: added the *flush* keyword argument. If the *flush* keyword @@ -1126,7 +1126,7 @@ Features * If Python is compiled without threads, the C version automatically disables the expensive thread local context machinery. In this case, - the variable :data:`~decimal.HAVE_THREADS` is set to ``False``. + the variable :const:`~decimal.HAVE_THREADS` is set to ``False``. API changes ~~~~~~~~~~~ @@ -1575,8 +1575,8 @@ os -- * The :mod:`os` module has a new :func:`~os.pipe2` function that makes it - possible to create a pipe with :data:`~os.O_CLOEXEC` or - :data:`~os.O_NONBLOCK` flags set atomically. This is especially useful to + possible to create a pipe with :const:`~os.O_CLOEXEC` or + :const:`~os.O_NONBLOCK` flags set atomically. This is especially useful to avoid race conditions in multi-threaded programs. * The :mod:`os` module has a new :func:`~os.sendfile` function which provides @@ -1690,9 +1690,9 @@ os * Some platforms now support additional constants for the :func:`~os.lseek` function, such as ``os.SEEK_HOLE`` and ``os.SEEK_DATA``. -* New constants :data:`~os.RTLD_LAZY`, :data:`~os.RTLD_NOW`, - :data:`~os.RTLD_GLOBAL`, :data:`~os.RTLD_LOCAL`, :data:`~os.RTLD_NODELETE`, - :data:`~os.RTLD_NOLOAD`, and :data:`~os.RTLD_DEEPBIND` are available on +* New constants :const:`~os.RTLD_LAZY`, :const:`~os.RTLD_NOW`, + :const:`~os.RTLD_GLOBAL`, :const:`~os.RTLD_LOCAL`, :const:`~os.RTLD_NODELETE`, + :const:`~os.RTLD_NOLOAD`, and :const:`~os.RTLD_DEEPBIND` are available on platforms that support them. These are for use with the :func:`sys.setdlopenflags` function, and supersede the similar constants defined in :mod:`ctypes` and :mod:`DLFCN`. (Contributed by Victor Stinner @@ -1994,7 +1994,7 @@ subprocess Command strings can now be bytes objects on posix platforms. (Contributed by Victor Stinner in :issue:`8513`.) -A new constant :data:`~subprocess.DEVNULL` allows suppressing output in a +A new constant :const:`~subprocess.DEVNULL` allows suppressing output in a platform-independent fashion. (Contributed by Ross Lagerwall in :issue:`5870`.) diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst index 7436a552f529e5..b0d0417447691e 100644 --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -775,7 +775,7 @@ of a given opcode and argument, information that is not otherwise available. doctest ------- -A new :ref:`option flag `, :data:`~doctest.FAIL_FAST`, halts +A new :ref:`option flag `, :const:`~doctest.FAIL_FAST`, halts test running as soon as the first failure is detected. (Contributed by R. David Murray and Daniel Urban in :issue:`16522`.) @@ -841,7 +841,7 @@ for example, if the file might have been changed and re-checked in less time than the resolution of a particular filesystem's file modification time field. (Contributed by Mark Levitt in :issue:`18149`.) -New module attribute :data:`~filecmp.DEFAULT_IGNORES` provides the list of +New module attribute :const:`~filecmp.DEFAULT_IGNORES` provides the list of directories that are used as the default value for the *ignore* parameter of the :func:`~filecmp.dircmp` function. (Contributed by Eli Bendersky in :issue:`15442`.) @@ -1189,7 +1189,7 @@ Windows). (Contributed by Brian Curtin in :issue:`11939`.) root on Windows. (Contributed by Tim Golden in :issue:`9035`.) :func:`os.open` supports two new flags on platforms that provide them, -:data:`~os.O_PATH` (un-opened file descriptor), and :data:`~os.O_TMPFILE` +:const:`~os.O_PATH` (un-opened file descriptor), and :const:`~os.O_TMPFILE` (unnamed temporary file; as of 3.4.0 release available only on Linux systems with a kernel version of 3.11 or newer that have uapi headers). (Contributed by Christian Heimes in :issue:`18673` and Benjamin Peterson, respectively.) @@ -1238,8 +1238,8 @@ plistlib stdlib serialization protocols, with new :func:`~plistlib.load`, :func:`~plistlib.dump`, :func:`~plistlib.loads`, and :func:`~plistlib.dumps` functions. (The older API is now deprecated.) In addition to the already -supported XML plist format (:data:`~plistlib.FMT_XML`), it also now supports -the binary plist format (:data:`~plistlib.FMT_BINARY`). (Contributed by Ronald +supported XML plist format (:const:`~plistlib.FMT_XML`), it also now supports +the binary plist format (:const:`~plistlib.FMT_BINARY`). (Contributed by Ronald Oussoren and others in :issue:`14455`.) @@ -1388,7 +1388,7 @@ try/except statement by code that only cares whether or not an error occurred. socket ------ -The socket module now supports the :data:`~socket.CAN_BCM` protocol on +The socket module now supports the :const:`~socket.CAN_BCM` protocol on platforms that support it. (Contributed by Brian Thorne in :issue:`15359`.) Socket objects have new methods to get or set their :ref:`inheritable flag @@ -1399,7 +1399,7 @@ The ``socket.AF_*`` and ``socket.SOCK_*`` constants are now enumeration values using the new :mod:`enum` module. This allows meaningful names to be printed during debugging, instead of integer "magic numbers". -The :data:`~socket.AF_LINK` constant is now available on BSD and OSX. +The :const:`~socket.AF_LINK` constant is now available on BSD and OSX. :func:`~socket.inet_pton` and :func:`~socket.inet_ntop` are now supported on Windows. (Contributed by Atsuo Ishimoto in :issue:`7171`.) @@ -1460,8 +1460,8 @@ Heimes in :issue:`18147`.) If OpenSSL 0.9.8 or later is available, :class:`~ssl.SSLContext` has a new attribute :attr:`~ssl.SSLContext.verify_flags` that can be used to control the certificate verification process by setting it to some combination of the new -constants :data:`~ssl.VERIFY_DEFAULT`, :data:`~ssl.VERIFY_CRL_CHECK_LEAF`, -:data:`~ssl.VERIFY_CRL_CHECK_CHAIN`, or :data:`~ssl.VERIFY_X509_STRICT`. +constants :const:`~ssl.VERIFY_DEFAULT`, :const:`~ssl.VERIFY_CRL_CHECK_LEAF`, +:const:`~ssl.VERIFY_CRL_CHECK_CHAIN`, or :const:`~ssl.VERIFY_X509_STRICT`. OpenSSL does not do any CRL verification by default. (Contributed by Christien Heimes in :issue:`8813`.) diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst index fb64135e1ee38f..23d2f6562aab86 100644 --- a/Doc/whatsnew/3.5.rst +++ b/Doc/whatsnew/3.5.rst @@ -478,7 +478,7 @@ not make an additional system call:: PEP 475: Retry system calls failing with EINTR ---------------------------------------------- -An :py:data:`errno.EINTR` error code is returned whenever a system call, that +An :py:const:`errno.EINTR` error code is returned whenever a system call, that is waiting for I/O, is interrupted by a signal. Previously, Python would raise :exc:`InterruptedError` in such cases. This meant that, when writing a Python application, the developer had two choices: @@ -527,7 +527,7 @@ by a signal: :func:`~os.writev`; * special cases: :func:`os.close` and :func:`os.dup2` now ignore - :py:data:`~errno.EINTR` errors; the syscall is not retried (see the PEP + :py:const:`~errno.EINTR` errors; the syscall is not retried (see the PEP for the rationale); * :mod:`select` functions: :func:`devpoll.poll() `, @@ -1498,7 +1498,7 @@ use ``/dev/urandom`` and avoiding failures due to potential file descriptor exhaustion. (Contributed by Victor Stinner in :issue:`22181`.) New :func:`~os.get_blocking` and :func:`~os.set_blocking` functions allow -getting and setting a file descriptor's blocking mode (:data:`~os.O_NONBLOCK`.) +getting and setting a file descriptor's blocking mode (:const:`~os.O_NONBLOCK`.) (Contributed by Victor Stinner in :issue:`22054`.) The :func:`~os.truncate` and :func:`~os.ftruncate` functions are now supported @@ -1783,7 +1783,7 @@ the TLS handshake. The new :meth:`SSLSocket.selected_alpn_protocol() ` returns the protocol that was selected during the TLS handshake. -The :data:`~ssl.HAS_ALPN` flag indicates whether ALPN support is present. +The :const:`~ssl.HAS_ALPN` flag indicates whether ALPN support is present. Other Changes @@ -2476,7 +2476,7 @@ Changes in the Python API in Python 3.5, all old ``.pyo`` files from previous versions of Python are invalid regardless of this PEP. -* The :mod:`socket` module now exports the :data:`~socket.CAN_RAW_FD_FRAMES` +* The :mod:`socket` module now exports the :const:`~socket.CAN_RAW_FD_FRAMES` constant on linux 3.6 and greater. * The :func:`ssl.cert_time_to_seconds` function now interprets the input time diff --git a/Doc/whatsnew/3.6.rst b/Doc/whatsnew/3.6.rst index 20c43e0e04a3cf..1b7cef14d912e8 100644 --- a/Doc/whatsnew/3.6.rst +++ b/Doc/whatsnew/3.6.rst @@ -1404,7 +1404,7 @@ socket ------ The :func:`~socket.socket.ioctl` function now supports the -:data:`~socket.SIO_LOOPBACK_FAST_PATH` control code. +:const:`~socket.SIO_LOOPBACK_FAST_PATH` control code. (Contributed by Daniel Stokes in :issue:`26536`.) The :meth:`~socket.socket.getsockopt` constants ``SO_DOMAIN``, @@ -1416,7 +1416,7 @@ The :meth:`~socket.socket.setsockopt` now supports the (Contributed by Christian Heimes in :issue:`27744`.) The socket module now supports the address family -:data:`~socket.AF_ALG` to interface with Linux Kernel crypto API. ``ALG_*``, +:const:`~socket.AF_ALG` to interface with Linux Kernel crypto API. ``ALG_*``, ``SOL_ALG`` and :meth:`~socket.socket.sendmsg_afalg` were added. (Contributed by Christian Heimes in :issue:`27744` with support from Victor Stinner.) diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index de03e5bedeaa35..d00c55a7766136 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -1280,13 +1280,13 @@ This function should be used instead of :func:`os.close` for better compatibility across platforms. (Contributed by Christian Heimes in :issue:`32454`.) -The :mod:`socket` module now exposes the :data:`socket.TCP_CONGESTION` -(Linux 2.6.13), :data:`socket.TCP_USER_TIMEOUT` (Linux 2.6.37), and -:data:`socket.TCP_NOTSENT_LOWAT` (Linux 3.12) constants. +The :mod:`socket` module now exposes the :const:`socket.TCP_CONGESTION` +(Linux 2.6.13), :const:`socket.TCP_USER_TIMEOUT` (Linux 2.6.37), and +:const:`socket.TCP_NOTSENT_LOWAT` (Linux 3.12) constants. (Contributed by Omar Sandoval in :issue:`26273` and Nathaniel J. Smith in :issue:`29728`.) -Support for :data:`socket.AF_VSOCK` sockets has been added to allow +Support for :const:`socket.AF_VSOCK` sockets has been added to allow communication between virtual machines and their hosts. (Contributed by Cathy Avery in :issue:`27584`.) @@ -1394,7 +1394,7 @@ subprocess The :func:`subprocess.run` function accepts the new *capture_output* keyword argument. When true, stdout and stderr will be captured. -This is equivalent to passing :data:`subprocess.PIPE` as *stdout* and +This is equivalent to passing :const:`subprocess.PIPE` as *stdout* and *stderr* arguments. (Contributed by Bo Bayles in :issue:`32102`.) @@ -1453,12 +1453,12 @@ time New clock identifiers have been added: -* :data:`time.CLOCK_BOOTTIME` (Linux): Identical to - :data:`time.CLOCK_MONOTONIC`, except it also includes any time that the +* :const:`time.CLOCK_BOOTTIME` (Linux): Identical to + :const:`time.CLOCK_MONOTONIC`, except it also includes any time that the system is suspended. -* :data:`time.CLOCK_PROF` (FreeBSD, NetBSD and OpenBSD): High-resolution +* :const:`time.CLOCK_PROF` (FreeBSD, NetBSD and OpenBSD): High-resolution per-process CPU timer. -* :data:`time.CLOCK_UPTIME` (FreeBSD, OpenBSD): Time whose absolute value is +* :const:`time.CLOCK_UPTIME` (FreeBSD, OpenBSD): Time whose absolute value is the time the system has been running and not suspended, providing accurate uptime measurement. diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index 90220dfb0c054c..99291ba48390ce 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -1302,7 +1302,7 @@ Zackery Spytz in :issue:`25451`.) time ---- -Added new clock :data:`~time.CLOCK_UPTIME_RAW` for macOS 10.12. +Added new clock :const:`~time.CLOCK_UPTIME_RAW` for macOS 10.12. (Contributed by Joannah Nanjekye in :issue:`35702`.) diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index c7466c13a40486..1f0fbbc2196664 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -427,8 +427,8 @@ digests. It skips MD5 on platforms that block MD5 digest. fcntl ----- -Added constants :data:`~fcntl.F_OFD_GETLK`, :data:`~fcntl.F_OFD_SETLK` -and :data:`~fcntl.F_OFD_SETLKW`. +Added constants :const:`~fcntl.F_OFD_GETLK`, :const:`~fcntl.F_OFD_SETLK` +and :const:`~fcntl.F_OFD_SETLKW`. (Contributed by Dong-hee Na in :issue:`38602`.) ftplib @@ -593,11 +593,11 @@ a non-blocking socket. (Contributed by Dong-hee Na in :issue:`39259`.) os -- -Added :data:`~os.CLD_KILLED` and :data:`~os.CLD_STOPPED` for :attr:`si_code`. +Added :const:`~os.CLD_KILLED` and :const:`~os.CLD_STOPPED` for :attr:`si_code`. (Contributed by Dong-hee Na in :issue:`38493`.) Exposed the Linux-specific :func:`os.pidfd_open` (:issue:`38692`) and -:data:`os.P_PIDFD` (:issue:`38713`) for process management with file +:const:`os.P_PIDFD` (:issue:`38713`) for process management with file descriptors. The :func:`os.unsetenv` function is now also available on Windows. @@ -669,11 +669,11 @@ a non-blocking socket. (Contributed by Dong-hee Na in :issue:`39259`.) socket ------ -The :mod:`socket` module now exports the :data:`~socket.CAN_RAW_JOIN_FILTERS` +The :mod:`socket` module now exports the :const:`~socket.CAN_RAW_JOIN_FILTERS` constant on Linux 4.1 and greater. (Contributed by Stefan Tatschner and Zackery Spytz in :issue:`25780`.) -The socket module now supports the :data:`~socket.CAN_J1939` protocol on +The socket module now supports the :const:`~socket.CAN_J1939` protocol on platforms that support it. (Contributed by Karl Ding in :issue:`40291`.) The socket module now has the :func:`socket.send_fds` and @@ -1084,7 +1084,7 @@ Changes in the Python API ``__VENV_PROMPT__`` is set to ``""``. * The :meth:`select.epoll.unregister` method no longer ignores the - :data:`~errno.EBADF` error. + :const:`~errno.EBADF` error. (Contributed by Victor Stinner in :issue:`39239`.) * The *compresslevel* parameter of :class:`bz2.BZ2File` became keyword-only, diff --git a/Misc/NEWS.d/3.10.0a1.rst b/Misc/NEWS.d/3.10.0a1.rst index 1c1c2d54e8c20a..58e57a951ae608 100644 --- a/Misc/NEWS.d/3.10.0a1.rst +++ b/Misc/NEWS.d/3.10.0a1.rst @@ -2176,7 +2176,7 @@ None. .. nonce: YoYoYo .. section: Library -Add a new :data:`os.RWF_APPEND` flag for :func:`os.pwritev`. +Add a new :const:`os.RWF_APPEND` flag for :func:`os.pwritev`. .. @@ -2304,7 +2304,7 @@ Restored the deprecated :mod:`xml.etree.cElementTree` module. .. nonce: ZCk0_c .. section: Library -:data:`~mmap.MAP_POPULATE` constant has now been added to the list of +:const:`~mmap.MAP_POPULATE` constant has now been added to the list of exported :mod:`mmap` module flags. .. diff --git a/Misc/NEWS.d/3.10.0a2.rst b/Misc/NEWS.d/3.10.0a2.rst index 61a291914f9333..cafd4804a1ee6e 100644 --- a/Misc/NEWS.d/3.10.0a2.rst +++ b/Misc/NEWS.d/3.10.0a2.rst @@ -604,7 +604,7 @@ changes the working directory. PR by Anthony Sottile. .. nonce: 9wXTtY .. section: Library -The :mod:`shelve` module now uses :data:`pickle.DEFAULT_PROTOCOL` by default +The :mod:`shelve` module now uses :const:`pickle.DEFAULT_PROTOCOL` by default instead of :mod:`pickle` protocol ``3``. .. diff --git a/Misc/NEWS.d/3.10.0a6.rst b/Misc/NEWS.d/3.10.0a6.rst index 803df6f51ce628..313aa689254040 100644 --- a/Misc/NEWS.d/3.10.0a6.rst +++ b/Misc/NEWS.d/3.10.0a6.rst @@ -294,8 +294,8 @@ actual dictionary. This created problems for introspection tools. .. nonce: SwcSuU .. section: Library -Added :data:`~os.O_EVTONLY`, :data:`~os.O_FSYNC`, :data:`~os.O_SYMLINK` and -:data:`~os.O_NOFOLLOW_ANY` for macOS. Patch by Dong-hee Na. +Added :const:`~os.O_EVTONLY`, :const:`~os.O_FSYNC`, :const:`~os.O_SYMLINK` and +:const:`~os.O_NOFOLLOW_ANY` for macOS. Patch by Dong-hee Na. .. @@ -304,7 +304,7 @@ Added :data:`~os.O_EVTONLY`, :data:`~os.O_FSYNC`, :data:`~os.O_SYMLINK` and .. nonce: a7Dote .. section: Library -Adds :data:`resource.RLIMIT_KQUEUES` constant from FreeBSD to the +Adds :const:`resource.RLIMIT_KQUEUES` constant from FreeBSD to the :mod:`resource` module. .. diff --git a/Misc/NEWS.d/3.10.0a7.rst b/Misc/NEWS.d/3.10.0a7.rst index 286d0a8a7e9190..ff01ee645c0a90 100644 --- a/Misc/NEWS.d/3.10.0a7.rst +++ b/Misc/NEWS.d/3.10.0a7.rst @@ -713,7 +713,7 @@ this situation. Also ensures that the :func:`tempfile.gettempdir()` and .. section: Library Expose ``X509_V_FLAG_ALLOW_PROXY_CERTS`` as -:data:`~ssl.VERIFY_ALLOW_PROXY_CERTS` to allow proxy certificate validation +:const:`~ssl.VERIFY_ALLOW_PROXY_CERTS` to allow proxy certificate validation as explained in https://www.openssl.org/docs/man1.1.1/man7/proxy-certificates.html. diff --git a/Misc/NEWS.d/3.10.0b1.rst b/Misc/NEWS.d/3.10.0b1.rst index cf89a550d38546..4f33420c144c93 100644 --- a/Misc/NEWS.d/3.10.0b1.rst +++ b/Misc/NEWS.d/3.10.0b1.rst @@ -871,7 +871,7 @@ assert_called_once_with) will unconditionally pass. .. nonce: -1XPDH .. section: Library -Add :data:`ssl.OP_IGNORE_UNEXPECTED_EOF` constants (OpenSSL 3.0.0) +Add :const:`ssl.OP_IGNORE_UNEXPECTED_EOF` constants (OpenSSL 3.0.0) .. diff --git a/Misc/NEWS.d/3.11.0a1.rst b/Misc/NEWS.d/3.11.0a1.rst index e5827e2f23b1f4..17cc1fa7cb7ac1 100644 --- a/Misc/NEWS.d/3.11.0a1.rst +++ b/Misc/NEWS.d/3.11.0a1.rst @@ -1468,8 +1468,8 @@ an installed expat library <= 2.2.0. On Unix, if the ``sem_clockwait()`` function is available in the C library (glibc 2.30 and newer), the :meth:`threading.Lock.acquire` method now uses -the monotonic clock (:data:`time.CLOCK_MONOTONIC`) for the timeout, rather -than using the system clock (:data:`time.CLOCK_REALTIME`), to not be +the monotonic clock (:const:`time.CLOCK_MONOTONIC`) for the timeout, rather +than using the system clock (:const:`time.CLOCK_REALTIME`), to not be affected by system clock changes. Patch by Victor Stinner. .. @@ -2087,8 +2087,8 @@ Upgrade bundled pip to 21.2.3 and setuptools to 57.4.0 .. section: Library Fix the :func:`os.set_inheritable` function on FreeBSD 14 for file -descriptor opened with the :data:`~os.O_PATH` flag: ignore the -:data:`~errno.EBADF` error on ``ioctl()``, fallback on the ``fcntl()`` +descriptor opened with the :const:`~os.O_PATH` flag: ignore the +:const:`~errno.EBADF` error on ``ioctl()``, fallback on the ``fcntl()`` implementation. Patch by Victor Stinner. .. diff --git a/Misc/NEWS.d/3.11.0a4.rst b/Misc/NEWS.d/3.11.0a4.rst index bcb6e8b7bdde31..3dd335929d655f 100644 --- a/Misc/NEWS.d/3.11.0a4.rst +++ b/Misc/NEWS.d/3.11.0a4.rst @@ -839,7 +839,7 @@ patch by Kumar Aditya. .. nonce: jeiPiX .. section: Library -Added :data:`signal.SIGSTKFLT` on platforms where this signal is defined. +Added :const:`signal.SIGSTKFLT` on platforms where this signal is defined. .. diff --git a/Misc/NEWS.d/3.11.0b1.rst b/Misc/NEWS.d/3.11.0b1.rst index d8c2ec0a799711..ad52bd14dbfd1d 100644 --- a/Misc/NEWS.d/3.11.0b1.rst +++ b/Misc/NEWS.d/3.11.0b1.rst @@ -817,8 +817,8 @@ it is ever needed and document the existing mechanism for ``posix_spawn()``. .. nonce: HFtERN .. section: Library -Fix :data:`signal.NSIG` value on FreeBSD to accept signal numbers greater -than 32, like :data:`signal.SIGRTMIN` and :data:`signal.SIGRTMAX`. Patch by +Fix :const:`signal.NSIG` value on FreeBSD to accept signal numbers greater +than 32, like :const:`signal.SIGRTMIN` and :const:`signal.SIGRTMAX`. Patch by Victor Stinner. .. diff --git a/Misc/NEWS.d/3.6.0rc1.rst b/Misc/NEWS.d/3.6.0rc1.rst index 15769f950db239..658f8c902d8704 100644 --- a/Misc/NEWS.d/3.6.0rc1.rst +++ b/Misc/NEWS.d/3.6.0rc1.rst @@ -69,8 +69,8 @@ supported. .. nonce: ilNIWN .. section: Library -Add new :data:`socket.TCP_CONGESTION` (Linux 2.6.13) and -:data:`socket.TCP_USER_TIMEOUT` (Linux 2.6.37) constants. Patch written by +Add new :const:`socket.TCP_CONGESTION` (Linux 2.6.13) and +:const:`socket.TCP_USER_TIMEOUT` (Linux 2.6.37) constants. Patch written by Omar Sandoval. .. diff --git a/Misc/NEWS.d/3.7.0a1.rst b/Misc/NEWS.d/3.7.0a1.rst index 24a1c4c83e646d..2a851fad5fb2e9 100644 --- a/Misc/NEWS.d/3.7.0a1.rst +++ b/Misc/NEWS.d/3.7.0a1.rst @@ -3274,7 +3274,7 @@ Added support for bytes paths in os.fwalk(). .. nonce: 37jMwb .. section: Library -Add new :data:`socket.TCP_NOTSENT_LOWAT` (Linux 3.12) constant. Patch by +Add new :const:`socket.TCP_NOTSENT_LOWAT` (Linux 3.12) constant. Patch by Nathaniel J. Smith. .. @@ -3871,8 +3871,8 @@ as an integer. Function only available on Android. .. nonce: ilNIWN .. section: Library -Add new :data:`socket.TCP_CONGESTION` (Linux 2.6.13) and -:data:`socket.TCP_USER_TIMEOUT` (Linux 2.6.37) constants. Patch written by +Add new :const:`socket.TCP_CONGESTION` (Linux 2.6.13) and +:const:`socket.TCP_USER_TIMEOUT` (Linux 2.6.37) constants. Patch written by Omar Sandoval. .. diff --git a/Misc/NEWS.d/3.7.0a3.rst b/Misc/NEWS.d/3.7.0a3.rst index 6576c1fadbff6d..92b0f328851208 100644 --- a/Misc/NEWS.d/3.7.0a3.rst +++ b/Misc/NEWS.d/3.7.0a3.rst @@ -754,8 +754,8 @@ now accepts characters as arguments. Based on patch by Steve Fink. .. nonce: DYQL0g .. section: Library -Add 3 new clock identifiers: :data:`time.CLOCK_BOOTTIME`, -:data:`time.CLOCK_PROF` and :data:`time.CLOCK_UPTIME`. +Add 3 new clock identifiers: :const:`time.CLOCK_BOOTTIME`, +:const:`time.CLOCK_PROF` and :const:`time.CLOCK_UPTIME`. .. diff --git a/Misc/NEWS.d/3.8.0a1.rst b/Misc/NEWS.d/3.8.0a1.rst index c9ca4b8404e67f..5f1a0142ae7781 100644 --- a/Misc/NEWS.d/3.8.0a1.rst +++ b/Misc/NEWS.d/3.8.0a1.rst @@ -1934,7 +1934,7 @@ failure. .. nonce: _ct_0H .. section: Library -The :data:`time.CLOCK_UPTIME_RAW` constant is now available for macOS 10.12. +The :const:`time.CLOCK_UPTIME_RAW` constant is now available for macOS 10.12. .. diff --git a/Misc/NEWS.d/3.8.0a4.rst b/Misc/NEWS.d/3.8.0a4.rst index d6fde41b7b11cd..17bbac6238913c 100644 --- a/Misc/NEWS.d/3.8.0a4.rst +++ b/Misc/NEWS.d/3.8.0a4.rst @@ -955,7 +955,7 @@ Add a new :mod:`_testinternalcapi` module to test the internal C API. .. section: Tests Fix ``test_imap4_host_default_value()`` of ``test_imaplib``: catch also -:data:`errno.ENETUNREACH` error. +:const:`errno.ENETUNREACH` error. .. diff --git a/Misc/NEWS.d/3.9.0a1.rst b/Misc/NEWS.d/3.9.0a1.rst index 5a929d481a3d55..97e07a643e59f8 100644 --- a/Misc/NEWS.d/3.9.0a1.rst +++ b/Misc/NEWS.d/3.9.0a1.rst @@ -1164,7 +1164,7 @@ defines them with eponymous methods. .. nonce: bmhquU .. section: Library -Add :data:`os.P_PIDFD` constant, which may be passed to :func:`os.waitid` to +Add :const:`os.P_PIDFD` constant, which may be passed to :func:`os.waitid` to wait on a Linux process file descriptor. .. @@ -1193,8 +1193,8 @@ Expose the Linux ``pidfd_open`` syscall as :func:`os.pidfd_open`. .. nonce: 7jvYFA .. section: Library -Added constants :data:`~fcntl.F_OFD_GETLK`, :data:`~fcntl.F_OFD_SETLK` and -:data:`~fcntl.F_OFD_SETLKW` to the :mod:`fcntl` module. Patch by Dong-hee +Added constants :const:`~fcntl.F_OFD_GETLK`, :const:`~fcntl.F_OFD_SETLK` and +:const:`~fcntl.F_OFD_SETLKW` to the :mod:`fcntl` module. Patch by Dong-hee Na. .. @@ -1283,7 +1283,7 @@ Fixed erroneous equality comparison in statistics.NormalDist(). .. nonce: 86ExWB .. section: Library -Added :data:`~os.CLD_KILLED` and :data:`~os.CLD_STOPPED` for +Added :const:`~os.CLD_KILLED` and :const:`~os.CLD_STOPPED` for :attr:`si_code`. Patch by Dong-hee Na. .. @@ -1355,8 +1355,8 @@ objects, patch by Samuel Colvin. .. nonce: 9w-IGF .. section: Library -Add missing :data:`stat.S_IFDOOR`, :data:`stat.S_IFPORT`, -:data:`stat.S_IFWHT`, :func:`stat.S_ISDOOR`, :func:`stat.S_ISPORT`, and +Add missing :const:`stat.S_IFDOOR`, :const:`stat.S_IFPORT`, +:const:`stat.S_IFWHT`, :func:`stat.S_ISDOOR`, :func:`stat.S_ISPORT`, and :func:`stat.S_ISWHT` values to the Python implementation of :mod:`stat`. .. @@ -4983,7 +4983,7 @@ set to CP_UTF7 or CP_UTF8. .. nonce: -0g2O3 .. section: Windows -Make :data:`winreg.REG_MULTI_SZ` support zero-length strings. +Make :const:`winreg.REG_MULTI_SZ` support zero-length strings. .. diff --git a/Misc/NEWS.d/3.9.0a6.rst b/Misc/NEWS.d/3.9.0a6.rst index af2cc7c3e97889..fec792a998bf94 100644 --- a/Misc/NEWS.d/3.9.0a6.rst +++ b/Misc/NEWS.d/3.9.0a6.rst @@ -680,7 +680,7 @@ child process, reset the lock to the unlocked state. Rename also the private .. nonce: kIjVge .. section: Library -Expose :data:`~socket.CAN_RAW_JOIN_FILTERS` in the :mod:`socket` module. +Expose :const:`~socket.CAN_RAW_JOIN_FILTERS` in the :mod:`socket` module. .. @@ -735,7 +735,7 @@ number of groups. For other implementations, double the group list size. .. nonce: HFpHZS .. section: Library -Add :data:`time.CLOCK_TAI` constant if the operating system support it. +Add :const:`time.CLOCK_TAI` constant if the operating system support it. .. From 4f7c23e4dfb4b932b3916f8f79895e2a1bf3ae43 Mon Sep 17 00:00:00 2001 From: Sebastiaan Zeeff <33516116+SebastiaanZ@users.noreply.github.com> Date: Sat, 22 Jul 2023 10:37:16 +0200 Subject: [PATCH 028/632] [3.11] Change non-integral to non-integer in random docs (GH-106975) (#106985) --- Doc/library/random.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/random.rst b/Doc/library/random.rst index 3109343a3c52df..4af4f83e280ca7 100644 --- a/Doc/library/random.rst +++ b/Doc/library/random.rst @@ -141,7 +141,7 @@ Functions for integers ``randrange(10)``. In the future, this will raise a :exc:`TypeError`. .. deprecated:: 3.10 - The exception raised for non-integral values such as ``randrange(10.5)`` + The exception raised for non-integer values such as ``randrange(10.5)`` or ``randrange('10')`` will be changed from :exc:`ValueError` to :exc:`TypeError`. From 06f8a439956fdf5f2d68bc3b5b6e2499981ce21f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Sat, 22 Jul 2023 12:15:18 +0000 Subject: [PATCH 029/632] [3.11] gh-106368: Increase coverage for Argument Clinic output directive (GH-106979) (#107002) (cherry picked from commit ee5c01b473eeadb007b9f330db3143e34e46038b) Co-authored-by: Erlend E. Aasland --- Lib/test/test_clinic.py | 62 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index 605d101b5bef90..6520966b345d18 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -249,6 +249,68 @@ def test_cpp_monitor_fail_no_matching_if(self): out = self.expect_failure(raw) self.assertEqual(out, msg) + def test_directive_output_unknown_preset(self): + out = self.expect_failure(""" + /*[clinic input] + output preset nosuchpreset + [clinic start generated code]*/ + """) + msg = "Unknown preset 'nosuchpreset'" + self.assertIn(msg, out) + + def test_directive_output_cant_pop(self): + out = self.expect_failure(""" + /*[clinic input] + output pop + [clinic start generated code]*/ + """) + msg = "Can't 'output pop', stack is empty" + self.assertIn(msg, out) + + def test_directive_output_print(self): + raw = dedent(""" + /*[clinic input] + output print 'I told you once.' + [clinic start generated code]*/ + """) + out = self.clinic.parse(raw) + # The generated output will differ for every run, but we can check that + # it starts with the clinic block, we check that it contains all the + # expected fields, and we check that it contains the checksum line. + self.assertTrue(out.startswith(dedent(""" + /*[clinic input] + output print 'I told you once.' + [clinic start generated code]*/ + """))) + fields = { + "cpp_endif", + "cpp_if", + "docstring_definition", + "docstring_prototype", + "impl_definition", + "impl_prototype", + "methoddef_define", + "methoddef_ifndef", + "parser_definition", + "parser_prototype", + } + for field in fields: + with self.subTest(field=field): + self.assertIn(field, out) + last_line = out.rstrip().split("\n")[-1] + self.assertTrue( + last_line.startswith("/*[clinic end generated code: output=") + ) + + def test_unknown_destination_command(self): + out = self.expect_failure(""" + /*[clinic input] + destination buffer nosuchcommand + [clinic start generated code]*/ + """) + msg = "unknown destination command 'nosuchcommand'" + self.assertIn(msg, out) + class ClinicGroupPermuterTest(TestCase): def _test(self, l, m, r, output): From a7a973e9c7c50d37681ebfaf67a44f4d08c202df Mon Sep 17 00:00:00 2001 From: Oliver Rew <12968411+oliver-rew@users.noreply.github.com> Date: Sat, 22 Jul 2023 14:43:43 +0200 Subject: [PATCH 030/632] [3.11] gh-100700 Remove Date and Release fields in past whatsnews (GH-100728) (#106999) Co-authored-by: Zachary Ware --- Doc/whatsnew/3.11.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index c437c5bfb6120f..f31b439ed2fd4a 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -2,8 +2,6 @@ What's New In Python 3.11 **************************** -:Release: |release| -:Date: |today| :Editor: Pablo Galindo Salgado .. Rules for maintenance: From c65ca7097aeea267a57c516707b6d1095870c6f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Sat, 22 Jul 2023 14:14:24 +0000 Subject: [PATCH 031/632] [3.11] Reformat code block to make it easier to read (GH-106965) (#107022) (cherry picked from commit ed491d9f782480fb00535abcf667027e0e323287) Co-authored-by: Joe Kaufeld --- Doc/tutorial/errors.rst | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/Doc/tutorial/errors.rst b/Doc/tutorial/errors.rst index 6419ff621f1b31..8a207c385c6ab7 100644 --- a/Doc/tutorial/errors.rst +++ b/Doc/tutorial/errors.rst @@ -535,11 +535,20 @@ of a certain type while letting all other exceptions propagate to other clauses and eventually to be reraised. :: >>> def f(): - ... raise ExceptionGroup("group1", - ... [OSError(1), - ... SystemError(2), - ... ExceptionGroup("group2", - ... [OSError(3), RecursionError(4)])]) + ... raise ExceptionGroup( + ... "group1", + ... [ + ... OSError(1), + ... SystemError(2), + ... ExceptionGroup( + ... "group2", + ... [ + ... OSError(3), + ... RecursionError(4) + ... ] + ... ) + ... ] + ... ) ... >>> try: ... f() From 755166e864e7ff451c2a30b7cdc3a68cd4e9ec3d Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 22 Jul 2023 07:29:54 -0700 Subject: [PATCH 032/632] [3.11] gh-105090: Replace incorrect TLSv1.2 with TLSv1.3 (GH-105404) (#107038) Co-authored-by: Jocelyn Castellano --- Doc/library/ssl.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index a0936747db5a08..fe896be2faeb5d 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -2688,7 +2688,7 @@ disabled by default. >>> client_context.maximum_version = ssl.TLSVersion.TLSv1_3 -The SSL context created above will only allow TLSv1.2 and later (if +The SSL context created above will only allow TLSv1.3 and later (if supported by your system) connections to a server. :const:`PROTOCOL_TLS_CLIENT` implies certificate validation and hostname checks by default. You have to load certificates into the context. From c332117b522df8b7aaf5cde81f965405dbb4ae7a Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 22 Jul 2023 09:23:31 -0700 Subject: [PATCH 033/632] gh-106847: Add -X warn_default_encoding in sys.flags Doc (GH-106854) gh-106847: Add -X warn_default_encoding in sys.flags Doc (GH-106854) (cherry picked from commit fd84ac0ee0a8d5e34e0a106eed7e50539b61c5f8) Co-authored-by: qqwqqw689 <114795525+qqwqqw689@users.noreply.github.com> Co-authored-by: Nikita Sobolev --- Doc/library/sys.rst | 46 ++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 718dc48e804c01..dca35cc731b695 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -511,27 +511,28 @@ always available. The :term:`named tuple` *flags* exposes the status of command line flags. The attributes are read only. - ============================= ============================================================================================================== - attribute flag - ============================= ============================================================================================================== - :const:`debug` :option:`-d` - :const:`inspect` :option:`-i` - :const:`interactive` :option:`-i` - :const:`isolated` :option:`-I` - :const:`optimize` :option:`-O` or :option:`-OO` - :const:`dont_write_bytecode` :option:`-B` - :const:`no_user_site` :option:`-s` - :const:`no_site` :option:`-S` - :const:`ignore_environment` :option:`-E` - :const:`verbose` :option:`-v` - :const:`bytes_warning` :option:`-b` - :const:`quiet` :option:`-q` - :const:`hash_randomization` :option:`-R` - :const:`dev_mode` :option:`-X dev <-X>` (:ref:`Python Development Mode `) - :const:`utf8_mode` :option:`-X utf8 <-X>` - :const:`safe_path` :option:`-P` - :const:`int_max_str_digits` :option:`-X int_max_str_digits <-X>` (:ref:`integer string conversion length limitation `) - ============================= ============================================================================================================== + ============================== ============================================================================================================== + attribute flag + ============================== ============================================================================================================== + :const:`debug` :option:`-d` + :const:`inspect` :option:`-i` + :const:`interactive` :option:`-i` + :const:`isolated` :option:`-I` + :const:`optimize` :option:`-O` or :option:`-OO` + :const:`dont_write_bytecode` :option:`-B` + :const:`no_user_site` :option:`-s` + :const:`no_site` :option:`-S` + :const:`ignore_environment` :option:`-E` + :const:`verbose` :option:`-v` + :const:`bytes_warning` :option:`-b` + :const:`quiet` :option:`-q` + :const:`hash_randomization` :option:`-R` + :const:`dev_mode` :option:`-X dev <-X>` (:ref:`Python Development Mode `) + :const:`utf8_mode` :option:`-X utf8 <-X>` + :const:`safe_path` :option:`-P` + :const:`int_max_str_digits` :option:`-X int_max_str_digits <-X>` (:ref:`integer string conversion length limitation `) + :const:`warn_default_encoding` :option:`-X warn_default_encoding <-X>` + ============================== ============================================================================================================== .. versionchanged:: 3.2 Added ``quiet`` attribute for the new :option:`-q` flag. @@ -550,6 +551,9 @@ always available. Mode ` and the ``utf8_mode`` attribute for the new :option:`-X` ``utf8`` flag. + .. versionchanged:: 3.10 + Added ``warn_default_encoding`` attribute for :option:`-X` ``warn_default_encoding`` flag. + .. versionchanged:: 3.11 Added the ``safe_path`` attribute for :option:`-P` option. From b71144193b5e7aec022a946a2e36f902280d7c30 Mon Sep 17 00:00:00 2001 From: wulmer Date: Sat, 22 Jul 2023 18:39:46 +0200 Subject: [PATCH 034/632] [3.11] Fix Sphinx warnings in `re` module docs (GH-107044). (#107055) (cherry picked from commit 149748ea4f552e6fe43a1d6d69bd65910a7c4813) --- Doc/library/re.rst | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/Doc/library/re.rst b/Doc/library/re.rst index 1f42cd28e65ec5..798b56aa5d0c35 100644 --- a/Doc/library/re.rst +++ b/Doc/library/re.rst @@ -500,6 +500,8 @@ The special characters are: (``b'\x00'``-``b'\x7f'``) in :class:`bytes` replacement strings. +.. _re-special-sequences: + The special sequences consist of ``'\'`` and a character from the list below. If the ordinary character is not an ASCII digit or an ASCII letter, then the resulting RE will match the second character. For example, ``\$`` matches the @@ -778,6 +780,17 @@ Flags Corresponds to the inline flag ``(?s)``. +.. data:: U + UNICODE + + In Python 2, this flag made :ref:`special sequences ` + include Unicode characters in matches. Since Python 3, Unicode characters + are matched by default. + + See :const:`A` for restricting matching on ASCII characters instead. + + This flag is only kept for backward compatibility. + .. data:: X VERBOSE @@ -1518,14 +1531,14 @@ Simulating scanf() .. index:: single: scanf() -Python does not currently have an equivalent to :c:func:`scanf`. Regular +Python does not currently have an equivalent to :c:func:`!scanf`. Regular expressions are generally more powerful, though also more verbose, than -:c:func:`scanf` format strings. The table below offers some more-or-less -equivalent mappings between :c:func:`scanf` format tokens and regular +:c:func:`!scanf` format strings. The table below offers some more-or-less +equivalent mappings between :c:func:`!scanf` format tokens and regular expressions. +--------------------------------+---------------------------------------------+ -| :c:func:`scanf` Token | Regular Expression | +| :c:func:`!scanf` Token | Regular Expression | +================================+=============================================+ | ``%c`` | ``.`` | +--------------------------------+---------------------------------------------+ @@ -1550,7 +1563,7 @@ To extract the filename and numbers from a string like :: /usr/sbin/sendmail - 0 errors, 4 warnings -you would use a :c:func:`scanf` format like :: +you would use a :c:func:`!scanf` format like :: %s - %d errors, %d warnings From 63c945a3f540a30288f21cb730a9da8bd4bf0ab6 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sat, 22 Jul 2023 19:44:11 +0200 Subject: [PATCH 035/632] [3.11] gh-106970: Fix Argument Clinic 'destination clear' command (#106972) (#107059) Add test for the 'destination clear' command, and the 'destination' directive in general. Fix two bugs in 'destination clear' command: 1. The text attribute of the allocator is called 'text', not '_text' 2. Return after processing the 'clear' command, instead of proceeding directly to the fail(). (cherry picked from commit 3372bcba9893030e4063a9264ec0b4d1b6166883) --- Lib/test/clinic.test.c | 56 +++++++++++++++++++ ...-07-21-23-16-05.gh-issue-106970.NLRnml.rst | 4 ++ Tools/clinic/clinic.py | 23 +++++--- 3 files changed, 74 insertions(+), 9 deletions(-) create mode 100644 Misc/NEWS.d/next/Tools-Demos/2023-07-21-23-16-05.gh-issue-106970.NLRnml.rst diff --git a/Lib/test/clinic.test.c b/Lib/test/clinic.test.c index e4d02f83bc636c..feb30458b462ac 100644 --- a/Lib/test/clinic.test.c +++ b/Lib/test/clinic.test.c @@ -4192,3 +4192,59 @@ Test_meth_coexist(TestObj *self, PyObject *Py_UNUSED(ignored)) static PyObject * Test_meth_coexist_impl(TestObj *self) /*[clinic end generated code: output=808a293d0cd27439 input=2a1d75b5e6fec6dd]*/ + + +/*[clinic input] +output push +output preset buffer +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=5bff3376ee0df0b5]*/ + +/*[clinic input] +buffer_clear + a: int +We'll call 'destination buffer clear' after this. + +Argument Clinic's buffer preset puts most generated code into the +'buffer' destination, except from 'impl_definition', which is put into +the 'block' destination, so we should expect everything but +'impl_definition' to be cleared. +[clinic start generated code]*/ + +static PyObject * +buffer_clear_impl(PyObject *module, int a) +/*[clinic end generated code: output=f14bba74677e1846 input=a4c308a6fdab043c]*/ + +/*[clinic input] +destination buffer clear +output pop +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=f20d06adb8252084]*/ + + +/*[clinic input] +output push +destination test1 new buffer +output everything suppress +output docstring_definition test1 +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=5a77c454970992fc]*/ + +/*[clinic input] +new_dest + a: int +Only this docstring should be outputted to test1. +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=da5af421ed8996ed]*/ + +/*[clinic input] +dump test1 +output pop +[clinic start generated code]*/ + +PyDoc_STRVAR(new_dest__doc__, +"new_dest($module, /, a)\n" +"--\n" +"\n" +"Only this docstring should be outputted to test1."); +/*[clinic end generated code: output=9cac703f51d90e84 input=090db8df4945576d]*/ diff --git a/Misc/NEWS.d/next/Tools-Demos/2023-07-21-23-16-05.gh-issue-106970.NLRnml.rst b/Misc/NEWS.d/next/Tools-Demos/2023-07-21-23-16-05.gh-issue-106970.NLRnml.rst new file mode 100644 index 00000000000000..194e3351b0470c --- /dev/null +++ b/Misc/NEWS.d/next/Tools-Demos/2023-07-21-23-16-05.gh-issue-106970.NLRnml.rst @@ -0,0 +1,4 @@ +Fix bugs in the Argument Clinic ``destination clear`` command; the +destination buffers would never be cleared, and the ``destination`` +directive parser would simply continue to the fault handler after processing +the command. Patch by Erlend E. Aasland. diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index b18c0205a8559f..f8e89bc74f1773 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -1798,7 +1798,7 @@ def __getitem__(self, i): def clear(self): for ta in self._array: - ta._text.clear() + ta.text.clear() def dump(self): texts = [ta.output() for ta in self._array] @@ -4121,14 +4121,19 @@ def directive_set(self, name, value): self.clinic.__dict__[name] = value - def directive_destination(self, name, command, *args): - if command == 'new': - self.clinic.add_destination(name, *args) - return - - if command == 'clear': - self.clinic.get_destination(name).clear() - fail("unknown destination command", repr(command)) + def directive_destination( + self, + name: str, + command: str, + *args + ) -> None: + match command: + case "new": + self.clinic.add_destination(name, *args) + case "clear": + self.clinic.get_destination(name).clear() + case _: + fail("unknown destination command", repr(command)) def directive_output(self, command_or_name, destination=''): From e5d2a19ac5ad9d1130d778c960c4b966c32cac72 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 22 Jul 2023 11:13:38 -0700 Subject: [PATCH 036/632] [3.11] gh-107008: Document the curses module variables LINES and COLS (GH-107011) (GH-107058) LINES and COLS referred in curses.update_lines_cols() documentations are the module variables, not the environment variables. (cherry picked from commit 26e08dfdd7ac1b3d567d30cd35e4898121580390) Co-authored-by: Serhiy Storchaka --- Doc/library/curses.rst | 20 ++++++++++++++++++- Doc/whatsnew/3.5.rst | 4 ++-- ...-07-22-15-14-13.gh-issue-107008.3JQ1Vt.rst | 2 ++ 3 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Documentation/2023-07-22-15-14-13.gh-issue-107008.3JQ1Vt.rst diff --git a/Doc/library/curses.rst b/Doc/library/curses.rst index 44dd9d32f148e8..e0bc462fb1c958 100644 --- a/Doc/library/curses.rst +++ b/Doc/library/curses.rst @@ -644,7 +644,8 @@ The module :mod:`curses` defines the following functions: .. function:: update_lines_cols() - Update :envvar:`LINES` and :envvar:`COLS`. Useful for detecting manual screen resize. + Update the :const:`LINES` and :const:`COLS` module variables. + Useful for detecting manual screen resize. .. versionadded:: 3.5 @@ -1345,10 +1346,27 @@ The :mod:`curses` module defines the following data members: .. data:: COLORS The maximum number of colors the terminal can support. + It is defined only after the call to :func:`start_color`. .. data:: COLOR_PAIRS The maximum number of color pairs the terminal can support. + It is defined only after the call to :func:`start_color`. + +.. data:: COLS + + The width of the screen, i.e., the number of columns. + It is defined only after the call to :func:`initscr`. + Updated by :func:`update_lines_cols`, :func:`resizeterm` and + :func:`resize_term`. + +.. data:: LINES + + The height of the screen, i.e., the number of lines. + It is defined only after the call to :func:`initscr`. + Updated by :func:`update_lines_cols`, :func:`resizeterm` and + :func:`resize_term`. + Some constants are available to specify character cell attributes. The exact constants available are system dependent. diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst index 23d2f6562aab86..69d9147524caed 100644 --- a/Doc/whatsnew/3.5.rst +++ b/Doc/whatsnew/3.5.rst @@ -1045,8 +1045,8 @@ not just sequences. (Contributed by Serhiy Storchaka in :issue:`23171`.) curses ------ -The new :func:`~curses.update_lines_cols` function updates the :envvar:`LINES` -and :envvar:`COLS` environment variables. This is useful for detecting +The new :func:`~curses.update_lines_cols` function updates the :data:`LINES` +and :data:`COLS` module variables. This is useful for detecting manual screen resizing. (Contributed by Arnon Yaari in :issue:`4254`.) diff --git a/Misc/NEWS.d/next/Documentation/2023-07-22-15-14-13.gh-issue-107008.3JQ1Vt.rst b/Misc/NEWS.d/next/Documentation/2023-07-22-15-14-13.gh-issue-107008.3JQ1Vt.rst new file mode 100644 index 00000000000000..a0fa27ec10303e --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2023-07-22-15-14-13.gh-issue-107008.3JQ1Vt.rst @@ -0,0 +1,2 @@ +Document the :mod:`curses` module variables :const:`~curses.LINES` and +:const:`~curses.COLS`. From 2895dbd0f661566829d6dc4eadabb46d0395fde9 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 22 Jul 2023 11:25:15 -0700 Subject: [PATCH 037/632] [3.11] gh-83006: Document behavior of `shutil.disk_usage` for non-mounted filesystems on Unix (GH-107031) (#107048) (cherry picked from commit 6e5f2235f3754307292c7d8d3698958136b5e311) Co-authored-by: Matthieu Caneill --- Doc/library/shutil.rst | 6 ++++++ .../Library/2023-07-22-15-51-33.gh-issue-83006.21zaCz.rst | 2 ++ 2 files changed, 8 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2023-07-22-15-51-33.gh-issue-83006.21zaCz.rst diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst index 26a5cd531fa14f..dcf421f162b15a 100644 --- a/Doc/library/shutil.rst +++ b/Doc/library/shutil.rst @@ -393,6 +393,12 @@ Directory and files operations total, used and free space, in bytes. *path* may be a file or a directory. + .. note:: + + On Unix filesystems, *path* must point to a path within a **mounted** + filesystem partition. On those platforms, CPython doesn't attempt to + retrieve disk usage information from non-mounted filesystems. + .. versionadded:: 3.3 .. versionchanged:: 3.8 diff --git a/Misc/NEWS.d/next/Library/2023-07-22-15-51-33.gh-issue-83006.21zaCz.rst b/Misc/NEWS.d/next/Library/2023-07-22-15-51-33.gh-issue-83006.21zaCz.rst new file mode 100644 index 00000000000000..e64d1860828430 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-07-22-15-51-33.gh-issue-83006.21zaCz.rst @@ -0,0 +1,2 @@ +Document behavior of :func:`shutil.disk_usage` for non-mounted filesystems +on Unix. From 9875b177a9ef6d062eee65a501621743e443f82e Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 22 Jul 2023 14:12:06 -0700 Subject: [PATCH 038/632] [3.11] gh-107028: tiny textual changes in logging docs and docstrings (GH-107029) (GH-107066) (cherry picked from commit 5e5a34ac3a827e040cd89426b1774fec2123336a) --- Doc/library/logging.handlers.rst | 9 +++++---- Lib/logging/handlers.py | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Doc/library/logging.handlers.rst b/Doc/library/logging.handlers.rst index 7f638251602c72..d1566957e7ed14 100644 --- a/Doc/library/logging.handlers.rst +++ b/Doc/library/logging.handlers.rst @@ -917,8 +917,9 @@ should, then :meth:`flush` is expected to do the flushing. .. method:: flush() - You can override this to implement custom flushing behavior. This version - just zaps the buffer to empty. + For a :class:`BufferingHandler` instance, flushing means that it sets the + buffer to an empty list. This method can be overwritten to implement more useful + flushing behavior. .. method:: shouldFlush(record) @@ -950,9 +951,9 @@ should, then :meth:`flush` is expected to do the flushing. .. method:: flush() - For a :class:`MemoryHandler`, flushing means just sending the buffered + For a :class:`MemoryHandler` instance, flushing means just sending the buffered records to the target, if there is one. The buffer is also cleared when - this happens. Override if you want different behavior. + buffered records are sent to the target. Override if you want different behavior. .. method:: setTarget(target) diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py index f5a9760fd496b6..eadd1b6340bcb6 100644 --- a/Lib/logging/handlers.py +++ b/Lib/logging/handlers.py @@ -1399,7 +1399,7 @@ def flush(self): records to the target, if there is one. Override if you want different behaviour. - The record buffer is also cleared by this operation. + The record buffer is only cleared if a target has been set. """ self.acquire() try: From 7d414873b3a020cec318d53cc1bb62ccdac061bd Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 23 Jul 2023 02:11:35 -0700 Subject: [PATCH 039/632] [3.11] gh-107017: removed mention that C does it the same way (GH-107020) (#107098) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jakub Červinka --- Doc/tutorial/controlflow.rst | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/Doc/tutorial/controlflow.rst b/Doc/tutorial/controlflow.rst index e140f51f1dda78..138d87f892e891 100644 --- a/Doc/tutorial/controlflow.rst +++ b/Doc/tutorial/controlflow.rst @@ -4,8 +4,8 @@ More Control Flow Tools *********************** -Besides the :keyword:`while` statement just introduced, Python uses the usual -flow control statements known from other languages, with some twists. +As well as the :keyword:`while` statement just introduced, Python uses a few more +that we will encounter in this chapter. .. _tut-if: @@ -163,14 +163,21 @@ arguments. In chapter :ref:`tut-structures`, we will discuss in more detail abo :keyword:`!break` and :keyword:`!continue` Statements, and :keyword:`!else` Clauses on Loops ============================================================================================ -The :keyword:`break` statement, like in C, breaks out of the innermost enclosing +The :keyword:`break` statement breaks out of the innermost enclosing :keyword:`for` or :keyword:`while` loop. -Loop statements may have an :keyword:`!else` clause; it is executed when the loop -terminates through exhaustion of the iterable (with :keyword:`for`) or when the -condition becomes false (with :keyword:`while`), but not when the loop is -terminated by a :keyword:`break` statement. This is exemplified by the -following loop, which searches for prime numbers:: +A :keyword:`!for` or :keyword:`!while` loop can include an :keyword:`!else` clause. + +In a :keyword:`for` loop, the :keyword:`!else` clause is executed +after the loop reaches its final iteration. + +In a :keyword:`while` loop, it's executed after the loop's condition becomes false. + +In either kind of loop, the :keyword:`!else` clause is **not** executed +if the loop was terminated by a :keyword:`break`. + +This is exemplified in the following :keyword:`!for` loop, +which searches for prime numbers:: >>> for n in range(2, 10): ... for x in range(2, n): From 5575b1204b9870301f2e5797a8535576487b5800 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 23 Jul 2023 02:12:52 -0700 Subject: [PATCH 040/632] [3.11] gh-71261: Add paragraph on shadowing submodules with star imports (GH-107004) (#107099) Co-authored-by: wulmer --- Doc/tutorial/modules.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Doc/tutorial/modules.rst b/Doc/tutorial/modules.rst index 3bd034bcc9703f..734dd1cfe6871a 100644 --- a/Doc/tutorial/modules.rst +++ b/Doc/tutorial/modules.rst @@ -512,6 +512,22 @@ code:: This would mean that ``from sound.effects import *`` would import the three named submodules of the :mod:`sound.effects` package. +Be aware that submodules might become shadowed by locally defined names. For +example, if you added a ``reverse`` function to the +:file:`sound/effects/__init__.py` file, the ``from sound.effects import *`` +would only import the two submodules ``echo`` and ``surround``, but *not* the +``reverse`` submodule, because it is shadowed by the locally defined +``reverse`` function:: + + __all__ = [ + "echo", # refers to the 'echo.py' file + "surround", # refers to the 'surround.py' file + "reverse", # !!! refers to the 'reverse' function now !!! + ] + + def reverse(msg: str): # <-- this name shadows the 'reverse.py' submodule + return msg[::-1] # in the case of a 'from sound.effects import *' + If ``__all__`` is not defined, the statement ``from sound.effects import *`` does *not* import all submodules from the package :mod:`sound.effects` into the current namespace; it only ensures that the package :mod:`sound.effects` has From 65e40aaed0567830d8ec88769c27321f3504cd80 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 23 Jul 2023 02:23:51 -0700 Subject: [PATCH 041/632] [3.11] gh-54738: Add argparse i18n howto (GH-104562) (#107101) (cherry picked from commit dcd7acb04a719d8d30c8d03b80d3d48b6c035e14) Co-authored-by: Tomas R --- Doc/howto/argparse.rst | 53 +++++++++++++++++++ Doc/library/gettext.rst | 1 + ...3-05-16-22-08-24.gh-issue-54738.mJvCnj.rst | 1 + 3 files changed, 55 insertions(+) create mode 100644 Misc/NEWS.d/next/Documentation/2023-05-16-22-08-24.gh-issue-54738.mJvCnj.rst diff --git a/Doc/howto/argparse.rst b/Doc/howto/argparse.rst index 9ea140c41a09c7..06a301a120c1c7 100644 --- a/Doc/howto/argparse.rst +++ b/Doc/howto/argparse.rst @@ -759,6 +759,59 @@ but not both at the same time: -q, --quiet +How to translate the argparse output +==================================== + +The output of the :mod:`argparse` module such as its help text and error +messages are all made translatable using the :mod:`gettext` module. This +allows applications to easily localize messages produced by +:mod:`argparse`. See also :ref:`i18n-howto`. + +For instance, in this :mod:`argparse` output: + +.. code-block:: shell-session + + $ python prog.py --help + usage: prog.py [-h] [-v | -q] x y + + calculate X to the power of Y + + positional arguments: + x the base + y the exponent + + options: + -h, --help show this help message and exit + -v, --verbose + -q, --quiet + +The strings ``usage:``, ``positional arguments:``, ``options:`` and +``show this help message and exit`` are all translatable. + +In order to translate these strings, they must first be extracted +into a ``.po`` file. For example, using `Babel `__, +run this command: + +.. code-block:: shell-session + + $ pybabel extract -o messages.po /usr/lib/python3.12/argparse.py + +This command will extract all translatable strings from the :mod:`argparse` +module and output them into a file named ``messages.po``. This command assumes +that your Python installation is in ``/usr/lib``. + +You can find out the location of the :mod:`argparse` module on your system +using this script:: + + import argparse + print(argparse.__file__) + +Once the messages in the ``.po`` file are translated and the translations are +installed using :mod:`gettext`, :mod:`argparse` will be able to display the +translated messages. + +To translate your own strings in the :mod:`argparse` output, use :mod:`gettext`. + Conclusion ========== diff --git a/Doc/library/gettext.rst b/Doc/library/gettext.rst index 747f8703b750ec..88a65b980d310f 100644 --- a/Doc/library/gettext.rst +++ b/Doc/library/gettext.rst @@ -411,6 +411,7 @@ One difference between this module and Henstridge's: his catalog objects supported access through a mapping API, but this appears to be unused and so is not currently supported. +.. _i18n-howto: Internationalizing your programs and modules -------------------------------------------- diff --git a/Misc/NEWS.d/next/Documentation/2023-05-16-22-08-24.gh-issue-54738.mJvCnj.rst b/Misc/NEWS.d/next/Documentation/2023-05-16-22-08-24.gh-issue-54738.mJvCnj.rst new file mode 100644 index 00000000000000..4da58fc982b6d7 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2023-05-16-22-08-24.gh-issue-54738.mJvCnj.rst @@ -0,0 +1 @@ +Add documentation on how to localize the :mod:`argparse` module. From ec8718dd7c318202723ceeee9479f3824f3c019c Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 23 Jul 2023 12:24:37 +0300 Subject: [PATCH 042/632] [3.11] gh-106948: Add standard external names to nitpick_ignore (GH-106949) (#107061) It includes standard C types, macros and variables like "size_t", "LONG_MAX" and "errno", and standard environment variables like "PATH".. (cherry picked from commit f8b7fe2f2647813ae8249675a80e59c117d30fe1) --- Doc/c-api/arg.rst | 2 +- Doc/c-api/memory.rst | 2 +- Doc/c-api/unicode.rst | 14 ++--- Doc/c-api/veryhigh.rst | 2 +- Doc/conf.py | 52 +++++++++++++++++++ Doc/library/array.rst | 4 +- Doc/library/ctypes.rst | 10 ++-- Doc/library/os.rst | 2 +- Doc/library/struct.rst | 4 +- Doc/library/venv.rst | 6 +-- Doc/whatsnew/3.3.rst | 2 +- Doc/whatsnew/3.5.rst | 2 +- Doc/whatsnew/3.9.rst | 4 +- Misc/NEWS.d/3.10.0a5.rst | 2 +- ...-07-21-11-51-57.gh-issue-106948.K_JQ7j.rst | 1 + 15 files changed, 81 insertions(+), 28 deletions(-) create mode 100644 Misc/NEWS.d/next/Documentation/2023-07-21-11-51-57.gh-issue-106948.K_JQ7j.rst diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst index ee130ab28510b0..9f7fd2fa8629e6 100644 --- a/Doc/c-api/arg.rst +++ b/Doc/c-api/arg.rst @@ -584,7 +584,7 @@ Building values Same as ``s#``. ``u`` (:class:`str`) [const wchar_t \*] - Convert a null-terminated :c:expr:`wchar_t` buffer of Unicode (UTF-16 or UCS-4) + Convert a null-terminated :c:type:`wchar_t` buffer of Unicode (UTF-16 or UCS-4) data to a Python Unicode object. If the Unicode buffer pointer is ``NULL``, ``None`` is returned. diff --git a/Doc/c-api/memory.rst b/Doc/c-api/memory.rst index 35c356f25c6c70..4ca3b8804427f8 100644 --- a/Doc/c-api/memory.rst +++ b/Doc/c-api/memory.rst @@ -581,7 +581,7 @@ that the treatment of negative indices differs from a Python slice): default). A serial number, incremented by 1 on each call to a malloc-like or - realloc-like function. Big-endian ``size_t``. If "bad memory" is detected + realloc-like function. Big-endian :c:type:`size_t`. If "bad memory" is detected later, the serial number gives an excellent way to set a breakpoint on the next run, to capture the instant at which this block was passed out. The static function bumpserialno() in obmalloc.c is the only place the serial diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst index b2241ea8bd2e1b..a96e0738f716e2 100644 --- a/Doc/c-api/unicode.rst +++ b/Doc/c-api/unicode.rst @@ -58,7 +58,7 @@ Python: .. c:type:: Py_UNICODE - This is a typedef of :c:expr:`wchar_t`, which is a 16-bit type or 32-bit type + This is a typedef of :c:type:`wchar_t`, which is a 16-bit type or 32-bit type depending on the platform. .. versionchanged:: 3.3 @@ -935,11 +935,11 @@ conversion function: wchar_t Support """"""""""""""" -:c:expr:`wchar_t` support for platforms which support it: +:c:type:`wchar_t` support for platforms which support it: .. c:function:: PyObject* PyUnicode_FromWideChar(const wchar_t *w, Py_ssize_t size) - Create a Unicode object from the :c:expr:`wchar_t` buffer *w* of the given *size*. + Create a Unicode object from the :c:type:`wchar_t` buffer *w* of the given *size*. Passing ``-1`` as the *size* indicates that the function must itself compute the length, using wcslen. Return ``NULL`` on failure. @@ -947,9 +947,9 @@ wchar_t Support .. c:function:: Py_ssize_t PyUnicode_AsWideChar(PyObject *unicode, wchar_t *w, Py_ssize_t size) - Copy the Unicode object contents into the :c:expr:`wchar_t` buffer *w*. At most - *size* :c:expr:`wchar_t` characters are copied (excluding a possibly trailing - null termination character). Return the number of :c:expr:`wchar_t` characters + Copy the Unicode object contents into the :c:type:`wchar_t` buffer *w*. 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*` string may or may not be null-terminated. It is the responsibility of the caller to make sure that the :c:expr:`wchar_t*` string is null-terminated in case this is @@ -963,7 +963,7 @@ wchar_t Support Convert the Unicode object to a wide character string. The output string always ends with a null character. If *size* is not ``NULL``, write the number of wide characters (excluding the trailing null termination character) into - *\*size*. Note that the resulting :c:expr:`wchar_t` string might contain + *\*size*. Note that the resulting :c:type:`wchar_t` string might contain null characters, which would cause the string to be truncated when used with most C functions. If *size* is ``NULL`` and the :c:expr:`wchar_t*` string contains null characters a :exc:`ValueError` is raised. diff --git a/Doc/c-api/veryhigh.rst b/Doc/c-api/veryhigh.rst index cfbb44f4eb3a67..79515c9df07edb 100644 --- a/Doc/c-api/veryhigh.rst +++ b/Doc/c-api/veryhigh.rst @@ -17,7 +17,7 @@ parameter. The available start symbols are :c:data:`Py_eval_input`, following the functions which accept them as parameters. Note also that several of these functions take :c:expr:`FILE*` parameters. One -particular issue which needs to be handled carefully is that the :c:expr:`FILE` +particular issue which needs to be handled carefully is that the :c:type:`FILE` structure for different C libraries can be different and incompatible. Under Windows (at least), it is possible for dynamically linked extensions to actually use different libraries, so care should be taken that :c:expr:`FILE*` parameters diff --git a/Doc/conf.py b/Doc/conf.py index 3bd828f8ce9ed7..c082717efda723 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -72,6 +72,58 @@ exclude_patterns.append(venvdir + '/*') nitpick_ignore = [ + # Standard C types + ('c:type', 'FILE'), + ('c:type', '__int'), + ('c:type', 'intmax_t'), + ('c:type', 'off_t'), + ('c:type', 'ptrdiff_t'), + ('c:type', 'siginfo_t'), + ('c:type', 'size_t'), + ('c:type', 'ssize_t'), + ('c:type', 'time_t'), + ('c:type', 'uintmax_t'), + ('c:type', 'va_list'), + ('c:type', 'wchar_t'), + # Standard C macros + ('c:macro', 'LLONG_MAX'), + ('c:macro', 'LLONG_MIN'), + ('c:macro', 'LONG_MAX'), + ('c:macro', 'LONG_MIN'), + # Standard C variables + ('c:data', 'errno'), + # Standard environment variables + ('envvar', 'BROWSER'), + ('envvar', 'COLUMNS'), + ('envvar', 'COMSPEC'), + ('envvar', 'DISPLAY'), + ('envvar', 'HOME'), + ('envvar', 'HOMEDRIVE'), + ('envvar', 'HOMEPATH'), + ('envvar', 'IDLESTARTUP'), + ('envvar', 'LANG'), + ('envvar', 'LANGUAGE'), + ('envvar', 'LC_ALL'), + ('envvar', 'LC_CTYPE'), + ('envvar', 'LC_COLLATE'), + ('envvar', 'LC_MESSAGES'), + ('envvar', 'LC_MONETARY'), + ('envvar', 'LC_NUMERIC'), + ('envvar', 'LC_TIME'), + ('envvar', 'LINES'), + ('envvar', 'LOGNAME'), + ('envvar', 'PAGER'), + ('envvar', 'PATH'), + ('envvar', 'PATHEXT'), + ('envvar', 'SOURCE_DATE_EPOCH'), + ('envvar', 'TEMP'), + ('envvar', 'TERM'), + ('envvar', 'TMP'), + ('envvar', 'TMPDIR'), + ('envvar', 'TZ'), + ('envvar', 'USER'), + ('envvar', 'USERNAME'), + ('envvar', 'USERPROFILE'), # Do not error nit-picky mode builds when _SubParsersAction.add_parser cannot # be resolved, as the method is currently undocumented. For context, see # https://github.com/python/cpython/pull/103289. diff --git a/Doc/library/array.rst b/Doc/library/array.rst index 75c49e0f6d1ebe..d6cb8c623adbf1 100644 --- a/Doc/library/array.rst +++ b/Doc/library/array.rst @@ -51,9 +51,9 @@ Notes: It can be 16 bits or 32 bits depending on the platform. .. versionchanged:: 3.9 - ``array('u')`` now uses ``wchar_t`` as C type instead of deprecated + ``array('u')`` now uses :c:type:`wchar_t` as C type instead of deprecated ``Py_UNICODE``. This change doesn't affect its behavior because - ``Py_UNICODE`` is alias of ``wchar_t`` since Python 3.3. + ``Py_UNICODE`` is alias of :c:type:`wchar_t` since Python 3.3. .. deprecated-removed:: 3.3 4.0 diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 2be2473f9b9a42..b55ec32ef5af57 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -221,7 +221,7 @@ Fundamental data types +----------------------+------------------------------------------+----------------------------+ | :class:`c_char` | :c:expr:`char` | 1-character bytes object | +----------------------+------------------------------------------+----------------------------+ -| :class:`c_wchar` | :c:expr:`wchar_t` | 1-character string | +| :class:`c_wchar` | :c:type:`wchar_t` | 1-character string | +----------------------+------------------------------------------+----------------------------+ | :class:`c_byte` | :c:expr:`char` | int | +----------------------+------------------------------------------+----------------------------+ @@ -244,9 +244,9 @@ Fundamental data types | :class:`c_ulonglong` | :c:expr:`unsigned __int64` or | int | | | :c:expr:`unsigned long long` | | +----------------------+------------------------------------------+----------------------------+ -| :class:`c_size_t` | :c:expr:`size_t` | int | +| :class:`c_size_t` | :c:type:`size_t` | int | +----------------------+------------------------------------------+----------------------------+ -| :class:`c_ssize_t` | :c:expr:`ssize_t` or | int | +| :class:`c_ssize_t` | :c:type:`ssize_t` or | int | | | :c:expr:`Py_ssize_t` | | +----------------------+------------------------------------------+----------------------------+ | :class:`c_float` | :c:expr:`float` | float | @@ -334,7 +334,7 @@ property:: The :func:`create_string_buffer` function replaces the old :func:`c_buffer` function (which is still available as an alias). To create a mutable memory -block containing unicode characters of the C type :c:expr:`wchar_t`, use the +block containing unicode characters of the C type :c:type:`wchar_t`, use the :func:`create_unicode_buffer` function. @@ -2361,7 +2361,7 @@ These are the fundamental ctypes data types: .. class:: c_wchar - Represents the C :c:expr:`wchar_t` datatype, and interprets the value as a + Represents the C :c:type:`wchar_t` datatype, and interprets the value as a single character unicode string. The constructor accepts an optional string initializer, the length of the string must be exactly one character. diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 283dc5ad1541a1..9d6d02652db908 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -4410,7 +4410,7 @@ written in Python, such as a mail server's external command delivery program. :data:`WNOHANG` and :data:`WNOWAIT` are additional optional flags. The return value is an object representing the data contained in the - :c:type:`!siginfo_t` structure with the following attributes: + :c:type:`siginfo_t` structure with the following attributes: * :attr:`!si_pid` (process ID) * :attr:`!si_uid` (real user ID of the child) diff --git a/Doc/library/struct.rst b/Doc/library/struct.rst index 26c8d650382ceb..416b01db615fa0 100644 --- a/Doc/library/struct.rst +++ b/Doc/library/struct.rst @@ -231,9 +231,9 @@ platform-dependent. | ``Q`` | :c:expr:`unsigned long | integer | 8 | \(2) | | | long` | | | | +--------+--------------------------+--------------------+----------------+------------+ -| ``n`` | :c:expr:`ssize_t` | integer | | \(3) | +| ``n`` | :c:type:`ssize_t` | integer | | \(3) | +--------+--------------------------+--------------------+----------------+------------+ -| ``N`` | :c:expr:`size_t` | integer | | \(3) | +| ``N`` | :c:type:`size_t` | integer | | \(3) | +--------+--------------------------+--------------------+----------------+------------+ | ``e`` | \(6) | float | 2 | \(4) | +--------+--------------------------+--------------------+----------------+------------+ diff --git a/Doc/library/venv.rst b/Doc/library/venv.rst index 07a6f5f8920f9c..0b9787d095d9ad 100644 --- a/Doc/library/venv.rst +++ b/Doc/library/venv.rst @@ -60,7 +60,7 @@ running from a virtual environment. A virtual environment may be "activated" using a script in its binary directory (``bin`` on POSIX; ``Scripts`` on Windows). -This will prepend that directory to your :envvar:`!PATH`, so that running +This will prepend that directory to your :envvar:`PATH`, so that running :program:`python` will invoke the environment's Python interpreter and you can run installed scripts without having to use their full path. The invocation of the activation script is platform-specific @@ -100,10 +100,10 @@ In order to achieve this, scripts installed into virtual environments have a "shebang" line which points to the environment's Python interpreter, i.e. :samp:`#!/{}/bin/python`. This means that the script will run with that interpreter regardless of the -value of :envvar:`!PATH`. On Windows, "shebang" line processing is supported if +value of :envvar:`PATH`. On Windows, "shebang" line processing is supported if you have the :ref:`launcher` installed. Thus, double-clicking an installed script in a Windows Explorer window should run it with the correct interpreter -without the environment needing to be activated or on the :envvar:`!PATH`. +without the environment needing to be activated or on the :envvar:`PATH`. When a virtual environment has been activated, the :envvar:`!VIRTUAL_ENV` environment variable is set to the path of the environment. diff --git a/Doc/whatsnew/3.3.rst b/Doc/whatsnew/3.3.rst index 782048c397f973..e3e49640f6c916 100644 --- a/Doc/whatsnew/3.3.rst +++ b/Doc/whatsnew/3.3.rst @@ -1983,7 +1983,7 @@ the form '-rwxrwxrwx'. struct ------ -The :mod:`struct` module now supports ``ssize_t`` and ``size_t`` via the +The :mod:`struct` module now supports :c:type:`ssize_t` and :c:type:`size_t` via the new codes ``n`` and ``N``, respectively. (Contributed by Antoine Pitrou in :issue:`3163`.) diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst index 69d9147524caed..315de6b59fe5f0 100644 --- a/Doc/whatsnew/3.5.rst +++ b/Doc/whatsnew/3.5.rst @@ -2192,7 +2192,7 @@ encode error with ``\N{...}`` escapes. (Contributed by Serhiy Storchaka in :issue:`19676`.) A new :c:func:`PyErr_FormatV` function similar to :c:func:`PyErr_Format`, -but accepts a ``va_list`` argument. +but accepts a :c:type:`va_list` argument. (Contributed by Antoine Pitrou in :issue:`18711`.) A new :c:data:`PyExc_RecursionError` exception. diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index 1f0fbbc2196664..23db4022dc06a0 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -1115,9 +1115,9 @@ Changes in the Python API ``PyCF_ALLOW_TOP_LEVEL_AWAIT`` was clashing with ``CO_FUTURE_DIVISION``. (Contributed by Batuhan Taskaya in :issue:`39562`) -* ``array('u')`` now uses ``wchar_t`` as C type instead of ``Py_UNICODE``. +* ``array('u')`` now uses :c:type:`wchar_t` as C type instead of ``Py_UNICODE``. This change doesn't affect to its behavior because ``Py_UNICODE`` is alias - of ``wchar_t`` since Python 3.3. + of :c:type:`wchar_t` since Python 3.3. (Contributed by Inada Naoki in :issue:`34538`.) * The :func:`logging.getLogger` API now returns the root logger when passed diff --git a/Misc/NEWS.d/3.10.0a5.rst b/Misc/NEWS.d/3.10.0a5.rst index 1c7c7447cae065..2854afcc8f84f5 100644 --- a/Misc/NEWS.d/3.10.0a5.rst +++ b/Misc/NEWS.d/3.10.0a5.rst @@ -667,4 +667,4 @@ exception (if an exception is set). Patch by Victor Stinner. .. section: C API Fixed a compiler warning in :c:func:`Py_UNICODE_ISSPACE()` on platforms with -signed ``wchar_t``. +signed :c:type:`wchar_t`. diff --git a/Misc/NEWS.d/next/Documentation/2023-07-21-11-51-57.gh-issue-106948.K_JQ7j.rst b/Misc/NEWS.d/next/Documentation/2023-07-21-11-51-57.gh-issue-106948.K_JQ7j.rst new file mode 100644 index 00000000000000..42b6348153b56a --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2023-07-21-11-51-57.gh-issue-106948.K_JQ7j.rst @@ -0,0 +1 @@ +Add a number of standard external names to ``nitpick_ignore``. From 5a7456623fc63a6a4af58eaba5c6ab91cf9109e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Sun, 23 Jul 2023 09:29:13 +0000 Subject: [PATCH 043/632] [3.11] gh-75371: reformat Makefile.pre.in to accommodate for empty FRAMEWORKALTINSTALLLAST (GH-107035) (#107050) in the case of an empty FRAMEWORKALTINSTALLLAST, this patch prevents leaving an astray linebreak and two tabs in the resulting Makefile. Before change: ``` .PHONY: commoninstall commoninstall: check-clean-src \ altbininstall libinstall inclinstall libainstall \ sharedinstall altmaninstall \ ``` After change (with empty FRAMEWORKALTINSTALLLAST): ``` .PHONY: commoninstall commoninstall: check-clean-src \ altbininstall libinstall inclinstall libainstall \ sharedinstall altmaninstall ``` (cherry picked from commit 9c38206925246ab919cf558ac069ae9458720ba7) Co-authored-by: Moritz Neeb --- Makefile.pre.in | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Makefile.pre.in b/Makefile.pre.in index 3948184ad8ea3d..2a407a7df697e0 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1764,8 +1764,7 @@ altinstall: commoninstall commoninstall: check-clean-src @FRAMEWORKALTINSTALLFIRST@ \ altbininstall libinstall inclinstall libainstall \ - sharedinstall oldsharedinstall altmaninstall \ - @FRAMEWORKALTINSTALLLAST@ + sharedinstall oldsharedinstall altmaninstall @FRAMEWORKALTINSTALLLAST@ # Install shared libraries enabled by Setup DESTDIRS= $(exec_prefix) $(LIBDIR) $(BINLIBDEST) $(DESTSHARED) From 561029aeb060941ea3dabece753928e6b4674653 Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Sun, 23 Jul 2023 11:34:56 +0200 Subject: [PATCH 044/632] [3.11] Convert `doc.yml` workflow to be reusable (GH-103914 + GH-105151) (#107043) Co-authored-by: Sviatoslav Sydorenko Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Co-authored-by: Hugo van Kemenade . (cherry picked from commit 88d14da76f579fe014cbd7c15e42be4234135fe9) (cherry picked from commit eaa670228066220f08c8d73f80365c50058d40b8) --- .github/workflows/build.yml | 26 ++++++++++++++++++- .../workflows/{doc.yml => reusable-docs.yml} | 23 +--------------- 2 files changed, 26 insertions(+), 23 deletions(-) rename .github/workflows/{doc.yml => reusable-docs.yml} (89%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6848ed6f54bf0e..5dc63ff64e9ca2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,7 +26,7 @@ permissions: contents: read concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}-reusable cancel-in-progress: true jobs: @@ -35,6 +35,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 outputs: + run-docs: ${{ steps.docs-changes.outputs.run-docs || false }} run_tests: ${{ steps.check.outputs.run_tests }} run_ssl_tests: ${{ steps.check.outputs.run_ssl_tests }} config_hash: ${{ steps.config_hash.outputs.hash }} @@ -68,6 +69,29 @@ jobs: id: config_hash run: | echo "hash=${{ hashFiles('configure', 'configure.ac', '.github/workflows/build.yml') }}" >> $GITHUB_OUTPUT + - name: Get a list of the changed documentation-related files + if: github.event_name == 'pull_request' + id: changed-docs-files + uses: Ana06/get-changed-files@v2.2.0 + with: + filter: | + Doc/** + Misc/** + .github/workflows/reusable-docs.yml + format: csv # works for paths with spaces + - name: Check for docs changes + if: >- + github.event_name == 'pull_request' + && steps.changed-docs-files.outputs.added_modified_renamed != '' + id: docs-changes + run: | + echo "run-docs=true" >> "${GITHUB_OUTPUT}" + + check-docs: + name: Docs + needs: check_source + if: fromJSON(needs.check_source.outputs.run-docs) + uses: ./.github/workflows/reusable-docs.yml check_abi: name: 'Check if the ABI has changed' diff --git a/.github/workflows/doc.yml b/.github/workflows/reusable-docs.yml similarity index 89% rename from .github/workflows/doc.yml rename to .github/workflows/reusable-docs.yml index e32078c67fba60..eade14bc8d6191 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/reusable-docs.yml @@ -1,29 +1,8 @@ name: Docs on: + workflow_call: workflow_dispatch: - #push: - # branches: - # - 'main' - # - '3.11' - # - '3.10' - # - '3.9' - # - '3.8' - # - '3.7' - # paths: - # - 'Doc/**' - pull_request: - branches: - - 'main' - - '3.11' - - '3.10' - - '3.9' - - '3.8' - - '3.7' - paths: - - 'Doc/**' - - 'Misc/**' - - '.github/workflows/doc.yml' permissions: contents: read From 6e3a2fb0fe07712156e1086d171ff2392343c32a Mon Sep 17 00:00:00 2001 From: Daniele Procida Date: Sun, 23 Jul 2023 12:25:23 +0200 Subject: [PATCH 045/632] [3.11] gh-106996: Add the basics of a turtle graphics tutorial (GH-107072) (#107110) --- Doc/includes/turtle-star.py | 10 --- Doc/library/turtle.rst | 153 +++++++++++++++++++++++++++++++++--- 2 files changed, 140 insertions(+), 23 deletions(-) delete mode 100644 Doc/includes/turtle-star.py diff --git a/Doc/includes/turtle-star.py b/Doc/includes/turtle-star.py deleted file mode 100644 index 1a5db761b32385..00000000000000 --- a/Doc/includes/turtle-star.py +++ /dev/null @@ -1,10 +0,0 @@ -from turtle import * -color('red', 'yellow') -begin_fill() -while True: - forward(200) - left(170) - if abs(pos()) < 1: - break -end_fill() -done() diff --git a/Doc/library/turtle.rst b/Doc/library/turtle.rst index 2298c6c545451b..03b299ba7279c9 100644 --- a/Doc/library/turtle.rst +++ b/Doc/library/turtle.rst @@ -19,14 +19,10 @@ Introduction ============ -Turtle graphics is a popular way for introducing programming to kids. It was -part of the original Logo programming language developed by Wally Feurzeig, -Seymour Papert and Cynthia Solomon in 1967. - -Imagine a robotic turtle starting at (0, 0) in the x-y plane. After an ``import turtle``, give it the -command ``turtle.forward(15)``, and it moves (on-screen!) 15 pixels in the -direction it is facing, drawing a line as it moves. Give it the command -``turtle.right(25)``, and it rotates in-place 25 degrees clockwise. +Turtle graphics is an implementation of `the popular geometric drawing tools +introduced in Logo `_, developed by Wally Feurzeig, Seymour Papert and Cynthia Solomon +in 1967. .. sidebar:: Turtle star @@ -36,10 +32,141 @@ direction it is facing, drawing a line as it moves. Give it the command .. image:: turtle-star.* :align: center - .. literalinclude:: ../includes/turtle-star.py +In Python, turtle graphics provides a representation of a physical "turtle" +(a little robot with a pen) that draws on a sheet of paper on the floor. + +It's an effective and well-proven way for learners to encounter +programming concepts and interaction with software, as it provides instant, +visible feedback. It also provides convenient access to graphical output +in general. + +Turtle drawing was originally created as an educational tool, to be used by +teachers in the classroom. For the programmer who needs to produce some +graphical output it can be a way to do that without the overhead of +introducing more complex or external libraries into their work. + + +Tutorial +======== + +New users should start here. In this tutorial we'll explore some of the +basics of turtle drawing. + + +Starting a turtle environment +----------------------------- + +In a Python shell, import all the objects of the ``turtle`` module:: + + from turtle import * + +If you run into a ``No module named '_tkinter'`` error, you'll have to +install the :mod:`Tk interface package ` on your system. + + +Basic drawing +------------- + +Send the turtle forward 100 steps:: + + forward(100) + +You should see (most likely, in a new window on your display) a line +drawn by the turtle, heading East. Change the direction of the turtle, +so that it turns 120 degrees left (anti-clockwise):: + + left(120) + +Let's continue by drawing a triangle:: + + forward(100) + left(120) + forward(100) + +Notice how the turtle, represented by an arrow, points in different +directions as you steer it. + +Experiment with those commands, and also with ``backward()`` and +``right()``. + + +Pen control +~~~~~~~~~~~ + +Try changing the color - for example, ``color('blue')`` - and +width of the line - for example, ``width(3)`` - and then drawing again. + +You can also move the turtle around without drawing, by lifting up the pen: +``up()`` before moving. To start drawing again, use ``down()``. + + +The turtle's position +~~~~~~~~~~~~~~~~~~~~~ + +Send your turtle back to its starting-point (useful if it has disappeared +off-screen):: + + home() + +The home position is at the center of the turtle's screen. If you ever need to +know them, get the turtle's x-y co-ordinates with:: + + pos() + +Home is at ``(0, 0)``. + +And after a while, it will probably help to clear the window so we can start +anew:: + + clearscreen() + + +Making algorithmic patterns +--------------------------- + +Using loops, it's possible to build up geometric patterns:: + + for steps in range(100): + for c in ('blue', 'red', 'green'): + color(c) + forward(steps) + right(30) + + +\ - which of course, are limited only by the imagination! + +Let's draw the star shape at the top of this page. We want red lines, +filled in with yellow:: + + color('red') + fillcolor('yellow') + +Just as ``up()`` and ``down()`` determine whether lines will be drawn, +filling can be turned on and off:: + + begin_fill() + +Next we'll create a loop:: + + while True: + forward(200) + left(170) + if abs(pos()) < 1: + break + +``abs(pos()) < 1`` is a good way to know when the turtle is back at its +home position. + +Finally, complete the filling:: + + end_fill() + +(Note that filling only actually takes place when you give the +``end_fill()`` command.) + -By combining together these and similar commands, intricate shapes and pictures -can easily be drawn. +Explanation +=========== The :mod:`turtle` module is an extended reimplementation of the same-named module from the Python standard distribution up to version Python 2.5. @@ -94,8 +221,8 @@ To use multiple turtles on a screen one has to use the object-oriented interface omitted here. -Overview of available Turtle and Screen methods -================================================= +Turtle graphics reference +========================= Turtle methods -------------- From 0574873e60e54bd4873025b4380116992e4923b9 Mon Sep 17 00:00:00 2001 From: Moritz Neeb Date: Sun, 23 Jul 2023 12:26:12 +0200 Subject: [PATCH 046/632] [3.11] gh-106969: Indicate no modules were added in 3.10 (GH-106988) (#107093) [3.11] gh-106969: Indicate no modules were added in 3.10 & 3.12 (GH-106988) The "New Modules" section was left in place to ensure that the anchor link for new modules will still exist: /whatsnew/3.12.htmlGH-new-modules /whatsnew/3.10.htmlGH-new-modules This means that existing links to this section don't break.. (cherry picked from commit 6dbffaed17d59079d6a2788d686009f762a3278f) Co-authored-by: Sebastiaan Zeeff <33516116+SebastiaanZ@users.noreply.github.com> --- Doc/whatsnew/3.10.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 56b8ff36bee774..50b85174ffdef7 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -886,7 +886,7 @@ Other Language Changes New Modules =========== -* None yet. +* None. Improved Modules From c51f766ebb5974024d5fc138f71350a6fb30c9d9 Mon Sep 17 00:00:00 2001 From: wulmer Date: Sun, 23 Jul 2023 13:56:09 +0200 Subject: [PATCH 047/632] [3.11] gh-101100: Fix some broken sphinx references (GH-107095). (#107120) --- Doc/c-api/iterator.rst | 2 +- Doc/c-api/mapping.rst | 6 +++--- Doc/c-api/refcounting.rst | 2 +- Doc/c-api/sequence.rst | 2 +- Doc/howto/functional.rst | 4 ++-- Doc/howto/regex.rst | 2 ++ Doc/howto/sorting.rst | 6 +++--- Doc/howto/unicode.rst | 8 ++++---- Doc/library/_thread.rst | 9 +++++---- Doc/library/codeop.rst | 4 ++-- Doc/library/constants.rst | 6 +++--- 11 files changed, 27 insertions(+), 24 deletions(-) diff --git a/Doc/c-api/iterator.rst b/Doc/c-api/iterator.rst index 3fcf099134d4dd..95952237ca746f 100644 --- a/Doc/c-api/iterator.rst +++ b/Doc/c-api/iterator.rst @@ -6,7 +6,7 @@ Iterator Objects ---------------- Python provides two general-purpose iterator objects. The first, a sequence -iterator, works with an arbitrary sequence supporting the :meth:`__getitem__` +iterator, works with an arbitrary sequence supporting the :meth:`~object.__getitem__` method. The second works with a callable object and a sentinel value, calling the callable for each item in the sequence, and ending the iteration when the sentinel value is returned. diff --git a/Doc/c-api/mapping.rst b/Doc/c-api/mapping.rst index cffb0ed50fb77d..d94a9dc45b5ebe 100644 --- a/Doc/c-api/mapping.rst +++ b/Doc/c-api/mapping.rst @@ -13,7 +13,7 @@ See also :c:func:`PyObject_GetItem`, :c:func:`PyObject_SetItem` and Return ``1`` if the object provides the mapping protocol or supports slicing, and ``0`` otherwise. Note that it returns ``1`` for Python classes with - a :meth:`__getitem__` method, since in general it is impossible to + a :meth:`~object.__getitem__` method, since in general it is impossible to determine what type of keys the class supports. This function always succeeds. @@ -60,7 +60,7 @@ See also :c:func:`PyObject_GetItem`, :c:func:`PyObject_SetItem` and This is equivalent to the Python expression ``key in o``. This function always succeeds. - Note that exceptions which occur while calling the :meth:`__getitem__` + Note that exceptions which occur while calling the :meth:`~object.__getitem__` method will get suppressed. To get error reporting use :c:func:`PyObject_GetItem()` instead. @@ -71,7 +71,7 @@ See also :c:func:`PyObject_GetItem`, :c:func:`PyObject_SetItem` and This is equivalent to the Python expression ``key in o``. This function always succeeds. - Note that exceptions which occur while calling the :meth:`__getitem__` + Note that exceptions which occur while calling the :meth:`~object.__getitem__` method and creating a temporary string object will get suppressed. To get error reporting use :c:func:`PyMapping_GetItemString()` instead. diff --git a/Doc/c-api/refcounting.rst b/Doc/c-api/refcounting.rst index 738bd77e9ce42f..af25e8bb913685 100644 --- a/Doc/c-api/refcounting.rst +++ b/Doc/c-api/refcounting.rst @@ -81,7 +81,7 @@ objects. .. warning:: The deallocation function can cause arbitrary Python code to be invoked (e.g. - when a class instance with a :meth:`__del__` method is deallocated). While + when a class instance with a :meth:`~object.__del__` method is deallocated). While exceptions in such code are not propagated, the executed code has free access to all Python global variables. This means that any object that is reachable from a global variable should be in a consistent state before :c:func:`Py_DECREF` is diff --git a/Doc/c-api/sequence.rst b/Doc/c-api/sequence.rst index 402a3e5e09ff56..ce28839f5ba739 100644 --- a/Doc/c-api/sequence.rst +++ b/Doc/c-api/sequence.rst @@ -9,7 +9,7 @@ Sequence Protocol .. c:function:: int PySequence_Check(PyObject *o) Return ``1`` if the object provides the sequence protocol, and ``0`` otherwise. - Note that it returns ``1`` for Python classes with a :meth:`__getitem__` + Note that it returns ``1`` for Python classes with a :meth:`~object.__getitem__` method, unless they are :class:`dict` subclasses, since in general it is impossible to determine what type of keys the class supports. This function always succeeds. diff --git a/Doc/howto/functional.rst b/Doc/howto/functional.rst index 64f2293b21e2ee..efc25dac695da0 100644 --- a/Doc/howto/functional.rst +++ b/Doc/howto/functional.rst @@ -1072,8 +1072,8 @@ write the obvious :keyword:`for` loop:: A related function is :func:`itertools.accumulate(iterable, func=operator.add) `. It performs the same calculation, but instead of -returning only the final result, :func:`accumulate` returns an iterator that -also yields each partial result:: +returning only the final result, :func:`~itertools.accumulate` returns an iterator +that also yields each partial result:: itertools.accumulate([1, 2, 3, 4, 5]) => 1, 3, 6, 10, 15 diff --git a/Doc/howto/regex.rst b/Doc/howto/regex.rst index 5cd6140f19ca2e..8d95d86ba398b6 100644 --- a/Doc/howto/regex.rst +++ b/Doc/howto/regex.rst @@ -522,6 +522,8 @@ cache. Compilation Flags ----------------- +.. currentmodule:: re + Compilation flags let you modify some aspects of how regular expressions work. Flags are available in the :mod:`re` module under two names, a long name such as :const:`IGNORECASE` and a short, one-letter form such as :const:`I`. (If you're diff --git a/Doc/howto/sorting.rst b/Doc/howto/sorting.rst index decce12bf3faf6..38dd09f0a721d2 100644 --- a/Doc/howto/sorting.rst +++ b/Doc/howto/sorting.rst @@ -273,7 +273,7 @@ Odds and Ends * The sort routines use ``<`` when making comparisons between two objects. So, it is easy to add a standard sort order to a class by - defining an :meth:`__lt__` method: + defining an :meth:`~object.__lt__` method: .. doctest:: @@ -281,8 +281,8 @@ Odds and Ends >>> sorted(student_objects) [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)] - However, note that ``<`` can fall back to using :meth:`__gt__` if - :meth:`__lt__` is not implemented (see :func:`object.__lt__`). + However, note that ``<`` can fall back to using :meth:`~object.__gt__` if + :meth:`~object.__lt__` is not implemented (see :func:`object.__lt__`). * Key functions need not depend directly on the objects being sorted. A key function can also access external resources. For instance, if the student grades diff --git a/Doc/howto/unicode.rst b/Doc/howto/unicode.rst index ca09aee72bf879..a5e0998ce8dcf9 100644 --- a/Doc/howto/unicode.rst +++ b/Doc/howto/unicode.rst @@ -424,8 +424,8 @@ lowercase letters 'ss'. A second tool is the :mod:`unicodedata` module's :func:`~unicodedata.normalize` function that converts strings to one -of several normal forms, where letters followed by a combining -character are replaced with single characters. :func:`normalize` can +of several normal forms, where letters followed by a combining character are +replaced with single characters. :func:`~unicodedata.normalize` can be used to perform string comparisons that won't falsely report inequality if two strings use combining characters differently: @@ -474,8 +474,8 @@ The Unicode Standard also specifies how to do caseless comparisons:: print(compare_caseless(single_char, multiple_chars)) -This will print ``True``. (Why is :func:`NFD` invoked twice? Because -there are a few characters that make :meth:`casefold` return a +This will print ``True``. (Why is :func:`!NFD` invoked twice? Because +there are a few characters that make :meth:`~str.casefold` return a non-normalized string, so the result needs to be normalized again. See section 3.13 of the Unicode Standard for a discussion and an example.) diff --git a/Doc/library/_thread.rst b/Doc/library/_thread.rst index 21f7847d679af2..4227a4fe1e33a2 100644 --- a/Doc/library/_thread.rst +++ b/Doc/library/_thread.rst @@ -148,8 +148,8 @@ This module defines the following constants and functions: .. data:: TIMEOUT_MAX The maximum value allowed for the *timeout* parameter of - :meth:`Lock.acquire`. Specifying a timeout greater than this value will - raise an :exc:`OverflowError`. + :meth:`Lock.acquire `. Specifying a timeout greater + than this value will raise an :exc:`OverflowError`. .. versionadded:: 3.2 @@ -215,8 +215,9 @@ In addition to these methods, lock objects can also be used via the * Calling :func:`sys.exit` or raising the :exc:`SystemExit` exception is equivalent to calling :func:`_thread.exit`. -* It is not possible to interrupt the :meth:`acquire` method on a lock --- the - :exc:`KeyboardInterrupt` exception will happen after the lock has been acquired. +* It is not possible to interrupt the :meth:`~threading.Lock.acquire` method on + a lock --- the :exc:`KeyboardInterrupt` exception will happen after the lock + has been acquired. * When the main thread exits, it is system defined whether the other threads survive. On most systems, they are killed without executing diff --git a/Doc/library/codeop.rst b/Doc/library/codeop.rst index 90df499f8207b7..55606e1c5f09ac 100644 --- a/Doc/library/codeop.rst +++ b/Doc/library/codeop.rst @@ -58,7 +58,7 @@ To do just the former: .. class:: Compile() - Instances of this class have :meth:`__call__` methods identical in signature to + Instances of this class have :meth:`~object.__call__` methods identical in signature to the built-in function :func:`compile`, but with the difference that if the instance compiles program text containing a :mod:`__future__` statement, the instance 'remembers' and compiles all subsequent program texts with the @@ -67,7 +67,7 @@ To do just the former: .. class:: CommandCompiler() - Instances of this class have :meth:`__call__` methods identical in signature to + Instances of this class have :meth:`~object.__call__` methods identical in signature to :func:`compile_command`; the difference is that if the instance compiles program text containing a :mod:`__future__` statement, the instance 'remembers' and compiles all subsequent program texts with the statement in force. diff --git a/Doc/library/constants.rst b/Doc/library/constants.rst index 38dd552a0363ac..401dc9a320c5e0 100644 --- a/Doc/library/constants.rst +++ b/Doc/library/constants.rst @@ -22,16 +22,16 @@ A small number of constants live in the built-in namespace. They are: An object frequently used to represent the absence of a value, as when default arguments are not passed to a function. Assignments to ``None`` are illegal and raise a :exc:`SyntaxError`. - ``None`` is the sole instance of the :data:`NoneType` type. + ``None`` is the sole instance of the :data:`~types.NoneType` type. .. data:: NotImplemented A special value which should be returned by the binary special methods - (e.g. :meth:`__eq__`, :meth:`__lt__`, :meth:`__add__`, :meth:`__rsub__`, + (e.g. :meth:`~object.__eq__`, :meth:`~object.__lt__`, :meth:`~object.__add__`, :meth:`~object.__rsub__`, etc.) to indicate that the operation is not implemented with respect to the other type; may be returned by the in-place binary special methods - (e.g. :meth:`__imul__`, :meth:`__iand__`, etc.) for the same purpose. + (e.g. :meth:`~object.__imul__`, :meth:`~object.__iand__`, etc.) for the same purpose. It should not be evaluated in a boolean context. ``NotImplemented`` is the sole instance of the :data:`types.NotImplementedType` type. From d2cdf0888bdab86d21cd70749b2d9a0eb426fa76 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 23 Jul 2023 15:06:32 +0300 Subject: [PATCH 048/632] [3.11] gh-107091: Fix the use of some C domain roles (GH-107092) (GH-107121) (cherry picked from commit 08a228da05a7aec937b65eea21f4091fa3c6b5cf) --- Doc/c-api/buffer.rst | 4 ++-- Doc/c-api/method.rst | 4 ++-- Doc/c-api/module.rst | 2 +- Doc/c-api/type.rst | 2 +- Doc/c-api/typeobj.rst | 4 ++-- Doc/extending/extending.rst | 10 +++++----- Doc/howto/isolating-extensions.rst | 4 ++-- Doc/whatsnew/3.11.rst | 2 +- Doc/whatsnew/3.9.rst | 2 +- Misc/NEWS.d/3.10.0a2.rst | 4 ++-- 10 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Doc/c-api/buffer.rst b/Doc/c-api/buffer.rst index 91d1edd9b2ec46..6e5443f0d6cdc5 100644 --- a/Doc/c-api/buffer.rst +++ b/Doc/c-api/buffer.rst @@ -225,7 +225,7 @@ object via :c:func:`PyObject_GetBuffer`. Since the complexity of the logical structure of the memory can vary drastically, the consumer uses the *flags* argument to specify the exact buffer type it can handle. -All :c:data:`Py_buffer` fields are unambiguously defined by the request +All :c:type:`Py_buffer` fields are unambiguously defined by the request type. request-independent fields @@ -464,7 +464,7 @@ Buffer-related functions .. c:function:: Py_ssize_t PyBuffer_SizeFromFormat(const char *format) - Return the implied :c:data:`~Py_buffer.itemsize` from :c:data:`~Py_buffer.format`. + Return the implied :c:member:`~Py_buffer.itemsize` from :c:member:`~Py_buffer.format`. On error, raise an exception and return -1. .. versionadded:: 3.9 diff --git a/Doc/c-api/method.rst b/Doc/c-api/method.rst index 93ad30cd4f7a8d..0d75ab8e1af111 100644 --- a/Doc/c-api/method.rst +++ b/Doc/c-api/method.rst @@ -7,8 +7,8 @@ Instance Method Objects .. index:: pair: object; instancemethod -An instance method is a wrapper for a :c:data:`PyCFunction` and the new way -to bind a :c:data:`PyCFunction` to a class object. It replaces the former call +An instance method is a wrapper for a :c:type:`PyCFunction` and the new way +to bind a :c:type:`PyCFunction` to a class object. It replaces the former call ``PyMethod_New(func, NULL, class)``. diff --git a/Doc/c-api/module.rst b/Doc/c-api/module.rst index bc8e3b23b99579..e358c5da14a69f 100644 --- a/Doc/c-api/module.rst +++ b/Doc/c-api/module.rst @@ -145,7 +145,7 @@ or request "multi-phase initialization" by returning the definition struct itsel .. c:member:: PyModuleDef_Base m_base - Always initialize this member to :c:data:`PyModuleDef_HEAD_INIT`. + Always initialize this member to :c:macro:`PyModuleDef_HEAD_INIT`. .. c:member:: const char *m_name diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst index a4ca724ad671a2..d6f32ed88b673b 100644 --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -149,7 +149,7 @@ Type Objects ``Py_TYPE(self)`` may be a *subclass* of the intended class, and subclasses are not necessarily defined in the same module as their superclass. See :c:type:`PyCMethod` to get the class that defines the method. - See :c:func:`PyType_GetModuleByDef` for cases when ``PyCMethod`` cannot + See :c:func:`PyType_GetModuleByDef` for cases when :c:type:`!PyCMethod` cannot be used. .. versionadded:: 3.9 diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 32989841c5b1a8..006e11b2e72352 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -2169,8 +2169,8 @@ Number Object Structures .. note:: - The :c:data:`nb_reserved` field should always be ``NULL``. It - was previously called :c:data:`nb_long`, and was renamed in + The :c:member:`~PyNumberMethods.nb_reserved` field should always be ``NULL``. It + was previously called :c:member:`!nb_long`, and was renamed in Python 3.0.1. .. c:member:: binaryfunc PyNumberMethods.nb_add diff --git a/Doc/extending/extending.rst b/Doc/extending/extending.rst index 76e0490d0d22df..097d86e30269cc 100644 --- a/Doc/extending/extending.rst +++ b/Doc/extending/extending.rst @@ -235,10 +235,10 @@ Note that the Python name for the exception object is :exc:`spam.error`. The being :exc:`Exception` (unless another class is passed in instead of ``NULL``), described in :ref:`bltin-exceptions`. -Note also that the :c:data:`SpamError` variable retains a reference to the newly +Note also that the :c:data:`!SpamError` variable retains a reference to the newly created exception class; this is intentional! Since the exception could be removed from the module by external code, an owned reference to the class is -needed to ensure that it will not be discarded, causing :c:data:`SpamError` to +needed to ensure that it will not be discarded, causing :c:data:`!SpamError` to become a dangling pointer. Should it become a dangling pointer, C code which raises the exception could cause a core dump or other unintended side effects. @@ -279,9 +279,9 @@ statement:: It returns ``NULL`` (the error indicator for functions returning object pointers) if an error is detected in the argument list, relying on the exception set by :c:func:`PyArg_ParseTuple`. Otherwise the string value of the argument has been -copied to the local variable :c:data:`command`. This is a pointer assignment and +copied to the local variable :c:data:`!command`. This is a pointer assignment and you are not supposed to modify the string to which it points (so in Standard C, -the variable :c:data:`command` should properly be declared as ``const char +the variable :c:data:`!command` should properly be declared as ``const char *command``). The next statement is a call to the Unix function :c:func:`system`, passing it @@ -289,7 +289,7 @@ the string we just got from :c:func:`PyArg_ParseTuple`:: sts = system(command); -Our :func:`spam.system` function must return the value of :c:data:`sts` as a +Our :func:`!spam.system` function must return the value of :c:data:`!sts` as a Python object. This is done using the function :c:func:`PyLong_FromLong`. :: return PyLong_FromLong(sts); diff --git a/Doc/howto/isolating-extensions.rst b/Doc/howto/isolating-extensions.rst index 522e0853b496a1..c88c9edc0ccad2 100644 --- a/Doc/howto/isolating-extensions.rst +++ b/Doc/howto/isolating-extensions.rst @@ -483,14 +483,14 @@ to get the state:: return NULL; } -``PyType_GetModuleByDef`` works by searching the +:c:func:`!PyType_GetModuleByDef` works by searching the :term:`method resolution order` (i.e. all superclasses) for the first superclass that has a corresponding module. .. note:: In very exotic cases (inheritance chains spanning multiple modules - created from the same definition), ``PyType_GetModuleByDef`` might not + created from the same definition), :c:func:`!PyType_GetModuleByDef` might not return the module of the true defining class. However, it will always return a module with the same definition, ensuring a compatible C memory layout. diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index f31b439ed2fd4a..91f54f38428a76 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -2227,7 +2227,7 @@ New Features (Contributed by Christian Heimes in :issue:`45459`.) -* Added the :c:data:`PyType_GetModuleByDef` function, used to get the module +* Added the :c:func:`PyType_GetModuleByDef` function, used to get the module in which a method was defined, in cases where this information is not available directly (via :c:type:`PyCMethod`). (Contributed by Petr Viktorin in :issue:`46613`.) diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index 23db4022dc06a0..6de432d6c036bc 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -1276,7 +1276,7 @@ New Features * :pep:`573`: Added :c:func:`PyType_FromModuleAndSpec` to associate a module with a class; :c:func:`PyType_GetModule` and :c:func:`PyType_GetModuleState` to retrieve the module and its state; and - :c:data:`PyCMethod` and :c:macro:`METH_METHOD` to allow a method to + :c:type:`PyCMethod` and :c:macro:`METH_METHOD` to allow a method to access the class it was defined in. (Contributed by Marcel Plch and Petr Viktorin in :issue:`38787`.) diff --git a/Misc/NEWS.d/3.10.0a2.rst b/Misc/NEWS.d/3.10.0a2.rst index cafd4804a1ee6e..9f85616c9f28dc 100644 --- a/Misc/NEWS.d/3.10.0a2.rst +++ b/Misc/NEWS.d/3.10.0a2.rst @@ -847,8 +847,8 @@ Victor Stinner. .. section: C API Fix potential crash in deallocating method objects when dynamically -allocated `PyMethodDef`'s lifetime is managed through the ``self`` argument -of a `PyCFunction`. +allocated :c:type:`PyMethodDef`'s lifetime is managed through the ``self`` argument +of a :c:type:`PyCFunction`. .. From afa24d52b821c2020e0966f96a6205bca7db85e9 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 23 Jul 2023 05:28:35 -0700 Subject: [PATCH 049/632] [3.11] gh-106186: Don't report MultipartInvariantViolationDefect for valid multipart emails when parsing header only (GH-107016) (#107112) (cherry picked from commit c65592c4d6d7552fb6284442906a96a6874cb266) Co-authored-by: htsedebenham <31847376+htsedebenham@users.noreply.github.com> --- Lib/email/feedparser.py | 2 +- Lib/test/test_email/data/msg_47.txt | 14 ++++++++++++++ Lib/test/test_email/test_email.py | 10 ++++++++++ .../2023-07-22-13-09-28.gh-issue-106186.EIsUNG.rst | 3 +++ 4 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 Lib/test/test_email/data/msg_47.txt create mode 100644 Misc/NEWS.d/next/Library/2023-07-22-13-09-28.gh-issue-106186.EIsUNG.rst diff --git a/Lib/email/feedparser.py b/Lib/email/feedparser.py index 97d3f5144d606f..e400dc7fb89b05 100644 --- a/Lib/email/feedparser.py +++ b/Lib/email/feedparser.py @@ -189,7 +189,7 @@ def close(self): assert not self._msgstack # Look for final set of defects if root.get_content_maintype() == 'multipart' \ - and not root.is_multipart(): + and not root.is_multipart() and not self._headersonly: defect = errors.MultipartInvariantViolationDefect() self.policy.handle_defect(root, defect) return root diff --git a/Lib/test/test_email/data/msg_47.txt b/Lib/test/test_email/data/msg_47.txt new file mode 100644 index 00000000000000..bb48b47d96baf8 --- /dev/null +++ b/Lib/test/test_email/data/msg_47.txt @@ -0,0 +1,14 @@ +Date: 01 Jan 2001 00:01+0000 +From: arthur@example.example +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary=foo + +--foo +Content-Type: text/plain +bar + +--foo +Content-Type: text/html +

baz

+ +--foo-- \ No newline at end of file diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py index ca8212825ffca4..2bb651609f572b 100644 --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -3696,6 +3696,16 @@ def test_bytes_header_parser(self): self.assertIsInstance(msg.get_payload(), str) self.assertIsInstance(msg.get_payload(decode=True), bytes) + def test_header_parser_multipart_is_valid(self): + # Don't flag valid multipart emails as having defects + with openfile('msg_47.txt', encoding="utf-8") as fp: + msgdata = fp.read() + + parser = email.parser.Parser(policy=email.policy.default) + parsed_msg = parser.parsestr(msgdata, headersonly=True) + + self.assertEqual(parsed_msg.defects, []) + def test_bytes_parser_does_not_close_file(self): with openfile('msg_02.txt', 'rb') as fp: email.parser.BytesParser().parse(fp) diff --git a/Misc/NEWS.d/next/Library/2023-07-22-13-09-28.gh-issue-106186.EIsUNG.rst b/Misc/NEWS.d/next/Library/2023-07-22-13-09-28.gh-issue-106186.EIsUNG.rst new file mode 100644 index 00000000000000..07fdcc96fa38a6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-07-22-13-09-28.gh-issue-106186.EIsUNG.rst @@ -0,0 +1,3 @@ +Do not report ``MultipartInvariantViolationDefect`` defect +when the :class:`email.parser.Parser` class is used +to parse emails with ``headersonly=True``. From 0cdc3a575d14d710045084a615ef7f2536423727 Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Sun, 23 Jul 2023 14:58:41 +0200 Subject: [PATCH 050/632] [3.11] Introduce a gate/check GHA job (GH-97533) (#107115) (cherry picked from commit e7cd557) --- .github/workflows/build.yml | 57 +++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5dc63ff64e9ca2..450a445f2bc44d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -439,3 +439,60 @@ jobs: run: make pythoninfo - name: Tests run: xvfb-run make buildbottest TESTOPTS="-j4 -uall,-cpu" + + all-required-green: # This job does nothing and is only used for the branch protection + name: All required checks pass + if: always() + + needs: + - check_source # Transitive dependency, needed to access `run_tests` value + - check-docs + - check_generated_files + - build_win32 + - build_win_amd64 + - build_macos + - build_ubuntu + - build_ubuntu_ssltests + - test_hypothesis + - build_asan + + runs-on: ubuntu-latest + + steps: + - name: Check whether the needed jobs succeeded or failed + uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe + with: + allowed-failures: >- + build_macos, + build_ubuntu_ssltests, + build_win32, + test_hypothesis, + allowed-skips: >- + ${{ + !fromJSON(needs.check_source.outputs.run-docs) + && ' + check-docs, + ' + || '' + }} + ${{ + needs.check_source.outputs.run_tests != 'true' + && ' + check_generated_files, + build_win32, + build_win_amd64, + build_macos, + build_ubuntu, + build_ubuntu_ssltests, + build_asan, + ' + || '' + }} + ${{ + !fromJSON(needs.check_source.outputs.run_hypothesis) + && ' + test_hypothesis, + ' + || '' + }} + jobs: ${{ toJSON(needs) }} From 07acf5756f146b798483b1ce0411c0a3f26cdfc8 Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Sun, 23 Jul 2023 15:33:19 +0200 Subject: [PATCH 051/632] =?UTF-8?q?[3.11]=20=F0=9F=94=A5=20Drop=20hypothes?= =?UTF-8?q?is=20job=20dep=20@=20GHA=20(#107128)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes an incorrect conflict resolution problem that happened in 0cdc3a575d14d710045084a615ef7f2536423727 while backporting PR #97533 as PR #107115 (merged prematurely). This problem caused GitHub Actions CI/CD to crash while attempting to load the workflow file definition, preventing the jobs that are defined in `.github/workflows/build.yml` from actually starting. --- .github/workflows/build.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 450a445f2bc44d..e7d3488ef8d9e3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -453,7 +453,6 @@ jobs: - build_macos - build_ubuntu - build_ubuntu_ssltests - - test_hypothesis - build_asan runs-on: ubuntu-latest @@ -466,7 +465,6 @@ jobs: build_macos, build_ubuntu_ssltests, build_win32, - test_hypothesis, allowed-skips: >- ${{ !fromJSON(needs.check_source.outputs.run-docs) @@ -488,11 +486,4 @@ jobs: ' || '' }} - ${{ - !fromJSON(needs.check_source.outputs.run_hypothesis) - && ' - test_hypothesis, - ' - || '' - }} jobs: ${{ toJSON(needs) }} From 6138ecdeb80d3a62d5cef27b08669495bccbe19b Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 23 Jul 2023 06:59:05 -0700 Subject: [PATCH 052/632] [3.11] gh-107017: Analolgy to Pascal and C replaced. (GH-107025) (#107123) Co-authored-by: TommyUnreal <45427816+TommyUnreal@users.noreply.github.com> Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Co-authored-by: Hugo van Kemenade --- Doc/tutorial/introduction.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/tutorial/introduction.rst b/Doc/tutorial/introduction.rst index b7a89905e4f38c..e8b582dfe85d28 100644 --- a/Doc/tutorial/introduction.rst +++ b/Doc/tutorial/introduction.rst @@ -52,8 +52,8 @@ Numbers The interpreter acts as a simple calculator: you can type an expression at it and it will write the value. Expression syntax is straightforward: the -operators ``+``, ``-``, ``*`` and ``/`` work just like in most other languages -(for example, Pascal or C); parentheses (``()``) can be used for grouping. +operators ``+``, ``-``, ``*`` and ``/`` can be used to perform +arithmetic; parentheses (``()``) can be used for grouping. For example:: >>> 2 + 2 From b3e600a632d018ce868bec0ef26116b8637fe8e0 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sun, 23 Jul 2023 23:49:48 +0200 Subject: [PATCH 053/632] [3.11] gh-106948: Docs: Disable links for C standard library functions, OS utility functions and system calls (#107062) (#107157) (cherry picked from commit b447e19e720e6781025432a40eb72b1cc93ac944) Co-authored-by: Serhiy Storchaka --- Doc/c-api/exceptions.rst | 2 +- Doc/c-api/sys.rst | 4 ++-- Doc/conf.py | 18 +++++++++++++++ Doc/howto/instrumentation.rst | 6 ++--- Doc/library/mailbox.rst | 8 +++---- Doc/library/os.rst | 6 ++--- Doc/library/select.rst | 42 +++++++++++++++++------------------ Doc/library/signal.rst | 2 +- Doc/whatsnew/2.6.rst | 6 ++--- Doc/whatsnew/2.7.rst | 2 +- Misc/NEWS.d/3.10.0a1.rst | 4 ++-- 11 files changed, 59 insertions(+), 41 deletions(-) diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst index 5dad596f6197fc..6ca53bd29f9e84 100644 --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -152,7 +152,7 @@ For convenience, some of these functions will always return a This is a convenience function to raise an exception when a C library function has returned an error and set the C variable :c:data:`errno`. It constructs a tuple object whose first item is the integer :c:data:`errno` value and whose - second item is the corresponding error message (gotten from :c:func:`strerror`), + second item is the corresponding error message (gotten from :c:func:`!strerror`), and then calls ``PyErr_SetObject(type, object)``. On Unix, when the :c:data:`errno` value is :c:macro:`EINTR`, indicating an interrupted system call, this calls :c:func:`PyErr_CheckSignals`, and if that set the error indicator, diff --git a/Doc/c-api/sys.rst b/Doc/c-api/sys.rst index 305a315549d156..57a36c038b592b 100644 --- a/Doc/c-api/sys.rst +++ b/Doc/c-api/sys.rst @@ -104,7 +104,7 @@ Operating System Utilities .. c:function:: PyOS_sighandler_t PyOS_getsig(int i) Return the current signal handler for signal *i*. This is a thin wrapper around - either :c:func:`sigaction` or :c:func:`signal`. Do not call those functions + either :c:func:`!sigaction` or :c:func:`!signal`. Do not call those functions directly! :c:type:`PyOS_sighandler_t` is a typedef alias for :c:expr:`void (\*)(int)`. @@ -112,7 +112,7 @@ Operating System Utilities .. c:function:: PyOS_sighandler_t PyOS_setsig(int i, PyOS_sighandler_t h) Set the signal handler for signal *i* to be *h*; return the old signal handler. - This is a thin wrapper around either :c:func:`sigaction` or :c:func:`signal`. Do + This is a thin wrapper around either :c:func:`!sigaction` or :c:func:`!signal`. Do not call those functions directly! :c:type:`PyOS_sighandler_t` is a typedef alias for :c:expr:`void (\*)(int)`. diff --git a/Doc/conf.py b/Doc/conf.py index c082717efda723..28c35f2b0a2348 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -72,6 +72,24 @@ exclude_patterns.append(venvdir + '/*') nitpick_ignore = [ + # Standard C functions + ('c:func', 'calloc'), + ('c:func', 'dlopen'), + ('c:func', 'exec'), + ('c:func', 'fcntl'), + ('c:func', 'fork'), + ('c:func', 'free'), + ('c:func', 'gmtime'), + ('c:func', 'localtime'), + ('c:func', 'main'), + ('c:func', 'malloc'), + ('c:func', 'printf'), + ('c:func', 'realloc'), + ('c:func', 'snprintf'), + ('c:func', 'sprintf'), + ('c:func', 'stat'), + ('c:func', 'system'), + ('c:func', 'vsnprintf'), # Standard C types ('c:type', 'FILE'), ('c:type', '__int'), diff --git a/Doc/howto/instrumentation.rst b/Doc/howto/instrumentation.rst index 4ce15c69dac90b..875f846aad0051 100644 --- a/Doc/howto/instrumentation.rst +++ b/Doc/howto/instrumentation.rst @@ -292,11 +292,11 @@ Available static markers .. object:: function__return(str filename, str funcname, int lineno) - This marker is the converse of :c:func:`function__entry`, and indicates that + This marker is the converse of :c:func:`!function__entry`, and indicates that execution of a Python function has ended (either via ``return``, or via an exception). It is only triggered for pure-Python (bytecode) functions. - The arguments are the same as for :c:func:`function__entry` + The arguments are the same as for :c:func:`!function__entry` .. object:: line(str filename, str funcname, int lineno) @@ -304,7 +304,7 @@ Available static markers the equivalent of line-by-line tracing with a Python profiler. It is not triggered within C functions. - The arguments are the same as for :c:func:`function__entry`. + The arguments are the same as for :c:func:`!function__entry`. .. object:: gc__start(int generation) diff --git a/Doc/library/mailbox.rst b/Doc/library/mailbox.rst index 56908dedea1b40..91df07d914cae2 100644 --- a/Doc/library/mailbox.rst +++ b/Doc/library/mailbox.rst @@ -477,7 +477,7 @@ Supported mailbox formats are Maildir, mbox, MH, Babyl, and MMDF. unlock() Three locking mechanisms are used---dot locking and, if available, the - :c:func:`flock` and :c:func:`lockf` system calls. + :c:func:`!flock` and :c:func:`!lockf` system calls. .. seealso:: @@ -588,7 +588,7 @@ Supported mailbox formats are Maildir, mbox, MH, Babyl, and MMDF. unlock() Three locking mechanisms are used---dot locking and, if available, the - :c:func:`flock` and :c:func:`lockf` system calls. For MH mailboxes, locking + :c:func:`!flock` and :c:func:`!lockf` system calls. For MH mailboxes, locking the mailbox means locking the :file:`.mh_sequences` file and, only for the duration of any operations that affect them, locking individual message files. @@ -686,7 +686,7 @@ Supported mailbox formats are Maildir, mbox, MH, Babyl, and MMDF. unlock() Three locking mechanisms are used---dot locking and, if available, the - :c:func:`flock` and :c:func:`lockf` system calls. + :c:func:`!flock` and :c:func:`!lockf` system calls. .. seealso:: @@ -737,7 +737,7 @@ Supported mailbox formats are Maildir, mbox, MH, Babyl, and MMDF. unlock() Three locking mechanisms are used---dot locking and, if available, the - :c:func:`flock` and :c:func:`lockf` system calls. + :c:func:`!flock` and :c:func:`!lockf` system calls. .. seealso:: diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 9d6d02652db908..dcb4e7d3ab259b 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -665,14 +665,14 @@ process and user. .. function:: getsid(pid, /) - Call the system call :c:func:`getsid`. See the Unix manual for the semantics. + Call the system call :c:func:`!getsid`. See the Unix manual for the semantics. .. availability:: Unix, not Emscripten, not WASI. .. function:: setsid() - Call the system call :c:func:`setsid`. See the Unix manual for the semantics. + Call the system call :c:func:`!setsid`. See the Unix manual for the semantics. .. availability:: Unix, not Emscripten, not WASI. @@ -690,7 +690,7 @@ process and user. .. function:: strerror(code, /) Return the error message corresponding to the error code in *code*. - On platforms where :c:func:`strerror` returns ``NULL`` when given an unknown + On platforms where :c:func:`!strerror` returns ``NULL`` when given an unknown error number, :exc:`ValueError` is raised. diff --git a/Doc/library/select.rst b/Doc/library/select.rst index b0891b0c8f584a..c2941e628d9d78 100644 --- a/Doc/library/select.rst +++ b/Doc/library/select.rst @@ -6,10 +6,10 @@ -------------- -This module provides access to the :c:func:`select` and :c:func:`poll` functions -available in most operating systems, :c:func:`devpoll` available on -Solaris and derivatives, :c:func:`epoll` available on Linux 2.5+ and -:c:func:`kqueue` available on most BSD. +This module provides access to the :c:func:`!select` and :c:func:`!poll` functions +available in most operating systems, :c:func:`!devpoll` available on +Solaris and derivatives, :c:func:`!epoll` available on Linux 2.5+ and +:c:func:`!kqueue` available on most BSD. Note that on Windows, it only works for sockets; on other operating systems, it also works for other file types (in particular, on Unix, it works on pipes). It cannot be used on regular files to determine whether a file has grown since @@ -41,10 +41,10 @@ The module defines the following: polling object; see section :ref:`devpoll-objects` below for the methods supported by devpoll objects. - :c:func:`devpoll` objects are linked to the number of file + :c:func:`!devpoll` objects are linked to the number of file descriptors allowed at the time of instantiation. If your program - reduces this value, :c:func:`devpoll` will fail. If your program - increases this value, :c:func:`devpoll` may return an + reduces this value, :c:func:`!devpoll` will fail. If your program + increases this value, :c:func:`!devpoll` may return an incomplete list of active file descriptors. The new file descriptor is :ref:`non-inheritable `. @@ -62,7 +62,7 @@ The module defines the following: *sizehint* informs epoll about the expected number of events to be registered. It must be positive, or ``-1`` to use the default. It is only - used on older systems where :c:func:`epoll_create1` is not available; + used on older systems where :c:func:`!epoll_create1` is not available; otherwise it has no effect (though its value is still checked). *flags* is deprecated and completely ignored. However, when supplied, its @@ -117,7 +117,7 @@ The module defines the following: .. function:: select(rlist, wlist, xlist[, timeout]) - This is a straightforward interface to the Unix :c:func:`select` system call. + This is a straightforward interface to the Unix :c:func:`!select` system call. The first three arguments are iterables of 'waitable objects': either integers representing file descriptors or objects with a parameterless method named :meth:`~io.IOBase.fileno` returning such an integer: @@ -154,7 +154,7 @@ The module defines the following: .. index:: single: WinSock File objects on Windows are not acceptable, but sockets are. On Windows, - the underlying :c:func:`select` function is provided by the WinSock + the underlying :c:func:`!select` function is provided by the WinSock library, and does not handle file descriptors that don't originate from WinSock. @@ -169,7 +169,7 @@ The module defines the following: The minimum number of bytes which can be written without blocking to a pipe when the pipe has been reported as ready for writing by :func:`~select.select`, - :func:`poll` or another interface in this module. This doesn't apply + :func:`!poll` or another interface in this module. This doesn't apply to other kind of file-like objects such as sockets. This value is guaranteed by POSIX to be at least 512. @@ -184,11 +184,11 @@ The module defines the following: ``/dev/poll`` Polling Objects ----------------------------- -Solaris and derivatives have ``/dev/poll``. While :c:func:`select` is -O(highest file descriptor) and :c:func:`poll` is O(number of file +Solaris and derivatives have ``/dev/poll``. While :c:func:`!select` is +O(highest file descriptor) and :c:func:`!poll` is O(number of file descriptors), ``/dev/poll`` is O(active file descriptors). -``/dev/poll`` behaviour is very close to the standard :c:func:`poll` +``/dev/poll`` behaviour is very close to the standard :c:func:`!poll` object. @@ -222,7 +222,7 @@ object. implement :meth:`!fileno`, so they can also be used as the argument. *eventmask* is an optional bitmask describing the type of events you want to - check for. The constants are the same that with :c:func:`poll` + check for. The constants are the same that with :c:func:`!poll` object. The default value is a combination of the constants :const:`POLLIN`, :const:`POLLPRI`, and :const:`POLLOUT`. @@ -231,7 +231,7 @@ object. Registering a file descriptor that's already registered is not an error, but the result is undefined. The appropriate action is to unregister or modify it first. This is an important difference - compared with :c:func:`poll`. + compared with :c:func:`!poll`. .. method:: devpoll.modify(fd[, eventmask]) @@ -376,13 +376,13 @@ Edge and Level Trigger Polling (epoll) Objects Polling Objects --------------- -The :c:func:`poll` system call, supported on most Unix systems, provides better +The :c:func:`!poll` system call, supported on most Unix systems, provides better scalability for network servers that service many, many clients at the same -time. :c:func:`poll` scales better because the system call only requires listing -the file descriptors of interest, while :c:func:`select` builds a bitmap, turns +time. :c:func:`!poll` scales better because the system call only requires listing +the file descriptors of interest, while :c:func:`!select` builds a bitmap, turns on bits for the fds of interest, and then afterward the whole bitmap has to be -linearly scanned again. :c:func:`select` is O(highest file descriptor), while -:c:func:`poll` is O(number of file descriptors). +linearly scanned again. :c:func:`!select` is O(highest file descriptor), while +:c:func:`!poll` is O(number of file descriptors). .. method:: poll.register(fd[, eventmask]) diff --git a/Doc/library/signal.rst b/Doc/library/signal.rst index 523d1ac5001360..e53315bee3ea3e 100644 --- a/Doc/library/signal.rst +++ b/Doc/library/signal.rst @@ -562,7 +562,7 @@ The :mod:`signal` module defines the following functions: Note that installing a signal handler with :func:`signal` will reset the restart behaviour to interruptible by implicitly calling - :c:func:`siginterrupt` with a true *flag* value for the given signal. + :c:func:`!siginterrupt` with a true *flag* value for the given signal. .. function:: signal(signalnum, handler) diff --git a/Doc/whatsnew/2.6.rst b/Doc/whatsnew/2.6.rst index 72a273fdd8d122..2b8fa1546a5884 100644 --- a/Doc/whatsnew/2.6.rst +++ b/Doc/whatsnew/2.6.rst @@ -2289,7 +2289,7 @@ changes, or look through the Subversion logs for all the details. (Contributed by Raymond Hettinger; :issue:`1861`.) * The :mod:`select` module now has wrapper functions - for the Linux :c:func:`epoll` and BSD :c:func:`kqueue` system calls. + for the Linux :c:func:`!epoll` and BSD :c:func:`!kqueue` system calls. :meth:`modify` method was added to the existing :class:`poll` objects; ``pollobj.modify(fd, eventmask)`` takes a file descriptor or file object and an event mask, modifying the recorded event mask @@ -2328,7 +2328,7 @@ changes, or look through the Subversion logs for all the details. one for reading and one for writing. The writable descriptor will be passed to :func:`set_wakeup_fd`, and the readable descriptor will be added to the list of descriptors monitored by the event loop via - :c:func:`select` or :c:func:`poll`. + :c:func:`!select` or :c:func:`!poll`. On receiving a signal, a byte will be written and the main event loop will be woken up, avoiding the need to poll. @@ -2982,7 +2982,7 @@ Changes to Python's build process and to the C API include: * Python now must be compiled with C89 compilers (after 19 years!). This means that the Python source tree has dropped its - own implementations of :c:func:`memmove` and :c:func:`strerror`, which + own implementations of :c:func:`!memmove` and :c:func:`!strerror`, which are in the C89 standard library. * Python 2.6 can be built with Microsoft Visual Studio 2008 (version diff --git a/Doc/whatsnew/2.7.rst b/Doc/whatsnew/2.7.rst index ba6aebafd59d9f..09798ca82d261f 100644 --- a/Doc/whatsnew/2.7.rst +++ b/Doc/whatsnew/2.7.rst @@ -355,7 +355,7 @@ added as a more powerful replacement for the This means Python now supports three different modules for parsing command-line arguments: :mod:`getopt`, :mod:`optparse`, and :mod:`argparse`. The :mod:`getopt` module closely resembles the C -library's :c:func:`getopt` function, so it remains useful if you're writing a +library's :c:func:`!getopt` function, so it remains useful if you're writing a Python prototype that will eventually be rewritten in C. :mod:`optparse` becomes redundant, but there are no plans to remove it because there are many scripts still using it, and there's no diff --git a/Misc/NEWS.d/3.10.0a1.rst b/Misc/NEWS.d/3.10.0a1.rst index 58e57a951ae608..2ef511c6e227e6 100644 --- a/Misc/NEWS.d/3.10.0a1.rst +++ b/Misc/NEWS.d/3.10.0a1.rst @@ -228,8 +228,8 @@ format string in f-string and :meth:`str.format`. .. section: Core and Builtins The implementation of :func:`signal.siginterrupt` now uses -:c:func:`sigaction` (if it is available in the system) instead of the -deprecated :c:func:`siginterrupt`. Patch by Pablo Galindo. +:c:func:`!sigaction` (if it is available in the system) instead of the +deprecated :c:func:`!siginterrupt`. Patch by Pablo Galindo. .. From 798d83bec89cb2e4565f7e16654fc3908a79a746 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 24 Jul 2023 05:06:06 -0700 Subject: [PATCH 054/632] [3.11] Docs: Remove duplicate word in Argument Clinic howto heading (GH-107169) (#107172) (cherry picked from commit ebe44a5155e9abc70c4b8914ad26b27c2b84f72b) Co-authored-by: Hakan Celik --- Doc/howto/clinic.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/howto/clinic.rst b/Doc/howto/clinic.rst index 50703d9b4723d9..4a83f6dde3a514 100644 --- a/Doc/howto/clinic.rst +++ b/Doc/howto/clinic.rst @@ -574,8 +574,8 @@ How-to guides ============= -How to to rename C functions and variables generated by Argument Clinic ------------------------------------------------------------------------ +How to rename C functions and variables generated by Argument Clinic +-------------------------------------------------------------------- Argument Clinic automatically names the functions it generates for you. Occasionally this may cause a problem, if the generated name collides with From 579868e8f1f7995570b2c101b8805e015341f62c Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 24 Jul 2023 06:06:46 -0700 Subject: [PATCH 055/632] [3.11] Fix PyVectorcall_Function doc versionadded (GH-107140) (#107174) Fix PyVectorcall_Function doc versionadded (GH-107140) The documentation implies that PyVectorcall_Function() was available in Python 3.8. This is half-true - it was available under a different name. I think it's clearer to set the "version added" to 3.9. (cherry picked from commit 0a9b339363a59be1249189c767ed6f46fd71e1c7) Co-authored-by: da-woods --- Doc/c-api/call.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/c-api/call.rst b/Doc/c-api/call.rst index a574965bb12605..838af604dca020 100644 --- a/Doc/c-api/call.rst +++ b/Doc/c-api/call.rst @@ -156,7 +156,7 @@ Vectorcall Support API This is mostly useful to check whether or not *op* supports vectorcall, which can be done by checking ``PyVectorcall_Function(op) != NULL``. - .. versionadded:: 3.8 + .. versionadded:: 3.9 .. c:function:: PyObject* PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *dict) From 604eb040598ea4b05e15ad98e0a1781babcad775 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 24 Jul 2023 17:45:13 +0200 Subject: [PATCH 056/632] [3.11] Docs: Add missing markup to Argument Clinic docs (#106876) (#107182) (cherry picked from commit ff5f94b72c8aad8e45c397c263dbe7f19221735f) Co-authored-by: Ezio Melotti Co-authored-by: Serhiy Storchaka --- Doc/howto/clinic.rst | 265 +++++++++++++++++++++++-------------------- 1 file changed, 141 insertions(+), 124 deletions(-) diff --git a/Doc/howto/clinic.rst b/Doc/howto/clinic.rst index 4a83f6dde3a514..2d332f03d91a28 100644 --- a/Doc/howto/clinic.rst +++ b/Doc/howto/clinic.rst @@ -96,7 +96,8 @@ things with all the information you give it. Basic concepts and usage ------------------------ -Argument Clinic ships with CPython; you'll find it in ``Tools/clinic/clinic.py``. +Argument Clinic ships with CPython; you'll find it in +:source:`Tools/clinic/clinic.py`. If you run that script, specifying a C file as an argument: .. code-block:: shell-session @@ -178,9 +179,10 @@ Let's dive in! 1. Find a Python builtin that calls either :c:func:`PyArg_ParseTuple` or :c:func:`PyArg_ParseTupleAndKeywords`, and hasn't been converted to work with Argument Clinic yet. - For my example I'm using ``_pickle.Pickler.dump()``. + For my example I'm using + :py:meth:`_pickle.Pickler.dump `. -2. If the call to the ``PyArg_Parse`` function uses any of the +2. If the call to the :c:func:`!PyArg_Parse*` function uses any of the following format units: .. code-block:: none @@ -197,10 +199,10 @@ Let's dive in! support all of these scenarios. But these are advanced topics—let's do something simpler for your first function. - Also, if the function has multiple calls to :c:func:`PyArg_ParseTuple` + Also, if the function has multiple calls to :c:func:`!PyArg_ParseTuple` or :c:func:`PyArg_ParseTupleAndKeywords` where it supports different types for the same argument, or if the function uses something besides - PyArg_Parse functions to parse its arguments, it probably + :c:func:`!PyArg_Parse*` functions to parse its arguments, it probably isn't suitable for conversion to Argument Clinic. Argument Clinic doesn't support generic functions or polymorphic parameters. @@ -217,7 +219,7 @@ Let's dive in! If the old docstring had a first line that looked like a function signature, throw that line away. (The docstring doesn't need it - anymore—when you use ``help()`` on your builtin in the future, + anymore—when you use :py:func:`help` on your builtin in the future, the first line will be built automatically based on the function's signature.) @@ -264,7 +266,7 @@ Let's dive in! When you declare a class, you must also specify two aspects of its type in C: the type declaration you'd use for a pointer to an instance of - this class, and a pointer to the :c:type:`PyTypeObject` for this class. + this class, and a pointer to the :c:type:`!PyTypeObject` for this class. Sample:: @@ -313,10 +315,10 @@ Let's dive in! Clinic easier. For each parameter, copy the "format unit" for that - parameter from the ``PyArg_Parse()`` format argument and + parameter from the :c:func:`PyArg_Parse` format argument and specify *that* as its converter, as a quoted string. ("format unit" is the formal name for the one-to-three - character substring of the ``format`` parameter that tells + character substring of the *format* parameter that tells the argument parsing function what the type of the variable is and how to convert it. For more on format units please see :ref:`arg-parsing`.) @@ -349,7 +351,7 @@ Let's dive in! itself before the first keyword-only argument, indented the same as the parameter lines. - (``_pickle.Pickler.dump`` has neither, so our sample is unchanged.) + (:py:meth:`!_pickle.Pickler.dump` has neither, so our sample is unchanged.) 10. If the existing C function calls :c:func:`PyArg_ParseTuple` @@ -410,7 +412,7 @@ Let's dive in! 12. Save and close the file, then run ``Tools/clinic/clinic.py`` on it. With luck everything worked---your block now has output, and - a ``.c.h`` file has been generated! Reopen the file in your + a :file:`.c.h` file has been generated! Reopen the file in your text editor to see:: /*[clinic input] @@ -431,8 +433,8 @@ Let's dive in! it found an error in your input. Keep fixing your errors and retrying until Argument Clinic processes your file without complaint. - For readability, most of the glue code has been generated to a ``.c.h`` - file. You'll need to include that in your original ``.c`` file, + For readability, most of the glue code has been generated to a :file:`.c.h` + file. You'll need to include that in your original :file:`.c` file, typically right after the clinic module block:: #include "clinic/_pickle.c.h" @@ -446,8 +448,8 @@ Let's dive in! ensure that the code generated by Argument Clinic calls the *exact* same function. - Second, the format string passed in to :c:func:`PyArg_ParseTuple` or - :c:func:`PyArg_ParseTupleAndKeywords` should be *exactly* the same + Second, the format string passed in to :c:func:`!PyArg_ParseTuple` or + :c:func:`!PyArg_ParseTupleAndKeywords` should be *exactly* the same as the hand-written one in the existing function, up to the colon or semi-colon. @@ -469,7 +471,7 @@ Let's dive in! {"dump", (PyCFunction)__pickle_Pickler_dump, METH_O, __pickle_Pickler_dump__doc__}, This static structure should be *exactly* the same as the existing static - :c:type:`PyMethodDef` structure for this builtin. + :c:type:`!PyMethodDef` structure for this builtin. If any of these items differ in *any way*, adjust your Argument Clinic function specification and rerun @@ -539,14 +541,14 @@ Let's dive in! ... 15. Remember the macro with the :c:type:`PyMethodDef` structure for this - function? Find the existing :c:type:`PyMethodDef` structure for this + function? Find the existing :c:type:`!PyMethodDef` structure for this function and replace it with a reference to the macro. (If the builtin is at module scope, this will probably be very near the end of the file; if the builtin is a class method, this will probably be below but relatively near to the implementation.) Note that the body of the macro contains a trailing comma. So when you - replace the existing static :c:type:`PyMethodDef` structure with the macro, + replace the existing static :c:type:`!PyMethodDef` structure with the macro, *don't* add a comma to the end. Sample:: @@ -562,7 +564,7 @@ Let's dive in! This change should not introduce any new compile-time warnings or errors, and there should be no externally visible change to Python's behavior. - Well, except for one difference: ``inspect.signature()`` run on your function + Well, except for one difference: :py:func:`inspect.signature` run on your function should now provide a valid signature! Congratulations, you've ported your first function to work with Argument Clinic! @@ -586,15 +588,15 @@ Argument Clinic will use that function name for the base (generated) function, then add ``"_impl"`` to the end and use that for the name of the impl function. For example, if we wanted to rename the C function names generated for -``pickle.Pickler.dump``, it'd look like this:: +:py:meth:`pickle.Pickler.dump`, it'd look like this:: /*[clinic input] pickle.Pickler.dump as pickler_dumper ... -The base function would now be named ``pickler_dumper()``, -and the impl function would now be named ``pickler_dumper_impl()``. +The base function would now be named :c:func:`!pickler_dumper`, +and the impl function would now be named :c:func:`!pickler_dumper_impl`. Similarly, you may have a problem where you want to give a parameter @@ -612,9 +614,9 @@ using the same ``"as"`` syntax:: fix_imports: bool = True Here, the name used in Python (in the signature and the ``keywords`` -array) would be ``file``, but the C variable would be named ``file_obj``. +array) would be *file*, but the C variable would be named ``file_obj``. -You can use this to rename the ``self`` parameter too! +You can use this to rename the *self* parameter too! How to convert functions using ``PyArg_UnpackTuple`` @@ -622,7 +624,7 @@ How to convert functions using ``PyArg_UnpackTuple`` To convert a function parsing its arguments with :c:func:`PyArg_UnpackTuple`, simply write out all the arguments, specifying each as an ``object``. You -may specify the ``type`` argument to cast the type as appropriate. All +may specify the *type* argument to cast the type as appropriate. All arguments should be marked positional-only (add a ``/`` on a line by itself after the last argument). @@ -641,16 +643,16 @@ keyword-only arguments.) This approach was used to simulate optional arguments back before :c:func:`PyArg_ParseTupleAndKeywords` was created. While functions using this approach can often be converted to -use :c:func:`PyArg_ParseTupleAndKeywords`, optional arguments, and default values, +use :c:func:`!PyArg_ParseTupleAndKeywords`, optional arguments, and default values, it's not always possible. Some of these legacy functions have -behaviors :c:func:`PyArg_ParseTupleAndKeywords` doesn't directly support. -The most obvious example is the builtin function ``range()``, which has +behaviors :c:func:`!PyArg_ParseTupleAndKeywords` doesn't directly support. +The most obvious example is the builtin function :py:func:`range`, which has an optional argument on the *left* side of its required argument! -Another example is ``curses.window.addch()``, which has a group of two +Another example is :py:meth:`curses.window.addch`, which has a group of two arguments that must always be specified together. (The arguments are -called ``x`` and ``y``; if you call the function passing in ``x``, -you must also pass in ``y``—and if you don't pass in ``x`` you may not -pass in ``y`` either.) +called *x* and *y*; if you call the function passing in *x*, +you must also pass in *y* — and if you don't pass in *x* you may not +pass in *y* either.) In any case, the goal of Argument Clinic is to support argument parsing for all existing CPython builtins without changing their semantics. @@ -671,7 +673,7 @@ can *only* be used with positional-only parameters. To specify an optional group, add a ``[`` on a line by itself before the parameters you wish to group together, and a ``]`` on a line by itself -after these parameters. As an example, here's how ``curses.window.addch`` +after these parameters. As an example, here's how :py:meth:`curses.window.addch` uses optional groups to make the first two parameters and the last parameter optional:: @@ -757,14 +759,14 @@ the same converters. All arguments to Argument Clinic converters are keyword-only. All Argument Clinic converters accept the following arguments: - ``c_default`` + *c_default* The default value for this parameter when defined in C. Specifically, this will be the initializer for the variable declared in the "parse function". See :ref:`the section on default values ` for how to use this. Specified as a string. - ``annotation`` + *annotation* The annotation value for this parameter. Not currently supported, because :pep:`8` mandates that the Python library may not use annotations. @@ -772,7 +774,7 @@ All Argument Clinic converters accept the following arguments: In addition, some converters accept additional arguments. Here is a list of these arguments, along with their meanings: - ``accept`` + *accept* A set of Python types (and possibly pseudo-types); this restricts the allowable Python argument to values of these types. (This is not a general-purpose facility; as a rule it only supports @@ -780,38 +782,38 @@ of these arguments, along with their meanings: To accept ``None``, add ``NoneType`` to this set. - ``bitwise`` + *bitwise* Only supported for unsigned integers. The native integer value of this Python argument will be written to the parameter without any range checking, even for negative values. - ``converter`` + *converter* Only supported by the ``object`` converter. Specifies the name of a :ref:`C "converter function" ` to use to convert this object to a native type. - ``encoding`` + *encoding* Only supported for strings. Specifies the encoding to use when converting this string from a Python str (Unicode) value into a C ``char *`` value. - ``subclass_of`` + *subclass_of* Only supported for the ``object`` converter. Requires that the Python value be a subclass of a Python type, as expressed in C. - ``type`` + *type* Only supported for the ``object`` and ``self`` converters. Specifies the C type that will be used to declare the variable. Default value is ``"PyObject *"``. - ``zeroes`` + *zeroes* Only supported for strings. If true, embedded NUL bytes (``'\\0'``) are permitted inside the value. The length of the string will be passed in to the impl function, just after the string parameter, as a parameter named ``_length``. Please note, not every possible combination of arguments will work. -Usually these arguments are implemented by specific ``PyArg_ParseTuple`` +Usually these arguments are implemented by specific :c:func:`PyArg_ParseTuple` *format units*, with specific behavior. For example, currently you cannot call ``unsigned_short`` without also specifying ``bitwise=True``. Although it's perfectly reasonable to think this would work, these semantics don't @@ -911,19 +913,19 @@ conversion functions, or types, or strings specifying an encoding. (But "legacy converters" don't support arguments. That's why we skipped them for your first function.) The argument you specified to the format unit is now an argument to the converter; this -argument is either ``converter`` (for ``O&``), ``subclass_of`` (for ``O!``), -or ``encoding`` (for all the format units that start with ``e``). +argument is either *converter* (for ``O&``), *subclass_of* (for ``O!``), +or *encoding* (for all the format units that start with ``e``). -When using ``subclass_of``, you may also want to use the other -custom argument for ``object()``: ``type``, which lets you set the type +When using *subclass_of*, you may also want to use the other +custom argument for ``object()``: *type*, which lets you set the type actually used for the parameter. For example, if you want to ensure -that the object is a subclass of ``PyUnicode_Type``, you probably want +that the object is a subclass of :c:var:`PyUnicode_Type`, you probably want to use the converter ``object(type='PyUnicodeObject *', subclass_of='&PyUnicode_Type')``. One possible problem with using Argument Clinic: it takes away some possible flexibility for the format units starting with ``e``. When writing a -``PyArg_Parse`` call by hand, you could theoretically decide at runtime what -encoding string to pass in to :c:func:`PyArg_ParseTuple`. But now this string must +:c:func:`!PyArg_Parse*` call by hand, you could theoretically decide at runtime what +encoding string to pass to that call. But now this string must be hard-coded at Argument-Clinic-preprocessing-time. This limitation is deliberate; it made supporting this format unit much easier, and may allow for future optimizations. This restriction doesn't seem unreasonable; CPython itself always passes in static @@ -976,7 +978,7 @@ expression. Currently the following are explicitly supported: * Numeric constants (integer and float) * String constants * ``True``, ``False``, and ``None`` -* Simple symbolic constants like ``sys.maxsize``, which must +* Simple symbolic constants like :py:data:`sys.maxsize`, which must start with the name of the module (In the future, this may need to get even more elaborate, @@ -997,28 +999,28 @@ Consider the following example: foo: Py_ssize_t = sys.maxsize - 1 -``sys.maxsize`` can have different values on different platforms. Therefore +:py:data:`sys.maxsize` can have different values on different platforms. Therefore Argument Clinic can't simply evaluate that expression locally and hard-code it in C. So it stores the default in such a way that it will get evaluated at runtime, when the user asks for the function's signature. What namespace is available when the expression is evaluated? It's evaluated in the context of the module the builtin came from. So, if your module has an -attribute called "``max_widgets``", you may simply use it: +attribute called :py:attr:`!max_widgets`, you may simply use it: .. code-block:: none foo: Py_ssize_t = max_widgets If the symbol isn't found in the current module, it fails over to looking in -``sys.modules``. That's how it can find ``sys.maxsize`` for example. (Since you -don't know in advance what modules the user will load into their interpreter, +:py:data:`sys.modules`. That's how it can find :py:data:`sys.maxsize` for example. +(Since you don't know in advance what modules the user will load into their interpreter, it's best to restrict yourself to modules that are preloaded by Python itself.) Evaluating default values only at runtime means Argument Clinic can't compute the correct equivalent C default value. So you need to tell it explicitly. When you use an expression, you must also specify the equivalent expression -in C, using the ``c_default`` parameter to the converter: +in C, using the *c_default* parameter to the converter: .. code-block:: none @@ -1084,7 +1086,7 @@ indicate an error has occurred? Normally, a function returns a valid (non-``NUL pointer for success, and ``NULL`` for failure. But if you use an integer return converter, all integers are valid. How can Argument Clinic detect an error? Its solution: each return converter implicitly looks for a special value that indicates an error. If you return -that value, and an error has been set (``PyErr_Occurred()`` returns a true +that value, and an error has been set (c:func:`PyErr_Occurred` returns a true value), then the generated code will propagate the error. Otherwise it will encode the value you return like normal. @@ -1195,9 +1197,9 @@ using a default converter. It automatically sets the ``type`` of this parameter to the "pointer to an instance" you specified when you declared the type. However, you can override Argument Clinic's converter and specify one yourself. -Just add your own ``self`` parameter as the first parameter in a +Just add your own *self* parameter as the first parameter in a block, and ensure that its converter is an instance of -``self_converter`` or a subclass thereof. +:class:`!self_converter` or a subclass thereof. What's the point? This lets you override the type of ``self``, or give it a different default name. @@ -1205,7 +1207,7 @@ or give it a different default name. How do you specify the custom type you want to cast ``self`` to? If you only have one or two functions with the same type for ``self``, you can directly use Argument Clinic's existing ``self`` converter, -passing in the type you want to use as the ``type`` parameter:: +passing in the type you want to use as the *type* parameter:: /*[clinic input] @@ -1220,7 +1222,7 @@ passing in the type you want to use as the ``type`` parameter:: On the other hand, if you have a lot of functions that will use the same type for ``self``, it's best to create your own converter, subclassing -``self_converter`` but overwriting the ``type`` member:: +:class:`!self_converter` but overwriting the :py:attr:`!type` member:: /*[python input] class PicklerObject_converter(self_converter): @@ -1248,8 +1250,8 @@ module level state. Use :c:func:`PyType_FromModuleAndSpec` to associate a new heap type with a module. You can now use :c:func:`PyType_GetModuleState` on the defining class to fetch the module state, for example from a module method. -Example from ``Modules/zlibmodule.c``. First, ``defining_class`` is added to -the clinic input:: +Example from :source:`Modules/zlibmodule.c`. +First, ``defining_class`` is added to the clinic input:: /*[clinic input] zlib.Compress.compress @@ -1279,16 +1281,17 @@ module state:: Each method may only have one argument using this converter, and it must appear after ``self``, or, if ``self`` is not used, as the first argument. The argument will be of type ``PyTypeObject *``. The argument will not appear in the -``__text_signature__``. +:py:attr:`!__text_signature__`. -The ``defining_class`` converter is not compatible with ``__init__`` and ``__new__`` -methods, which cannot use the ``METH_METHOD`` convention. +The ``defining_class`` converter is not compatible with :py:meth:`!__init__` +and :py:meth:`!__new__` methods, which cannot use the :c:macro:`METH_METHOD` +convention. It is not possible to use ``defining_class`` with slot methods. In order to fetch the module state from such methods, use :c:func:`PyType_GetModuleByDef` to look up the module and then :c:func:`PyModule_GetState` to fetch the module state. Example from the ``setattro`` slot method in -``Modules/_threadmodule.c``:: +:source:`Modules/_threadmodule.c`:: static int local_setattro(localobject *self, PyObject *name, PyObject *v) @@ -1306,7 +1309,7 @@ How to write a custom converter ------------------------------- As we hinted at in the previous section... you can write your own converters! -A converter is simply a Python class that inherits from ``CConverter``. +A converter is simply a Python class that inherits from :py:class:`!CConverter`. The main purpose of a custom converter is if you have a parameter using the ``O&`` format unit—parsing this parameter means calling a :c:func:`PyArg_ParseTuple` "converter function". @@ -1317,61 +1320,74 @@ will be automatically registered with Argument Clinic; its name will be the name of your class with the ``_converter`` suffix stripped off. (This is accomplished with a metaclass.) -You shouldn't subclass ``CConverter.__init__``. Instead, you should -write a ``converter_init()`` function. ``converter_init()`` -always accepts a ``self`` parameter; after that, all additional +You shouldn't subclass :py:meth:`!CConverter.__init__`. Instead, you should +write a :py:meth:`!converter_init` function. :py:meth:`!converter_init` +always accepts a *self* parameter; after that, all additional parameters *must* be keyword-only. Any arguments passed in to the converter in Argument Clinic will be passed along to your -``converter_init()``. +:py:meth:`!converter_init`. -There are some additional members of ``CConverter`` you may wish +There are some additional members of :py:class:`!CConverter` you may wish to specify in your subclass. Here's the current list: -``type`` - The C type to use for this variable. - ``type`` should be a Python string specifying the type, e.g. ``int``. - If this is a pointer type, the type string should end with ``' *'``. +.. module:: clinic -``default`` - The Python default value for this parameter, as a Python value. - Or the magic value ``unspecified`` if there is no default. +.. class:: CConverter -``py_default`` - ``default`` as it should appear in Python code, - as a string. - Or ``None`` if there is no default. + .. attribute:: type -``c_default`` - ``default`` as it should appear in C code, - as a string. - Or ``None`` if there is no default. + The C type to use for this variable. + :attr:`!type` should be a Python string specifying the type, + e.g. ``'int'``. + If this is a pointer type, the type string should end with ``' *'``. -``c_ignored_default`` - The default value used to initialize the C variable when - there is no default, but not specifying a default may - result in an "uninitialized variable" warning. This can - easily happen when using option groups—although - properly written code will never actually use this value, - the variable does get passed in to the impl, and the - C compiler will complain about the "use" of the - uninitialized value. This value should always be a - non-empty string. + .. attribute:: default -``converter`` - The name of the C converter function, as a string. + The Python default value for this parameter, as a Python value. + Or the magic value ``unspecified`` if there is no default. -``impl_by_reference`` - A boolean value. If true, - Argument Clinic will add a ``&`` in front of the name of - the variable when passing it into the impl function. + .. attribute:: py_default -``parse_by_reference`` - A boolean value. If true, - Argument Clinic will add a ``&`` in front of the name of - the variable when passing it into :c:func:`PyArg_ParseTuple`. + :attr:`!default` as it should appear in Python code, + as a string. + Or ``None`` if there is no default. + .. attribute:: c_default -Here's the simplest example of a custom converter, from ``Modules/zlibmodule.c``:: + :attr:`!default` as it should appear in C code, + as a string. + Or ``None`` if there is no default. + + .. attribute:: c_ignored_default + + The default value used to initialize the C variable when + there is no default, but not specifying a default may + result in an "uninitialized variable" warning. This can + easily happen when using option groups—although + properly written code will never actually use this value, + the variable does get passed in to the impl, and the + C compiler will complain about the "use" of the + uninitialized value. This value should always be a + non-empty string. + + .. attribute:: converter + + The name of the C converter function, as a string. + + .. attribute:: impl_by_reference + + A boolean value. If true, + Argument Clinic will add a ``&`` in front of the name of + the variable when passing it into the impl function. + + .. attribute:: parse_by_reference + + A boolean value. If true, + Argument Clinic will add a ``&`` in front of the name of + the variable when passing it into :c:func:`PyArg_ParseTuple`. + + +Here's the simplest example of a custom converter, from :source:`Modules/zlibmodule.c`:: /*[python input] @@ -1391,7 +1407,7 @@ automatically support default values. More sophisticated custom converters can insert custom C code to handle initialization and cleanup. You can see more examples of custom converters in the CPython -source tree; grep the C files for the string ``CConverter``. +source tree; grep the C files for the string :py:class:`!CConverter`. How to write a custom return converter @@ -1401,18 +1417,18 @@ Writing a custom return converter is much like writing a custom converter. Except it's somewhat simpler, because return converters are themselves much simpler. -Return converters must subclass ``CReturnConverter``. +Return converters must subclass :py:class:`!CReturnConverter`. There are no examples yet of custom return converters, because they are not widely used yet. If you wish to -write your own return converter, please read ``Tools/clinic/clinic.py``, -specifically the implementation of ``CReturnConverter`` and +write your own return converter, please read :source:`Tools/clinic/clinic.py`, +specifically the implementation of :py:class:`!CReturnConverter` and all its subclasses. How to convert ``METH_O`` and ``METH_NOARGS`` functions ------------------------------------------------------- -To convert a function using ``METH_O``, make sure the function's +To convert a function using :c:macro:`METH_O`, make sure the function's single argument is using the ``object`` converter, and mark the arguments as positional-only:: @@ -1424,24 +1440,25 @@ arguments as positional-only:: [clinic start generated code]*/ -To convert a function using ``METH_NOARGS``, just don't specify +To convert a function using :c:macro:`METH_NOARGS`, just don't specify any arguments. You can still use a self converter, a return converter, and specify -a ``type`` argument to the object converter for ``METH_O``. +a *type* argument to the object converter for :c:macro:`METH_O`. How to convert ``tp_new`` and ``tp_init`` functions --------------------------------------------------- -You can convert ``tp_new`` and ``tp_init`` functions. Just name -them ``__new__`` or ``__init__`` as appropriate. Notes: +You can convert :c:member:`~PyTypeObject.tp_new` and +:c:member:`~PyTypeObject.tp_init` functions. +Just name them ``__new__`` or ``__init__`` as appropriate. Notes: * The function name generated for ``__new__`` doesn't end in ``__new__`` like it would by default. It's just the name of the class, converted into a valid C identifier. -* No ``PyMethodDef`` ``#define`` is generated for these functions. +* No :c:type:`PyMethodDef` ``#define`` is generated for these functions. * ``__init__`` functions return ``int``, not ``PyObject *``. @@ -1476,7 +1493,7 @@ Let's start with defining some terminology: *field* A field, in this context, is a subsection of Clinic's output. - For example, the ``#define`` for the ``PyMethodDef`` structure + For example, the ``#define`` for the :c:type:`PyMethodDef` structure is a field, called ``methoddef_define``. Clinic has seven different fields it can output per function definition: @@ -1520,8 +1537,8 @@ Let's start with defining some terminology: The filename chosen for the file is ``{basename}.clinic{extension}``, where ``basename`` and ``extension`` were assigned the output from ``os.path.splitext()`` run on the current file. (Example: - the ``file`` destination for ``_pickle.c`` would be written to - ``_pickle.clinic.c``.) + the ``file`` destination for :file:`_pickle.c` would be written to + :file:`_pickle.clinic.c`.) **Important: When using a** ``file`` **destination, you** *must check in* **the generated file!** @@ -1774,7 +1791,7 @@ like so:: } #endif /* HAVE_FUNCTIONNAME */ -Then, remove those three lines from the ``PyMethodDef`` structure, +Then, remove those three lines from the :c:type:`PyMethodDef` structure, replacing them with the macro Argument Clinic generated: .. code-block:: none @@ -1815,7 +1832,7 @@ This may mean that you get a complaint from Argument Clinic: When this happens, just open your file, find the ``dump buffer`` block that Argument Clinic added to your file (it'll be at the very bottom), then -move it above the ``PyMethodDef`` structure where that macro is used. +move it above the :c:type:`PyMethodDef` structure where that macro is used. How to use Argument Clinic in Python files From 8725d36fda2125fd089e6a714ba70089ba7498db Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 24 Jul 2023 09:39:43 -0700 Subject: [PATCH 057/632] [3.11] gh-107017: Change Chapter Strings to Texts in the Introduction chapter. (GH-107104) (#107168) Co-authored-by: TommyUnreal <45427816+TommyUnreal@users.noreply.github.com> Co-authored-by: Hugo van Kemenade --- Doc/tutorial/introduction.rst | 40 +++++++++++++++++------------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/Doc/tutorial/introduction.rst b/Doc/tutorial/introduction.rst index e8b582dfe85d28..24ae720fe2bb62 100644 --- a/Doc/tutorial/introduction.rst +++ b/Doc/tutorial/introduction.rst @@ -138,16 +138,25 @@ and uses the ``j`` or ``J`` suffix to indicate the imaginary part .. _tut-strings: -Strings -------- +Text +---- -Besides numbers, Python can also manipulate strings, which can be expressed -in several ways. They can be enclosed in single quotes (``'...'``) or -double quotes (``"..."``) with the same result [#]_. ``\`` can be used -to escape quotes:: +Python can manipulate text (represented by type :class:`str`, so-called +"strings") as well as numbers. This includes characters "``!``", words +"``rabbit``", names "``Paris``", sentences "``Got your back.``", etc. +"``Yay! :)``". They can be enclosed in single quotes (``'...'``) or double +quotes (``"..."``) with the same result [#]_. >>> 'spam eggs' # single quotes 'spam eggs' + >>> "Paris rabbit got your back :)! Yay!" # double quotes + 'Paris rabbit got your back :)! Yay!' + >>> '1975' # digits and numerals enclosed in quotes are also strings + '1975' + +To quote a quote, we need to "escape" it, by preceding it with ``\``. +Alternatively, we can use the other type of quotation marks:: + >>> 'doesn\'t' # use \' to escape the single quote... "doesn't" >>> "doesn't" # ...or use double quotes instead @@ -159,23 +168,14 @@ to escape quotes:: >>> '"Isn\'t," they said.' '"Isn\'t," they said.' -In the interactive interpreter, the output string is enclosed in quotes and -special characters are escaped with backslashes. While this might sometimes -look different from the input (the enclosing quotes could change), the two -strings are equivalent. The string is enclosed in double quotes if -the string contains a single quote and no double quotes, otherwise it is -enclosed in single quotes. The :func:`print` function produces a more -readable output, by omitting the enclosing quotes and by printing escaped -and special characters:: +In the Python shell, the string definition and output string can look +different. The :func:`print` function produces a more readable output, by +omitting the enclosing quotes and by printing escaped and special characters:: - >>> '"Isn\'t," they said.' - '"Isn\'t," they said.' - >>> print('"Isn\'t," they said.') - "Isn't," they said. >>> s = 'First line.\nSecond line.' # \n means newline - >>> s # without print(), \n is included in the output + >>> s # without print(), special characters are included in the string 'First line.\nSecond line.' - >>> print(s) # with print(), \n produces a new line + >>> print(s) # with print(), special characters are interpreted, so \n produces new line First line. Second line. From 058741cc39dd2b63907931e2b626f7428ba13253 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 25 Jul 2023 11:38:19 +0100 Subject: [PATCH 058/632] [3.11] gh-106774: Update bundled pip version to 23.2.1 (GH-106775) (gh-107223) * Update bundled pip version to 23.2.1. (cherry picked from commit f443b54a2f14e386a91fe4b09f41a265445008b8) --- Lib/ensurepip/__init__.py | 2 +- ...ne-any.whl => pip-23.2.1-py3-none-any.whl} | Bin 2064688 -> 2086091 bytes ...-07-15-10-24-56.gh-issue-106774.FJcqCj.rst | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) rename Lib/ensurepip/_bundled/{pip-23.1.2-py3-none-any.whl => pip-23.2.1-py3-none-any.whl} (74%) create mode 100644 Misc/NEWS.d/next/Library/2023-07-15-10-24-56.gh-issue-106774.FJcqCj.rst diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py index cac9d30b7a8fca..81db9fa922d049 100644 --- a/Lib/ensurepip/__init__.py +++ b/Lib/ensurepip/__init__.py @@ -11,7 +11,7 @@ __all__ = ["version", "bootstrap"] _PACKAGE_NAMES = ('setuptools', 'pip') _SETUPTOOLS_VERSION = "65.5.0" -_PIP_VERSION = "23.1.2" +_PIP_VERSION = "23.2.1" _PROJECTS = [ ("setuptools", _SETUPTOOLS_VERSION, "py3"), ("pip", _PIP_VERSION, "py3"), diff --git a/Lib/ensurepip/_bundled/pip-23.1.2-py3-none-any.whl b/Lib/ensurepip/_bundled/pip-23.2.1-py3-none-any.whl similarity index 74% rename from Lib/ensurepip/_bundled/pip-23.1.2-py3-none-any.whl rename to Lib/ensurepip/_bundled/pip-23.2.1-py3-none-any.whl index 6a2515615ccda3148bf0828450d9149ef13819ac..ba28ef02e265f032560e28e40b2be0bd6e2e964f 100644 GIT binary patch delta 526875 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 G7c`Eh_FtvxDpt){n@X+;qEBzrEzR>n^8Jy=A-{ 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%0A+Kk$=VpdcGl~KsPbe!!Z|5n$ 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

S{iXE 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 &({F4A#dlFmXi8h5WeD0fI8s_N^7N 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 RarJRUq_bEVAIXi~{{Rr(Am0E0 diff --git a/Misc/NEWS.d/next/Library/2023-07-15-10-24-56.gh-issue-106774.FJcqCj.rst b/Misc/NEWS.d/next/Library/2023-07-15-10-24-56.gh-issue-106774.FJcqCj.rst new file mode 100644 index 00000000000000..ed467573b89e14 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-07-15-10-24-56.gh-issue-106774.FJcqCj.rst @@ -0,0 +1 @@ +Update the bundled copy of pip to version 23.2.1. From b8b3e6afc0a48c3cbb7c36d2f73e332edcd6058c Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 25 Jul 2023 14:15:14 +0300 Subject: [PATCH 059/632] [3.11] gh-99612: Fix PyUnicode_DecodeUTF8Stateful() for ASCII-only data (GH-99613) (GH-107224) Previously *consumed was not set in this case. (cherry picked from commit f08e52ccb027f6f703302b8c1a82db9fd3934270) --- Lib/test/test_capi/test_codecs.py | 54 +++++++++++++++++++ ...2-11-20-09-52-50.gh-issue-99612.eBHksg.rst | 2 + Modules/_testcapimodule.c | 37 ++++++++++++- Objects/unicodeobject.c | 3 ++ 4 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 Lib/test/test_capi/test_codecs.py create mode 100644 Misc/NEWS.d/next/C API/2022-11-20-09-52-50.gh-issue-99612.eBHksg.rst diff --git a/Lib/test/test_capi/test_codecs.py b/Lib/test/test_capi/test_codecs.py new file mode 100644 index 00000000000000..e46726192aa05b --- /dev/null +++ b/Lib/test/test_capi/test_codecs.py @@ -0,0 +1,54 @@ +import unittest +from test.support import import_helper + +_testcapi = import_helper.import_module('_testcapi') + + +class CAPITest(unittest.TestCase): + + def test_decodeutf8(self): + """Test PyUnicode_DecodeUTF8()""" + decodeutf8 = _testcapi.unicode_decodeutf8 + + for s in ['abc', '\xa1\xa2', '\u4f60\u597d', 'a\U0001f600']: + b = s.encode('utf-8') + self.assertEqual(decodeutf8(b), s) + self.assertEqual(decodeutf8(b, 'strict'), s) + + self.assertRaises(UnicodeDecodeError, decodeutf8, b'\x80') + self.assertRaises(UnicodeDecodeError, decodeutf8, b'\xc0') + self.assertRaises(UnicodeDecodeError, decodeutf8, b'\xff') + self.assertRaises(UnicodeDecodeError, decodeutf8, b'a\xf0\x9f') + self.assertEqual(decodeutf8(b'a\xf0\x9f', 'replace'), 'a\ufffd') + self.assertEqual(decodeutf8(b'a\xf0\x9fb', 'replace'), 'a\ufffdb') + + self.assertRaises(LookupError, decodeutf8, b'a\x80', 'foo') + # TODO: Test PyUnicode_DecodeUTF8() with NULL as data and + # negative size. + + def test_decodeutf8stateful(self): + """Test PyUnicode_DecodeUTF8Stateful()""" + decodeutf8stateful = _testcapi.unicode_decodeutf8stateful + + for s in ['abc', '\xa1\xa2', '\u4f60\u597d', 'a\U0001f600']: + b = s.encode('utf-8') + self.assertEqual(decodeutf8stateful(b), (s, len(b))) + self.assertEqual(decodeutf8stateful(b, 'strict'), (s, len(b))) + + self.assertRaises(UnicodeDecodeError, decodeutf8stateful, b'\x80') + self.assertRaises(UnicodeDecodeError, decodeutf8stateful, b'\xc0') + self.assertRaises(UnicodeDecodeError, decodeutf8stateful, b'\xff') + self.assertEqual(decodeutf8stateful(b'a\xf0\x9f'), ('a', 1)) + self.assertEqual(decodeutf8stateful(b'a\xf0\x9f', 'replace'), ('a', 1)) + self.assertRaises(UnicodeDecodeError, decodeutf8stateful, b'a\xf0\x9fb') + self.assertEqual(decodeutf8stateful(b'a\xf0\x9fb', 'replace'), ('a\ufffdb', 4)) + + self.assertRaises(LookupError, decodeutf8stateful, b'a\x80', 'foo') + # TODO: Test PyUnicode_DecodeUTF8Stateful() with NULL as data and + # negative size. + # TODO: Test PyUnicode_DecodeUTF8Stateful() with NULL as the address of + # "consumed". + + +if __name__ == "__main__": + unittest.main() diff --git a/Misc/NEWS.d/next/C API/2022-11-20-09-52-50.gh-issue-99612.eBHksg.rst b/Misc/NEWS.d/next/C API/2022-11-20-09-52-50.gh-issue-99612.eBHksg.rst new file mode 100644 index 00000000000000..40e3c8db5403c7 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2022-11-20-09-52-50.gh-issue-99612.eBHksg.rst @@ -0,0 +1,2 @@ +Fix :c:func:`PyUnicode_DecodeUTF8Stateful` for ASCII-only data: +``*consumed`` was not set. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index b40368eb4b3409..b29a919a8325b6 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -2307,6 +2307,40 @@ unicode_asutf8andsize(PyObject *self, PyObject *args) return Py_BuildValue("(Nn)", result, utf8_len); } +/* Test PyUnicode_DecodeUTF8() */ +static PyObject * +unicode_decodeutf8(PyObject *self, PyObject *args) +{ + const char *data; + Py_ssize_t size; + const char *errors = NULL; + + if (!PyArg_ParseTuple(args, "y#|z", &data, &size, &errors)) + return NULL; + + return PyUnicode_DecodeUTF8(data, size, errors); +} + +/* Test PyUnicode_DecodeUTF8Stateful() */ +static PyObject * +unicode_decodeutf8stateful(PyObject *self, PyObject *args) +{ + const char *data; + Py_ssize_t size; + const char *errors = NULL; + Py_ssize_t consumed = 123456789; + PyObject *result; + + if (!PyArg_ParseTuple(args, "y#|z", &data, &size, &errors)) + return NULL; + + result = PyUnicode_DecodeUTF8Stateful(data, size, errors, &consumed); + if (!result) { + return NULL; + } + return Py_BuildValue("(Nn)", result, consumed); +} + static PyObject * unicode_findchar(PyObject *self, PyObject *args) { @@ -6562,7 +6596,8 @@ static PyMethodDef TestMethods[] = { {"unicode_asucs4", unicode_asucs4, METH_VARARGS}, {"unicode_asutf8", unicode_asutf8, METH_VARARGS}, {"unicode_asutf8andsize", unicode_asutf8andsize, METH_VARARGS}, - {"unicode_findchar", unicode_findchar, METH_VARARGS}, + {"unicode_decodeutf8", unicode_decodeutf8, METH_VARARGS}, + {"unicode_decodeutf8stateful",unicode_decodeutf8stateful, METH_VARARGS}, {"unicode_findchar", unicode_findchar, METH_VARARGS}, {"unicode_copycharacters", unicode_copycharacters, METH_VARARGS}, #if USE_UNICODE_WCHAR_CACHE {"unicode_legacy_string", unicode_legacy_string, METH_VARARGS}, diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 50deefe0adc2cc..ffa8b982bdc29f 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -5120,6 +5120,9 @@ unicode_decode_utf8(const char *s, Py_ssize_t size, } s += ascii_decode(s, end, PyUnicode_1BYTE_DATA(u)); if (s == end) { + if (consumed) { + *consumed = size; + } return u; } From 1bc160947ea58fb3d11fa8667266bc80e9a14b7f Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 25 Jul 2023 05:17:14 -0700 Subject: [PATCH 060/632] [3.11] gh-106996: Add a how-to section to the turtle documentation (GH-107153) (#107234) Co-authored-by: Daniele Procida Co-authored-by: Hugo van Kemenade --- Doc/library/turtle.rst | 114 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/Doc/library/turtle.rst b/Doc/library/turtle.rst index 03b299ba7279c9..262739ec2bbd5d 100644 --- a/Doc/library/turtle.rst +++ b/Doc/library/turtle.rst @@ -46,6 +46,8 @@ graphical output it can be a way to do that without the overhead of introducing more complex or external libraries into their work. +.. _turtle-tutorial: + Tutorial ======== @@ -165,6 +167,118 @@ Finally, complete the filling:: ``end_fill()`` command.) +.. _turtle-how-to: + +How to... +========= + +This section covers some typical turtle use-cases and approaches. + + +Get started as quickly as possible +---------------------------------- + +One of the joys of turtle graphics is the immediate, visual feedback that's +available from simple commands - it's an excellent way to introduce children +to programming ideas, with a minimum of overhead (not just children, of +course). + +The turtle module makes this possible by exposing all its basic functionality +as functions, available with ``from turtle import *``. The :ref:`turtle +graphics tutorial ` covers this approach. + +It's worth noting that many of the turtle commands also have even more terse +equivalents, such as ``fd()`` for :func:`forward`. These are especially +useful when working with learners for whom typing is not a skill. + +.. _note: + + You'll need to have the :mod:`Tk interface package ` installed on + your system for turtle graphics to work. Be warned that this is not + always straightforward, so check this in advance if you're planning to + use turtle graphics with a learner. + + +Use the ``turtle`` module namespace +----------------------------------- + +Using ``from turtle import *`` is convenient - but be warned that it imports a +rather large collection of objects, and if you're doing anything but turtle +graphics you run the risk of a name conflict (this becomes even more an issue +if you're using turtle graphics in a script where other modules might be +imported). + +The solution is to use ``import turtle`` - ``fd()`` becomes +``turtle.fd()``, ``width()`` becomes ``turtle.width()`` and so on. (If typing +"turtle" over and over again becomes tedious, use for example ``import turtle +as t`` instead.) + + +Use turtle graphics in a script +------------------------------- + +It's recommended to use the ``turtle`` module namespace as described +immediately above, for example:: + + import turtle as t + from random import random + + for i in range(100): + steps = int(random() * 100) + angle = int(random() * 360) + t.right(angle) + t.fd(steps) + +Another step is also required though - as soon as the script ends, Python +will also close the turtle's window. Add:: + + t.mainloop() + +to the end of the script. The script will now wait to be dismissed and +will not exit until it is terminated, for example by closing the turtle +graphics window. + + +Use object-oriented turtle graphics +----------------------------------- + +.. seealso:: :ref:`Explanation of the object-oriented interface ` + +Other than for very basic introductory purposes, or for trying things out +as quickly as possible, it's more usual and much more powerful to use the +object-oriented approach to turtle graphics. For example, this allows +multiple turtles on screen at once. + +In this approach, the various turtle commands are methods of objects (mostly of +``Turtle`` objects). You *can* use the object-oriented approach in the shell, +but it would be more typical in a Python script. + +The example above then becomes:: + + from turtle import Turtle + from random import random + + t = Turtle() + for i in range(100): + steps = int(random() * 100) + angle = int(random() * 360) + t.right(angle) + t.fd(steps) + + t.screen.mainloop() + +Note the last line. ``t.screen`` is an instance of the :class:`Screen` +that a Turtle instance exists on; it's created automatically along with +the turtle. + +The turtle's screen can be customised, for example:: + + t.screen.title('Object-oriented turtle demo') + t.screen.bgcolor("orange") + + +.. _turtle-explanation: + Explanation =========== From db03cb953a26970e1affe4e8fc7a8da79a3d229a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 25 Jul 2023 16:49:32 +0200 Subject: [PATCH 061/632] [3.11] gh-107237: Fix test_udp_reconnection() of test_logging (#107238) (#107245) gh-107237: Fix test_udp_reconnection() of test_logging (#107238) test_logging: Fix test_udp_reconnection() by increasing the timeout from 100 ms to 5 minutes (LONG_TIMEOUT). Replace also blocking wait() with wait(LONG_TIMEOUT) in test_output() to prevent the test to hang. (cherry picked from commit ed082383272c2c238e364e9cc83229234aee23cc) --- Lib/test/test_logging.py | 8 ++++---- .../Tests/2023-07-25-14-36-33.gh-issue-107237.y1pY79.rst | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2023-07-25-14-36-33.gh-issue-107237.y1pY79.rst diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 76bd8724e29b41..f1f2b75fef869b 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -1981,17 +1981,17 @@ def test_output(self): # The log message sent to the SysLogHandler is properly received. logger = logging.getLogger("slh") logger.error("sp\xe4m") - self.handled.wait() + self.handled.wait(support.LONG_TIMEOUT) self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m\x00') self.handled.clear() self.sl_hdlr.append_nul = False logger.error("sp\xe4m") - self.handled.wait() + self.handled.wait(support.LONG_TIMEOUT) self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m') self.handled.clear() self.sl_hdlr.ident = "h\xe4m-" logger.error("sp\xe4m") - self.handled.wait() + self.handled.wait(support.LONG_TIMEOUT) self.assertEqual(self.log_output, b'<11>h\xc3\xa4m-sp\xc3\xa4m') def test_udp_reconnection(self): @@ -1999,7 +1999,7 @@ def test_udp_reconnection(self): self.sl_hdlr.close() self.handled.clear() logger.error("sp\xe4m") - self.handled.wait(0.1) + self.handled.wait(support.LONG_TIMEOUT) self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m\x00') @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") diff --git a/Misc/NEWS.d/next/Tests/2023-07-25-14-36-33.gh-issue-107237.y1pY79.rst b/Misc/NEWS.d/next/Tests/2023-07-25-14-36-33.gh-issue-107237.y1pY79.rst new file mode 100644 index 00000000000000..a04f7eeddef174 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-07-25-14-36-33.gh-issue-107237.y1pY79.rst @@ -0,0 +1,2 @@ +``test_logging``: Fix ``test_udp_reconnection()`` by increasing the timeout +from 100 ms to 5 minutes (LONG_TIMEOUT). Patch by Victor Stinner. From 012bc1329a01bd92b0e09d87db9b70a242e06e3a Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 25 Jul 2023 09:34:56 -0700 Subject: [PATCH 062/632] [3.11] Remove superflous whitespaces in `layout.html`. (GH-107251) (#107252) Remove superflous whitespaces in `layout.html`. (GH-107251) Remove superflous whitespaces in layout.html. (cherry picked from commit 313284aa423252ebd5d4e761220e0f4fdeac626d) Co-authored-by: Ezio Melotti --- Doc/tools/templates/layout.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/tools/templates/layout.html b/Doc/tools/templates/layout.html index 10cb6fd880fa1a..9832feba141675 100644 --- a/Doc/tools/templates/layout.html +++ b/Doc/tools/templates/layout.html @@ -4,16 +4,16 @@ {%- if outdated %}

{%- endif %} {%- if is_deployment_preview %}
{% trans %}This is a deploy preview created from a pull request. - For authoritative documentation, see the {% endtrans %} - {% trans %} the current stable release{% endtrans %}. + For authoritative documentation, see{% endtrans %} + {% trans %}the current stable release{% endtrans %}.
{%- endif %} {% endblock %} From 41756e3960a38249b9e0076412ef5e08625a7acc Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 25 Jul 2023 11:49:28 -0700 Subject: [PATCH 063/632] [3.11] gh-62519: Make pgettext search plurals when translation is not found (GH-107118) (GH-107133) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit b3c34e55c053846beb35f5e4253ef237b3494bd0) Co-authored-by: Tomas R Co-authored-by: Łukasz Langa --- Lib/gettext.py | 10 ++++++---- Lib/test/test_gettext.py | 4 ++++ .../2023-07-23-12-26-23.gh-issue-62519.w8-81X.rst | 2 ++ 3 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-07-23-12-26-23.gh-issue-62519.w8-81X.rst diff --git a/Lib/gettext.py b/Lib/gettext.py index 6c5ec4e517f637..57f1a449c28a43 100644 --- a/Lib/gettext.py +++ b/Lib/gettext.py @@ -444,10 +444,12 @@ def pgettext(self, context, message): missing = object() tmsg = self._catalog.get(ctxt_msg_id, missing) if tmsg is missing: - if self._fallback: - return self._fallback.pgettext(context, message) - return message - return tmsg + tmsg = self._catalog.get((ctxt_msg_id, self.plural(1)), missing) + if tmsg is not missing: + return tmsg + if self._fallback: + return self._fallback.pgettext(context, message) + return message def npgettext(self, context, msgid1, msgid2, n): ctxt_msg_id = self.CONTEXT % (context, msgid1) diff --git a/Lib/test/test_gettext.py b/Lib/test/test_gettext.py index 1608d1b18e98fb..7f7b51c19f35c7 100644 --- a/Lib/test/test_gettext.py +++ b/Lib/test/test_gettext.py @@ -329,6 +329,8 @@ def test_plural_context_forms1(self): x = gettext.npgettext('With context', 'There is %s file', 'There are %s files', 2) eq(x, 'Hay %s ficheros (context)') + x = gettext.pgettext('With context', 'There is %s file') + eq(x, 'Hay %s fichero (context)') def test_plural_forms2(self): eq = self.assertEqual @@ -349,6 +351,8 @@ def test_plural_context_forms2(self): x = t.npgettext('With context', 'There is %s file', 'There are %s files', 2) eq(x, 'Hay %s ficheros (context)') + x = gettext.pgettext('With context', 'There is %s file') + eq(x, 'Hay %s fichero (context)') # Examples from http://www.gnu.org/software/gettext/manual/gettext.html diff --git a/Misc/NEWS.d/next/Library/2023-07-23-12-26-23.gh-issue-62519.w8-81X.rst b/Misc/NEWS.d/next/Library/2023-07-23-12-26-23.gh-issue-62519.w8-81X.rst new file mode 100644 index 00000000000000..96e2a3dcc24fb0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-07-23-12-26-23.gh-issue-62519.w8-81X.rst @@ -0,0 +1,2 @@ +Make :func:`gettext.pgettext` search plural definitions when +translation is not found. From 2ce8e133d0ed13d439477659d6349c26728d839c Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 25 Jul 2023 13:02:06 -0700 Subject: [PATCH 064/632] [3.11] gh-107226: PyModule_AddObjectRef() should only be in the limited API 3.10 (GH-107227) (GH-107261) (cherry picked from commit 698b01513550798886add5e06a1c3f9a89d7dfc6) Co-authored-by: Serhiy Storchaka --- Include/modsupport.h | 2 ++ .../next/C API/2023-07-25-13-41-09.gh-issue-107226.N919zH.rst | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 Misc/NEWS.d/next/C API/2023-07-25-13-41-09.gh-issue-107226.N919zH.rst diff --git a/Include/modsupport.h b/Include/modsupport.h index 0e96a5c988846e..b3813a7e319357 100644 --- a/Include/modsupport.h +++ b/Include/modsupport.h @@ -41,10 +41,12 @@ PyAPI_FUNC(PyObject *) _Py_BuildValue_SizeT(const char *, ...); PyAPI_FUNC(PyObject *) Py_VaBuildValue(const char *, va_list); +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030a0000 // Add an attribute with name 'name' and value 'obj' to the module 'mod. // On success, return 0 on success. // On error, raise an exception and return -1. PyAPI_FUNC(int) PyModule_AddObjectRef(PyObject *mod, const char *name, PyObject *value); +#endif /* Py_LIMITED_API */ // Similar to PyModule_AddObjectRef() but steal a reference to 'obj' // (Py_DECREF(obj)) on success (if it returns 0). diff --git a/Misc/NEWS.d/next/C API/2023-07-25-13-41-09.gh-issue-107226.N919zH.rst b/Misc/NEWS.d/next/C API/2023-07-25-13-41-09.gh-issue-107226.N919zH.rst new file mode 100644 index 00000000000000..6178f18517d48f --- /dev/null +++ b/Misc/NEWS.d/next/C API/2023-07-25-13-41-09.gh-issue-107226.N919zH.rst @@ -0,0 +1,2 @@ +:c:func:`PyModule_AddObjectRef` is now only available in the limited API +version 3.10 or later. From 5a89248696ee92e2182c67e366538bb88df5bed7 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 25 Jul 2023 14:18:23 -0700 Subject: [PATCH 065/632] [3.11] gh-106939: document ShareableList nul-strip quirk. (GH-107266) (#107270) gh-106939: document ShareableList nul-strip quirk. (GH-107266) * gh-106939: document ShareableList nul-strip quirk. * Mention the `int` size constraint. (cherry picked from commit 70dc00946938027d5a79bcb7b65038319040144e) Co-authored-by: Gregory P. Smith --- Doc/library/multiprocessing.shared_memory.rst | 44 ++++++++++++++++--- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/Doc/library/multiprocessing.shared_memory.rst b/Doc/library/multiprocessing.shared_memory.rst index 76046b34610abe..f453e6403d932d 100644 --- a/Doc/library/multiprocessing.shared_memory.rst +++ b/Doc/library/multiprocessing.shared_memory.rst @@ -255,16 +255,17 @@ shared memory blocks created using that manager are all released when the :keyword:`with` statement's code block finishes execution. -.. class:: ShareableList(sequence=None, *, name=None) +.. class:: ShareableList(sequence=None, \*, name=None) Provides a mutable list-like object where all values stored within are stored in a shared memory block. This constrains storable values to - only the ``int``, ``float``, ``bool``, ``str`` (less than 10M bytes each), - ``bytes`` (less than 10M bytes each), and ``None`` built-in data types. - It also notably differs from the built-in ``list`` type in that these - lists can not change their overall length (i.e. no append, insert, etc.) - and do not support the dynamic creation of new :class:`ShareableList` - instances via slicing. + only the ``int`` (signed 64-bit), ``float``, ``bool``, ``str`` (less + than 10M bytes each when encoded as utf-8), ``bytes`` (less than 10M + bytes each), and ``None`` built-in data types. It also notably + differs from the built-in ``list`` type in that these lists can not + change their overall length (i.e. no append, insert, etc.) and do not + support the dynamic creation of new :class:`ShareableList` instances + via slicing. *sequence* is used in populating a new ``ShareableList`` full of values. Set to ``None`` to instead attach to an already existing @@ -275,6 +276,35 @@ shared memory blocks created using that manager are all released when the existing ``ShareableList``, specify its shared memory block's unique name while leaving ``sequence`` set to ``None``. + .. note:: + + A known issue exists for :class:`bytes` and :class:`str` values. + If they end with ``\x00`` nul bytes or characters, those may be + *silently stripped* when fetching them by index from the + :class:`ShareableList`. This ``.rstrip(b'\x00')`` behavior is + considered a bug and may go away in the future. See :gh:`106939`. + + For applications where rstripping of trailing nulls is a problem, + work around it by always unconditionally appending an extra non-0 + byte to the end of such values when storing and unconditionally + removing it when fetching: + + .. doctest:: + + >>> from multiprocessing import shared_memory + >>> nul_bug_demo = shared_memory.ShareableList(['?\x00', b'\x03\x02\x01\x00\x00\x00']) + >>> nul_bug_demo[0] + '?' + >>> nul_bug_demo[1] + b'\x03\x02\x01' + >>> nul_bug_demo.shm.unlink() + >>> padded = shared_memory.ShareableList(['?\x00\x07', b'\x03\x02\x01\x00\x00\x00\x07']) + >>> padded[0][:-1] + '?\x00' + >>> padded[1][:-1] + b'\x03\x02\x01\x00\x00\x00' + >>> padded.shm.unlink() + .. method:: count(value) Returns the number of occurrences of ``value``. From 9fa44d7a3045f4571e2cabd53a1cc0f16a0f3723 Mon Sep 17 00:00:00 2001 From: "Wei-Hsiang (Matt) Wang" Date: Wed, 26 Jul 2023 13:17:44 +0800 Subject: [PATCH 066/632] [3.11] GH-97950: Fix old-style index directive in Doc/library/imp.rst (#107246) Use new-style index directive ('statement') - library/imp --- Doc/library/imp.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/imp.rst b/Doc/library/imp.rst index 000793a7e66cae..cc95cd65029407 100644 --- a/Doc/library/imp.rst +++ b/Doc/library/imp.rst @@ -10,7 +10,7 @@ .. deprecated-removed:: 3.4 3.12 The :mod:`imp` module is deprecated in favor of :mod:`importlib`. -.. index:: statement: import +.. index:: pair: statement; import -------------- From 36208b5f6aecf40c325667385cc3afd389efab88 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 26 Jul 2023 00:58:07 -0700 Subject: [PATCH 067/632] [3.11] gh-106350: Tkinter: do not ignore return value of `mp_init()` (GH-106351) (GH-107259) (cherry picked from commit b5ae7c498438657a6ba0bf4cc216b9c2c93a06c7) Co-authored-by: Christopher Chavez --- .../Library/2023-07-03-03-46-20.gh-issue-106350.LLcTEe.rst | 2 ++ Modules/_tkinter.c | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-07-03-03-46-20.gh-issue-106350.LLcTEe.rst diff --git a/Misc/NEWS.d/next/Library/2023-07-03-03-46-20.gh-issue-106350.LLcTEe.rst b/Misc/NEWS.d/next/Library/2023-07-03-03-46-20.gh-issue-106350.LLcTEe.rst new file mode 100644 index 00000000000000..681d63a6668be8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-07-03-03-46-20.gh-issue-106350.LLcTEe.rst @@ -0,0 +1,2 @@ +Detect possible memory allocation failure in the libtommath function :c:func:`mp_init` +used by the ``_tkinter`` module. diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index 2aab68ee1784f9..e5377073ff765e 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -906,8 +906,9 @@ asBignumObj(PyObject *value) return NULL; } hexchars += neg + 2; /* skip sign and "0x" */ - mp_init(&bigValue); - if (mp_read_radix(&bigValue, hexchars, 16) != MP_OKAY) { + if (mp_init(&bigValue) != MP_OKAY || + mp_read_radix(&bigValue, hexchars, 16) != MP_OKAY) + { mp_clear(&bigValue); Py_DECREF(hexstr); PyErr_NoMemory(); From bd0def00b3d92b30d6d0440a45ab811f3328933e Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 26 Jul 2023 18:27:08 +0200 Subject: [PATCH 068/632] [3.11] gh-106948: Update documentation nitpick_ignore for c:identifer domain (#107295) (#107299) gh-106948: Update documentation nitpick_ignore for c:identifer domain (#107295) Update the nitpick_ignore of the documentation configuration to fix Sphinx warnings about standard C types when declaring functions with the "c:function" markups. Copy standard C types declared in the "c:type" domain to the "c:identifier" domain, since "c:function" markup looks for types in the "c:identifier" domain. Co-authored-by: Serhiy Storchaka (cherry picked from commit b1de3807b832b72dfeb66dd5646159d08d2cc74a) --- Doc/conf.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Doc/conf.py b/Doc/conf.py index 28c35f2b0a2348..d5eea9cdfb88d1 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -148,6 +148,15 @@ ('py:meth', '_SubParsersAction.add_parser'), ] +# gh-106948: Copy standard C types declared in the "c:type" domain to the +# "c:identifier" domain, since "c:function" markup looks for types in the +# "c:identifier" domain. Use list() to not iterate on items which are being +# added +for role, name in list(nitpick_ignore): + if role == 'c:type': + nitpick_ignore.append(('c:identifier', name)) +del role, name + # Disable Docutils smartquotes for several translations smartquotes_excludes = { 'languages': ['ja', 'fr', 'zh_TW', 'zh_CN'], 'builders': ['man', 'text'], From cfa9f3b7ccfb0482bb525c73f994cf669c32a11c Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 26 Jul 2023 22:35:16 +0300 Subject: [PATCH 069/632] [3.11] gh-107091: Fix some uses of :c:member: role (GH-107129) (GH-107311) (cherry picked from commit af61cb9c7837ff3c11da79e3ee1cab3fdd0ba4da) --- Doc/c-api/import.rst | 2 +- Doc/c-api/init_config.rst | 2 +- Doc/c-api/module.rst | 8 ++++---- Doc/c-api/structures.rst | 6 ++++-- Doc/howto/isolating-extensions.rst | 2 +- Doc/whatsnew/3.5.rst | 2 +- Doc/whatsnew/3.7.rst | 2 +- 7 files changed, 13 insertions(+), 11 deletions(-) diff --git a/Doc/c-api/import.rst b/Doc/c-api/import.rst index 4ca1517efcb38d..8b2d38582343b7 100644 --- a/Doc/c-api/import.rst +++ b/Doc/c-api/import.rst @@ -138,7 +138,7 @@ Importing Modules :class:`SourceFileLoader` otherwise. The module's :attr:`__file__` attribute will be set to the code object's - :c:member:`co_filename`. If applicable, :attr:`__cached__` will also + :attr:`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/c-api/init_config.rst b/Doc/c-api/init_config.rst index 24d66201f68dbb..715ad47f13eb45 100644 --- a/Doc/c-api/init_config.rst +++ b/Doc/c-api/init_config.rst @@ -522,7 +522,7 @@ PyConfig Moreover, if :c:func:`PyConfig_SetArgv` or :c:func:`PyConfig_SetBytesArgv` is used, this method must be called before other methods, since the preinitialization configuration depends on command line arguments (if - :c:member:`parse_argv` is non-zero). + :c:member:`~PyConfig.parse_argv` is non-zero). The caller of these methods is responsible to handle exceptions (error or exit) using ``PyStatus_Exception()`` and ``Py_ExitStatusException()``. diff --git a/Doc/c-api/module.rst b/Doc/c-api/module.rst index e358c5da14a69f..6abd550ab80b21 100644 --- a/Doc/c-api/module.rst +++ b/Doc/c-api/module.rst @@ -164,7 +164,7 @@ or request "multi-phase initialization" by returning the definition struct itsel This memory area is allocated based on *m_size* on module creation, and freed when the module object is deallocated, after the - :c:member:`m_free` function has been called, if present. + :c:member:`~PyModuleDef.m_free` function has been called, if present. Setting ``m_size`` to ``-1`` means that the module does not support sub-interpreters, because it has global state. @@ -202,7 +202,7 @@ or request "multi-phase initialization" by returning the definition struct itsel This function is not called if the module state was requested but is not allocated yet. This is the case immediately after the module is created and before the module is executed (:c:data:`Py_mod_exec` function). More - precisely, this function is not called if :c:member:`m_size` is greater + precisely, this function is not called if :c:member:`~PyModuleDef.m_size` is greater than 0 and the module state (as returned by :c:func:`PyModule_GetState`) is ``NULL``. @@ -217,7 +217,7 @@ or request "multi-phase initialization" by returning the definition struct itsel This function is not called if the module state was requested but is not allocated yet. This is the case immediately after the module is created and before the module is executed (:c:data:`Py_mod_exec` function). More - precisely, this function is not called if :c:member:`m_size` is greater + precisely, this function is not called if :c:member:`~PyModuleDef.m_size` is greater than 0 and the module state (as returned by :c:func:`PyModule_GetState`) is ``NULL``. @@ -238,7 +238,7 @@ or request "multi-phase initialization" by returning the definition struct itsel This function is not called if the module state was requested but is not allocated yet. This is the case immediately after the module is created and before the module is executed (:c:data:`Py_mod_exec` function). More - precisely, this function is not called if :c:member:`m_size` is greater + precisely, this function is not called if :c:member:`~PyModuleDef.m_size` is greater than 0 and the module state (as returned by :c:func:`PyModule_GetState`) is ``NULL``. diff --git a/Doc/c-api/structures.rst b/Doc/c-api/structures.rst index e9f158c176924e..5c85838d2dbfa0 100644 --- a/Doc/c-api/structures.rst +++ b/Doc/c-api/structures.rst @@ -263,14 +263,16 @@ Implementing functions and methods points to the contents of the docstring -The :c:member:`ml_meth` is a C function pointer. The functions may be of different +The :c:member:`~PyMethodDef.ml_meth` is a C function pointer. +The functions may be of different types, but they always return :c:expr:`PyObject*`. If the function is not of the :c:type:`PyCFunction`, the compiler will require a cast in the method table. Even though :c:type:`PyCFunction` defines the first parameter as :c:expr:`PyObject*`, it is common that the method implementation uses the specific C type of the *self* object. -The :c:member:`ml_flags` field is a bitfield which can include the following flags. +The :c:member:`~PyMethodDef.ml_flags` field is a bitfield which can include +the following flags. The individual flags indicate either a calling convention or a binding convention. diff --git a/Doc/howto/isolating-extensions.rst b/Doc/howto/isolating-extensions.rst index c88c9edc0ccad2..0362fa6c685443 100644 --- a/Doc/howto/isolating-extensions.rst +++ b/Doc/howto/isolating-extensions.rst @@ -467,7 +467,7 @@ Module State Access from Slot Methods, Getters and Setters Slot methods—the fast C equivalents for special methods, such as :c:member:`~PyNumberMethods.nb_add` for :py:attr:`~object.__add__` or -:c:member:`~PyType.tp_new` for initialization—have a very simple API that +:c:member:`~PyTypeObject.tp_new` for initialization—have a very simple API that doesn't allow passing in the defining class, unlike with :c:type:`PyCMethod`. The same goes for getters and setters defined with :c:type:`PyGetSetDef`. diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst index 315de6b59fe5f0..9bd97e4726ca24 100644 --- a/Doc/whatsnew/3.5.rst +++ b/Doc/whatsnew/3.5.rst @@ -2533,7 +2533,7 @@ Changes in the C API * As part of the :pep:`492` implementation, the ``tp_reserved`` slot of :c:type:`PyTypeObject` was replaced with a - :c:member:`tp_as_async` slot. Refer to :ref:`coro-objects` for + :c:member:`~PyTypeObject.tp_as_async` slot. Refer to :ref:`coro-objects` for new types, structures and functions. diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index d00c55a7766136..02406691d4ac7d 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -1674,7 +1674,7 @@ The new :c:func:`import__find__load__start` and module imports. (Contributed by Christian Heimes in :issue:`31574`.) -The fields :c:member:`name` and :c:member:`doc` of structures +The fields :c:member:`!name` and :c:member:`!doc` of structures :c:type:`PyMemberDef`, :c:type:`PyGetSetDef`, :c:type:`PyStructSequence_Field`, :c:type:`PyStructSequence_Desc`, and :c:type:`wrapperbase` are now of type ``const char *`` rather of From 35aa6b80a6018ea12ea54dfb97da46b70c1cc81e Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 26 Jul 2023 13:00:13 -0700 Subject: [PATCH 070/632] [3.11] Document that `os.link()` is not available on Emscripten (GH-104822) (GH-107309) Document that `os.link()` is not available on Emscripten (GH-104822) (cherry picked from commit 737d1da0746053d515158eac5b115e8bd813f6d3) Co-authored-by: Roman Yurchak --- Doc/library/os.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index dcb4e7d3ab259b..56e34a3df87612 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -2036,7 +2036,7 @@ features: .. audit-event:: os.link src,dst,src_dir_fd,dst_dir_fd os.link - .. availability:: Unix, Windows. + .. availability:: Unix, Windows, not Emscripten. .. versionchanged:: 3.2 Added Windows support. From 603c5d50e89982e9b72a1b4d9edd5b52982b395a Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 26 Jul 2023 14:00:36 -0700 Subject: [PATCH 071/632] [3.11] gh-107091: Fix some uses of :c:type: role (GH-107138) (GH-107313) (cherry picked from commit 6d5b6e71c87fca7c5c26f5dd8f325087962215cc) Co-authored-by: Serhiy Storchaka --- Doc/c-api/tuple.rst | 2 ++ Doc/c-api/typeobj.rst | 20 ++++++++++---------- Doc/conf.py | 4 ++++ Doc/library/os.rst | 12 ++++++------ Doc/whatsnew/2.4.rst | 2 +- Doc/whatsnew/3.2.rst | 2 +- Doc/whatsnew/3.3.rst | 2 +- Doc/whatsnew/3.7.rst | 2 +- Misc/NEWS.d/3.10.0a7.rst | 4 ++-- 9 files changed, 28 insertions(+), 22 deletions(-) diff --git a/Doc/c-api/tuple.rst b/Doc/c-api/tuple.rst index 0982d29e48dec0..689fad683aed64 100644 --- a/Doc/c-api/tuple.rst +++ b/Doc/c-api/tuple.rst @@ -111,6 +111,8 @@ Tuple Objects raises :exc:`MemoryError` or :exc:`SystemError`. +.. _struct-sequence-objects: + Struct Sequence Objects ----------------------- diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 006e11b2e72352..002603857394c6 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -161,9 +161,9 @@ Quick Reference .. [#cols] Columns: - **"O"**: set on :c:type:`PyBaseObject_Type` + **"O"**: set on :c:data:`PyBaseObject_Type` - **"T"**: set on :c:type:`PyType_Type` + **"T"**: set on :c:data:`PyType_Type` **"D"**: default (if slot is set to ``NULL``) @@ -567,8 +567,8 @@ PyTypeObject Slots Each slot has a section describing inheritance. If :c:func:`PyType_Ready` may set a value when the field is set to ``NULL`` then there will also be -a "Default" section. (Note that many fields set on :c:type:`PyBaseObject_Type` -and :c:type:`PyType_Type` effectively act as defaults.) +a "Default" section. (Note that many fields set on :c:data:`PyBaseObject_Type` +and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: const char* PyTypeObject.tp_name @@ -962,7 +962,7 @@ and :c:type:`PyType_Type` effectively act as defaults.) **Default:** - :c:type:`PyBaseObject_Type` uses :c:func:`PyObject_GenericGetAttr`. + :c:data:`PyBaseObject_Type` uses :c:func:`PyObject_GenericGetAttr`. .. c:member:: setattrofunc PyTypeObject.tp_setattro @@ -988,7 +988,7 @@ and :c:type:`PyType_Type` effectively act as defaults.) **Default:** - :c:type:`PyBaseObject_Type` uses :c:func:`PyObject_GenericSetAttr`. + :c:data:`PyBaseObject_Type` uses :c:func:`PyObject_GenericSetAttr`. .. c:member:: PyBufferProcs* PyTypeObject.tp_as_buffer @@ -1030,7 +1030,7 @@ and :c:type:`PyType_Type` effectively act as defaults.) **Default:** - :c:type:`PyBaseObject_Type` uses + :c:data:`PyBaseObject_Type` uses ``Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE``. **Bit Masks:** @@ -1492,7 +1492,7 @@ and :c:type:`PyType_Type` effectively act as defaults.) **Default:** - :c:type:`PyBaseObject_Type` provides a :attr:`tp_richcompare` + :c:data:`PyBaseObject_Type` provides a :attr:`tp_richcompare` implementation, which may be inherited. However, if only :attr:`tp_hash` is defined, not even the inherited function is used and instances of the type will not be able to participate in any @@ -1814,7 +1814,7 @@ and :c:type:`PyType_Type` effectively act as defaults.) :c:func:`PyType_GenericAlloc`, to force a standard heap allocation strategy. - For static subtypes, :c:type:`PyBaseObject_Type` uses + For static subtypes, :c:data:`PyBaseObject_Type` uses :c:func:`PyType_GenericAlloc`. That is the recommended value for all statically defined types. @@ -1877,7 +1877,7 @@ and :c:type:`PyType_Type` effectively act as defaults.) match :c:func:`PyType_GenericAlloc` and the value of the :c:macro:`Py_TPFLAGS_HAVE_GC` flag bit. - For static subtypes, :c:type:`PyBaseObject_Type` uses PyObject_Del. + For static subtypes, :c:data:`PyBaseObject_Type` uses :c:func:`PyObject_Del`. .. c:member:: inquiry PyTypeObject.tp_is_gc diff --git a/Doc/conf.py b/Doc/conf.py index d5eea9cdfb88d1..c0e20c801f183b 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -103,6 +103,10 @@ ('c:type', 'uintmax_t'), ('c:type', 'va_list'), ('c:type', 'wchar_t'), + ('c:struct', 'in6_addr'), + ('c:struct', 'in_addr'), + ('c:struct', 'stat'), + ('c:struct', 'statvfs'), # Standard C macros ('c:macro', 'LLONG_MAX'), ('c:macro', 'LLONG_MIN'), diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 56e34a3df87612..bb9cbae2b13450 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -2243,13 +2243,13 @@ features: .. function:: major(device, /) Extract the device major number from a raw device number (usually the - :attr:`st_dev` or :attr:`st_rdev` field from :c:type:`stat`). + :attr:`st_dev` or :attr:`st_rdev` field from :c:struct:`stat`). .. function:: minor(device, /) Extract the device minor number from a raw device number (usually the - :attr:`st_dev` or :attr:`st_rdev` field from :c:type:`stat`). + :attr:`st_dev` or :attr:`st_rdev` field from :c:struct:`stat`). .. function:: makedev(major, minor, /) @@ -2743,7 +2743,7 @@ features: .. class:: stat_result Object whose attributes correspond roughly to the members of the - :c:type:`stat` structure. It is used for the result of :func:`os.stat`, + :c:struct:`stat` structure. It is used for the result of :func:`os.stat`, :func:`os.fstat` and :func:`os.lstat`. Attributes: @@ -2906,12 +2906,12 @@ features: See the ``IO_REPARSE_TAG_*`` constants in the :mod:`stat` module. The standard module :mod:`stat` defines functions and constants that are - useful for extracting information from a :c:type:`stat` structure. (On + useful for extracting information from a :c:struct:`stat` structure. (On Windows, some items are filled with dummy values.) For backward compatibility, a :class:`stat_result` instance is also accessible as a tuple of at least 10 integers giving the most important (and - portable) members of the :c:type:`stat` structure, in the order + portable) members of the :c:struct:`stat` structure, in the order :attr:`st_mode`, :attr:`st_ino`, :attr:`st_dev`, :attr:`st_nlink`, :attr:`st_uid`, :attr:`st_gid`, :attr:`st_size`, :attr:`st_atime`, :attr:`st_mtime`, :attr:`st_ctime`. More items may be added at the end by @@ -2944,7 +2944,7 @@ features: Perform a :c:func:`statvfs` system call on the given path. The return value is an object whose attributes describe the filesystem on the given path, and - correspond to the members of the :c:type:`statvfs` structure, namely: + correspond to the members of the :c:struct:`statvfs` structure, namely: :attr:`f_bsize`, :attr:`f_frsize`, :attr:`f_blocks`, :attr:`f_bfree`, :attr:`f_bavail`, :attr:`f_files`, :attr:`f_ffree`, :attr:`f_favail`, :attr:`f_flag`, :attr:`f_namemax`, :attr:`f_fsid`. diff --git a/Doc/whatsnew/2.4.rst b/Doc/whatsnew/2.4.rst index d68f600ba44f65..a00c4360b9c685 100644 --- a/Doc/whatsnew/2.4.rst +++ b/Doc/whatsnew/2.4.rst @@ -1491,7 +1491,7 @@ Some of the changes to Python's build process and to the C API are: though that processor architecture doesn't call that register "the TSC register". (Contributed by Jeremy Hylton.) -* The :c:type:`tracebackobject` type has been renamed to +* The :c:type:`!tracebackobject` type has been renamed to :c:type:`PyTracebackObject`. .. ====================================================================== diff --git a/Doc/whatsnew/3.2.rst b/Doc/whatsnew/3.2.rst index 2c176926e55e1f..01f5cf5204bcf7 100644 --- a/Doc/whatsnew/3.2.rst +++ b/Doc/whatsnew/3.2.rst @@ -564,7 +564,7 @@ Some smaller changes made to the core Python language are: (See :issue:`4617`.) -* The internal :c:type:`structsequence` tool now creates subclasses of tuple. +* :ref:`Struct sequence types ` are now subclasses of tuple. This means that C structures like those returned by :func:`os.stat`, :func:`time.gmtime`, and :data:`sys.version_info` now work like a :term:`named tuple` and now work with functions and methods that diff --git a/Doc/whatsnew/3.3.rst b/Doc/whatsnew/3.3.rst index e3e49640f6c916..1fda66cf9f0eaf 100644 --- a/Doc/whatsnew/3.3.rst +++ b/Doc/whatsnew/3.3.rst @@ -2194,7 +2194,7 @@ Changes to Python's build process and to the C API include: * :c:func:`PyUnicode_AsUCS4`, :c:func:`PyUnicode_AsUCS4Copy` * :c:macro:`PyUnicode_DATA`, :c:macro:`PyUnicode_1BYTE_DATA`, :c:macro:`PyUnicode_2BYTE_DATA`, :c:macro:`PyUnicode_4BYTE_DATA` - * :c:macro:`PyUnicode_KIND` with :c:type:`PyUnicode_Kind` enum: + * :c:macro:`PyUnicode_KIND` with :c:enum:`PyUnicode_Kind` enum: :c:data:`PyUnicode_WCHAR_KIND`, :c:data:`PyUnicode_1BYTE_KIND`, :c:data:`PyUnicode_2BYTE_KIND`, :c:data:`PyUnicode_4BYTE_KIND` * :c:macro:`PyUnicode_READ`, :c:macro:`PyUnicode_READ_CHAR`, :c:macro:`PyUnicode_WRITE` diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index 02406691d4ac7d..a3fd6321f3124e 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -1677,7 +1677,7 @@ module imports. The fields :c:member:`!name` and :c:member:`!doc` of structures :c:type:`PyMemberDef`, :c:type:`PyGetSetDef`, :c:type:`PyStructSequence_Field`, :c:type:`PyStructSequence_Desc`, -and :c:type:`wrapperbase` are now of type ``const char *`` rather of +and :c:struct:`wrapperbase` are now of type ``const char *`` rather of ``char *``. (Contributed by Serhiy Storchaka in :issue:`28761`.) The result of :c:func:`PyUnicode_AsUTF8AndSize` and :c:func:`PyUnicode_AsUTF8` diff --git a/Misc/NEWS.d/3.10.0a7.rst b/Misc/NEWS.d/3.10.0a7.rst index ff01ee645c0a90..7933f71b01c14d 100644 --- a/Misc/NEWS.d/3.10.0a7.rst +++ b/Misc/NEWS.d/3.10.0a7.rst @@ -215,8 +215,8 @@ a non-Python signal handler. .. nonce: VouZjn .. section: Core and Builtins -Add ``__match_args__`` to :c:type:`structsequence` based classes. Patch by -Pablo Galindo. +Add ``__match_args__`` to :ref:`struct sequence objects `. +Patch by Pablo Galindo. .. From 55edee247cc305d97a47e7eb428783a405c87a78 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 26 Jul 2023 23:21:28 +0200 Subject: [PATCH 072/632] [3.11] Docs: Remove the numbered steps from the Argument Clinic tutorial (#107203) (#107319) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead, order the tutorial as one body of prose, making it easier to align the tutorial according to Diátaxis principles. (cherry picked from commit 592395577c679543d899e68a3cff538b8b4df80d) --- Doc/howto/clinic.rst | 609 +++++++++++++++++++++---------------------- 1 file changed, 303 insertions(+), 306 deletions(-) diff --git a/Doc/howto/clinic.rst b/Doc/howto/clinic.rst index 2d332f03d91a28..d14713e925224a 100644 --- a/Doc/howto/clinic.rst +++ b/Doc/howto/clinic.rst @@ -167,23 +167,23 @@ convert a function to work with it. Here, then, are the bare minimum steps you'd need to follow to convert a function to work with Argument Clinic. Note that for code you plan to check in to CPython, you really should take the conversion farther, -using some of the advanced concepts you'll see later on in -the document (like "return converters" and "self converters"). +using some of the :ref:`advanced concepts ` +you'll see later on in the document, +like :ref:`clinic-howto-return-converters` +and :ref:`clinic-howto-self-converter`. But we'll keep it simple for this walkthrough so you can learn. -Let's dive in! +First, make sure you're working with a freshly updated checkout +of the CPython trunk. -0. Make sure you're working with a freshly updated checkout - of the CPython trunk. +Next, find a Python builtin that calls either :c:func:`PyArg_ParseTuple` +or :c:func:`PyArg_ParseTupleAndKeywords`, and hasn't been converted +to work with Argument Clinic yet. +For this tutorial, we'll be using +:py:meth:`_pickle.Pickler.dump `. -1. Find a Python builtin that calls either :c:func:`PyArg_ParseTuple` - or :c:func:`PyArg_ParseTupleAndKeywords`, and hasn't been converted - to work with Argument Clinic yet. - For my example I'm using - :py:meth:`_pickle.Pickler.dump `. - -2. If the call to the :c:func:`!PyArg_Parse*` function uses any of the - following format units: +If the call to the :c:func:`!PyArg_Parse*` function uses any of the +following format units...: .. code-block:: none @@ -194,380 +194,370 @@ Let's dive in! et et# - or if it has multiple calls to :c:func:`PyArg_ParseTuple`, - you should choose a different function. Argument Clinic *does* - support all of these scenarios. But these are advanced - topics—let's do something simpler for your first function. +... or if it has multiple calls to :c:func:`PyArg_ParseTuple`, +you should choose a different function. +(See :ref:`clinic-howto-advanced-converters` for those scenarios.) - Also, if the function has multiple calls to :c:func:`!PyArg_ParseTuple` - or :c:func:`PyArg_ParseTupleAndKeywords` where it supports different - types for the same argument, or if the function uses something besides - :c:func:`!PyArg_Parse*` functions to parse its arguments, it probably - isn't suitable for conversion to Argument Clinic. Argument Clinic - doesn't support generic functions or polymorphic parameters. +Also, if the function has multiple calls to :c:func:`!PyArg_ParseTuple` +or :c:func:`PyArg_ParseTupleAndKeywords` where it supports different +types for the same argument, or if the function uses something besides +:c:func:`!PyArg_Parse*` functions to parse its arguments, it probably +isn't suitable for conversion to Argument Clinic. Argument Clinic +doesn't support generic functions or polymorphic parameters. -3. Add the following boilerplate above the function, creating our block:: +Next, add the following boilerplate above the function, +creating our input block:: /*[clinic input] [clinic start generated code]*/ -4. Cut the docstring and paste it in between the ``[clinic]`` lines, - removing all the junk that makes it a properly quoted C string. - When you're done you should have just the text, based at the left - margin, with no line wider than 80 characters. - (Argument Clinic will preserve indents inside the docstring.) - - If the old docstring had a first line that looked like a function - signature, throw that line away. (The docstring doesn't need it - anymore—when you use :py:func:`help` on your builtin in the future, - the first line will be built automatically based on the function's - signature.) - - Sample:: - - /*[clinic input] - Write a pickled representation of obj to the open file. - [clinic start generated code]*/ - -5. If your docstring doesn't have a "summary" line, Argument Clinic will - complain. So let's make sure it has one. The "summary" line should - be a paragraph consisting of a single 80-column line - at the beginning of the docstring. - - (Our example docstring consists solely of a summary line, so the sample - code doesn't have to change for this step.) - -6. Above the docstring, enter the name of the function, followed - by a blank line. This should be the Python name of the function, - and should be the full dotted path - to the function—it should start with the name of the module, - include any sub-modules, and if the function is a method on - a class it should include the class name too. - - Sample:: - - /*[clinic input] - _pickle.Pickler.dump - - Write a pickled representation of obj to the open file. - [clinic start generated code]*/ +Cut the docstring and paste it in between the ``[clinic]`` lines, +removing all the junk that makes it a properly quoted C string. +When you're done you should have just the text, based at the left +margin, with no line wider than 80 characters. +Argument Clinic will preserve indents inside the docstring. -7. If this is the first time that module or class has been used with Argument - Clinic in this C file, - you must declare the module and/or class. Proper Argument Clinic hygiene - prefers declaring these in a separate block somewhere near the - top of the C file, in the same way that include files and statics go at - the top. (In our sample code we'll just show the two blocks next to - each other.) +If the old docstring had a first line that looked like a function +signature, throw that line away; The docstring doesn't need it anymore --- +when you use :py:func:`help` on your builtin in the future, +the first line will be built automatically based on the function's signature. - The name of the class and module should be the same as the one - seen by Python. Check the name defined in the :c:type:`PyModuleDef` - or :c:type:`PyTypeObject` as appropriate. +Example docstring summary line:: - When you declare a class, you must also specify two aspects of its type - in C: the type declaration you'd use for a pointer to an instance of - this class, and a pointer to the :c:type:`!PyTypeObject` for this class. - - Sample:: - - /*[clinic input] - module _pickle - class _pickle.Pickler "PicklerObject *" "&Pickler_Type" - [clinic start generated code]*/ - - /*[clinic input] - _pickle.Pickler.dump - - Write a pickled representation of obj to the open file. - [clinic start generated code]*/ - - - - -8. Declare each of the parameters to the function. Each parameter - should get its own line. All the parameter lines should be - indented from the function name and the docstring. - - The general form of these parameter lines is as follows: + /*[clinic input] + Write a pickled representation of obj to the open file. + [clinic start generated code]*/ - .. code-block:: none +If your docstring doesn't have a "summary" line, Argument Clinic will +complain, so let's make sure it has one. The "summary" line should +be a paragraph consisting of a single 80-column line +at the beginning of the docstring. +(See :pep:`257` regarding docstring conventions.) - name_of_parameter: converter +Our example docstring consists solely of a summary line, so the sample +code doesn't have to change for this step. - If the parameter has a default value, add that after the - converter: +Now, above the docstring, enter the name of the function, followed +by a blank line. This should be the Python name of the function, +and should be the full dotted path to the function --- +it should start with the name of the module, +include any sub-modules, and if the function is a method on +a class it should include the class name too. - .. code-block:: none +In our example, :mod:`!_pickle` is the module, :py:class:`!Pickler` is the class, +and :py:meth:`!dump` is the method, so the name becomes +:py:meth:`!_pickle.Pickler.dump`:: - name_of_parameter: converter = default_value + /*[clinic input] + _pickle.Pickler.dump - Argument Clinic's support for "default values" is quite sophisticated; - please see :ref:`the section below on default values ` - for more information. + Write a pickled representation of obj to the open file. + [clinic start generated code]*/ - Add a blank line below the parameters. +If this is the first time that module or class has been used with Argument +Clinic in this C file, +you must declare the module and/or class. Proper Argument Clinic hygiene +prefers declaring these in a separate block somewhere near the +top of the C file, in the same way that include files and statics go at +the top. +In our sample code we'll just show the two blocks next to each other. - What's a "converter"? It establishes both the type - of the variable used in C, and the method to convert the Python - value into a C value at runtime. - For now you're going to use what's called a "legacy converter"—a - convenience syntax intended to make porting old code into Argument - Clinic easier. +The name of the class and module should be the same as the one +seen by Python. Check the name defined in the :c:type:`PyModuleDef` +or :c:type:`PyTypeObject` as appropriate. - For each parameter, copy the "format unit" for that - parameter from the :c:func:`PyArg_Parse` format argument and - specify *that* as its converter, as a quoted - string. ("format unit" is the formal name for the one-to-three - character substring of the *format* parameter that tells - the argument parsing function what the type of the variable - is and how to convert it. For more on format units please - see :ref:`arg-parsing`.) +When you declare a class, you must also specify two aspects of its type +in C: the type declaration you'd use for a pointer to an instance of +this class, and a pointer to the :c:type:`!PyTypeObject` for this class:: - For multicharacter format units like ``z#``, use the - entire two-or-three character string. + /*[clinic input] + module _pickle + class _pickle.Pickler "PicklerObject *" "&Pickler_Type" + [clinic start generated code]*/ - Sample:: + /*[clinic input] + _pickle.Pickler.dump - /*[clinic input] - module _pickle - class _pickle.Pickler "PicklerObject *" "&Pickler_Type" - [clinic start generated code]*/ + Write a pickled representation of obj to the open file. + [clinic start generated code]*/ - /*[clinic input] - _pickle.Pickler.dump +Declare each of the parameters to the function. Each parameter +should get its own line. All the parameter lines should be +indented from the function name and the docstring. +The general form of these parameter lines is as follows: - obj: 'O' +.. code-block:: none - Write a pickled representation of obj to the open file. - [clinic start generated code]*/ + name_of_parameter: converter -9. If your function has ``|`` in the format string, meaning some - parameters have default values, you can ignore it. Argument - Clinic infers which parameters are optional based on whether - or not they have default values. +If the parameter has a default value, add that after the +converter: - If your function has ``$`` in the format string, meaning it - takes keyword-only arguments, specify ``*`` on a line by - itself before the first keyword-only argument, indented the - same as the parameter lines. +.. code-block:: none - (:py:meth:`!_pickle.Pickler.dump` has neither, so our sample is unchanged.) + name_of_parameter: converter = default_value +Argument Clinic's support for "default values" is quite sophisticated; +see :ref:`clinic-howto-default-values` for more information. -10. If the existing C function calls :c:func:`PyArg_ParseTuple` - (as opposed to :c:func:`PyArg_ParseTupleAndKeywords`), then all its - arguments are positional-only. +Next, add a blank line below the parameters. - To mark all parameters as positional-only in Argument Clinic, - add a ``/`` on a line by itself after the last parameter, - indented the same as the parameter lines. +What's a "converter"? +It establishes both the type of the variable used in C, +and the method to convert the Python value into a C value at runtime. +For now you're going to use what's called a "legacy converter" --- +a convenience syntax intended to make porting old code into Argument +Clinic easier. - Currently this is all-or-nothing; either all parameters are - positional-only, or none of them are. (In the future Argument - Clinic may relax this restriction.) +For each parameter, copy the "format unit" for that +parameter from the :c:func:`PyArg_Parse` format argument and +specify *that* as its converter, as a quoted string. +The "format unit" is the formal name for the one-to-three +character substring of the *format* parameter that tells +the argument parsing function what the type of the variable +is and how to convert it. +For more on format units please see :ref:`arg-parsing`. - Sample:: +For multicharacter format units like ``z#``, +use the entire two-or-three character string. - /*[clinic input] - module _pickle - class _pickle.Pickler "PicklerObject *" "&Pickler_Type" - [clinic start generated code]*/ +Sample:: - /*[clinic input] - _pickle.Pickler.dump + /*[clinic input] + module _pickle + class _pickle.Pickler "PicklerObject *" "&Pickler_Type" + [clinic start generated code]*/ - obj: 'O' - / + /*[clinic input] + _pickle.Pickler.dump - Write a pickled representation of obj to the open file. - [clinic start generated code]*/ + obj: 'O' -11. It's helpful to write a per-parameter docstring for each parameter. - But per-parameter docstrings are optional; you can skip this step - if you prefer. + Write a pickled representation of obj to the open file. + [clinic start generated code]*/ - Here's how to add a per-parameter docstring. The first line - of the per-parameter docstring must be indented further than the - parameter definition. The left margin of this first line establishes - the left margin for the whole per-parameter docstring; all the text - you write will be outdented by this amount. You can write as much - text as you like, across multiple lines if you wish. +If your function has ``|`` in the format string, +meaning some parameters have default values, you can ignore it. +Argument Clinic infers which parameters are optional +based on whether or not they have default values. - Sample:: +If your function has ``$`` in the format string, +meaning it takes keyword-only arguments, +specify ``*`` on a line by itself before the first keyword-only argument, +indented the same as the parameter lines. - /*[clinic input] - module _pickle - class _pickle.Pickler "PicklerObject *" "&Pickler_Type" - [clinic start generated code]*/ +:py:meth:`!_pickle.Pickler.dump` has neither, so our sample is unchanged. - /*[clinic input] - _pickle.Pickler.dump +Next, if the existing C function calls :c:func:`PyArg_ParseTuple` +(as opposed to :c:func:`PyArg_ParseTupleAndKeywords`), then all its +arguments are positional-only. - obj: 'O' - The object to be pickled. - / +To mark parameters as positional-only in Argument Clinic, +add a ``/`` on a line by itself after the last positional-only parameter, +indented the same as the parameter lines. - Write a pickled representation of obj to the open file. - [clinic start generated code]*/ +Sample:: -12. Save and close the file, then run ``Tools/clinic/clinic.py`` on - it. With luck everything worked---your block now has output, and - a :file:`.c.h` file has been generated! Reopen the file in your - text editor to see:: + /*[clinic input] + module _pickle + class _pickle.Pickler "PicklerObject *" "&Pickler_Type" + [clinic start generated code]*/ - /*[clinic input] - _pickle.Pickler.dump + /*[clinic input] + _pickle.Pickler.dump - obj: 'O' - The object to be pickled. - / + obj: 'O' + / - Write a pickled representation of obj to the open file. - [clinic start generated code]*/ + Write a pickled representation of obj to the open file. + [clinic start generated code]*/ - static PyObject * - _pickle_Pickler_dump(PicklerObject *self, PyObject *obj) - /*[clinic end generated code: output=87ecad1261e02ac7 input=552eb1c0f52260d9]*/ +It can be helpful to write a per-parameter docstring for each parameter. +Since per-parameter docstrings are optional, +you can skip this step if you prefer. - Obviously, if Argument Clinic didn't produce any output, it's because - it found an error in your input. Keep fixing your errors and retrying - until Argument Clinic processes your file without complaint. +Nevertheless, here's how to add a per-parameter docstring. +The first line of the per-parameter docstring +must be indented further than the parameter definition. +The left margin of this first line establishes +the left margin for the whole per-parameter docstring; +all the text you write will be outdented by this amount. +You can write as much text as you like, across multiple lines if you wish. - For readability, most of the glue code has been generated to a :file:`.c.h` - file. You'll need to include that in your original :file:`.c` file, - typically right after the clinic module block:: +Sample:: - #include "clinic/_pickle.c.h" + /*[clinic input] + module _pickle + class _pickle.Pickler "PicklerObject *" "&Pickler_Type" + [clinic start generated code]*/ -13. Double-check that the argument-parsing code Argument Clinic generated - looks basically the same as the existing code. + /*[clinic input] + _pickle.Pickler.dump - First, ensure both places use the same argument-parsing function. - The existing code must call either - :c:func:`PyArg_ParseTuple` or :c:func:`PyArg_ParseTupleAndKeywords`; - ensure that the code generated by Argument Clinic calls the - *exact* same function. + obj: 'O' + The object to be pickled. + / - Second, the format string passed in to :c:func:`!PyArg_ParseTuple` or - :c:func:`!PyArg_ParseTupleAndKeywords` should be *exactly* the same - as the hand-written one in the existing function, up to the colon - or semi-colon. + Write a pickled representation of obj to the open file. + [clinic start generated code]*/ - (Argument Clinic always generates its format strings - with a ``:`` followed by the name of the function. If the - existing code's format string ends with ``;``, to provide - usage help, this change is harmless—don't worry about it.) +Save and close the file, then run ``Tools/clinic/clinic.py`` on it. +With luck everything worked---your block now has output, +and a :file:`.c.h` file has been generated! +Reload the file in your text editor to see the generated code:: - Third, for parameters whose format units require two arguments - (like a length variable, or an encoding string, or a pointer - to a conversion function), ensure that the second argument is - *exactly* the same between the two invocations. + /*[clinic input] + _pickle.Pickler.dump - Fourth, inside the output portion of the block you'll find a preprocessor - macro defining the appropriate static :c:type:`PyMethodDef` structure for - this builtin:: + obj: 'O' + The object to be pickled. + / - #define __PICKLE_PICKLER_DUMP_METHODDEF \ - {"dump", (PyCFunction)__pickle_Pickler_dump, METH_O, __pickle_Pickler_dump__doc__}, + Write a pickled representation of obj to the open file. + [clinic start generated code]*/ - This static structure should be *exactly* the same as the existing static - :c:type:`!PyMethodDef` structure for this builtin. + static PyObject * + _pickle_Pickler_dump(PicklerObject *self, PyObject *obj) + /*[clinic end generated code: output=87ecad1261e02ac7 input=552eb1c0f52260d9]*/ - If any of these items differ in *any way*, - adjust your Argument Clinic function specification and rerun - ``Tools/clinic/clinic.py`` until they *are* the same. +Obviously, if Argument Clinic didn't produce any output, +it's because it found an error in your input. +Keep fixing your errors and retrying until Argument Clinic processes your file +without complaint. +For readability, most of the glue code has been generated to a :file:`.c.h` +file. You'll need to include that in your original :file:`.c` file, +typically right after the clinic module block:: -14. Notice that the last line of its output is the declaration - of your "impl" function. This is where the builtin's implementation goes. - Delete the existing prototype of the function you're modifying, but leave - the opening curly brace. Now delete its argument parsing code and the - declarations of all the variables it dumps the arguments into. - Notice how the Python arguments are now arguments to this impl function; - if the implementation used different names for these variables, fix it. + #include "clinic/_pickle.c.h" - Let's reiterate, just because it's kind of weird. Your code should now - look like this:: +Double-check that the argument-parsing code Argument Clinic generated +looks basically the same as the existing code. - static return_type - your_function_impl(...) - /*[clinic end generated code: checksum=...]*/ - { - ... +First, ensure both places use the same argument-parsing function. +The existing code must call either +:c:func:`PyArg_ParseTuple` or :c:func:`PyArg_ParseTupleAndKeywords`; +ensure that the code generated by Argument Clinic calls the +*exact* same function. - Argument Clinic generated the checksum line and the function prototype just - above it. You should write the opening (and closing) curly braces for the - function, and the implementation inside. +Second, the format string passed in to :c:func:`!PyArg_ParseTuple` or +:c:func:`!PyArg_ParseTupleAndKeywords` should be *exactly* the same +as the hand-written one in the existing function, +up to the colon or semi-colon. - Sample:: +Argument Clinic always generates its format strings +with a ``:`` followed by the name of the function. +If the existing code's format string ends with ``;``, +to provide usage help, this change is harmless --- don't worry about it. - /*[clinic input] - module _pickle - class _pickle.Pickler "PicklerObject *" "&Pickler_Type" - [clinic start generated code]*/ - /*[clinic end generated code: checksum=da39a3ee5e6b4b0d3255bfef95601890afd80709]*/ +Third, for parameters whose format units require two arguments, +like a length variable, an encoding string, or a pointer +to a conversion function, ensure that the second argument is +*exactly* the same between the two invocations. - /*[clinic input] - _pickle.Pickler.dump +Fourth, inside the output portion of the block, +you'll find a preprocessor macro defining the appropriate static +:c:type:`PyMethodDef` structure for this builtin:: - obj: 'O' - The object to be pickled. - / + #define __PICKLE_PICKLER_DUMP_METHODDEF \ + {"dump", (PyCFunction)__pickle_Pickler_dump, METH_O, __pickle_Pickler_dump__doc__}, - Write a pickled representation of obj to the open file. - [clinic start generated code]*/ +This static structure should be *exactly* the same as the existing static +:c:type:`!PyMethodDef` structure for this builtin. - PyDoc_STRVAR(__pickle_Pickler_dump__doc__, - "Write a pickled representation of obj to the open file.\n" - "\n" - ... - static PyObject * - _pickle_Pickler_dump_impl(PicklerObject *self, PyObject *obj) - /*[clinic end generated code: checksum=3bd30745bf206a48f8b576a1da3d90f55a0a4187]*/ - { - /* Check whether the Pickler was initialized correctly (issue3664). - Developers often forget to call __init__() in their subclasses, which - would trigger a segfault without this check. */ - if (self->write == NULL) { - PyErr_Format(PicklingError, - "Pickler.__init__() was not called by %s.__init__()", - Py_TYPE(self)->tp_name); - return NULL; - } +If any of these items differ in *any way*, +adjust your Argument Clinic function specification and rerun +``Tools/clinic/clinic.py`` until they *are* the same. - if (_Pickler_ClearBuffer(self) < 0) - return NULL; +Notice that the last line of its output is the declaration +of your "impl" function. This is where the builtin's implementation goes. +Delete the existing prototype of the function you're modifying, but leave +the opening curly brace. Now delete its argument parsing code and the +declarations of all the variables it dumps the arguments into. +Notice how the Python arguments are now arguments to this impl function; +if the implementation used different names for these variables, fix it. - ... +Let's reiterate, just because it's kind of weird. +Your code should now look like this:: -15. Remember the macro with the :c:type:`PyMethodDef` structure for this - function? Find the existing :c:type:`!PyMethodDef` structure for this - function and replace it with a reference to the macro. (If the builtin - is at module scope, this will probably be very near the end of the file; - if the builtin is a class method, this will probably be below but relatively - near to the implementation.) + static return_type + your_function_impl(...) + /*[clinic end generated code: input=..., output=...]*/ + { + ... - Note that the body of the macro contains a trailing comma. So when you - replace the existing static :c:type:`!PyMethodDef` structure with the macro, - *don't* add a comma to the end. +Argument Clinic generated the checksum line and the function prototype just +above it. You should write the opening and closing curly braces for the +function, and the implementation inside. - Sample:: +Sample:: - static struct PyMethodDef Pickler_methods[] = { - __PICKLE_PICKLER_DUMP_METHODDEF - __PICKLE_PICKLER_CLEAR_MEMO_METHODDEF - {NULL, NULL} /* sentinel */ - }; + /*[clinic input] + module _pickle + class _pickle.Pickler "PicklerObject *" "&Pickler_Type" + [clinic start generated code]*/ + /*[clinic end generated code: checksum=da39a3ee5e6b4b0d3255bfef95601890afd80709]*/ + /*[clinic input] + _pickle.Pickler.dump -16. Compile, then run the relevant portions of the regression-test suite. - This change should not introduce any new compile-time warnings or errors, - and there should be no externally visible change to Python's behavior. + obj: 'O' + The object to be pickled. + / - Well, except for one difference: :py:func:`inspect.signature` run on your function - should now provide a valid signature! + Write a pickled representation of obj to the open file. + [clinic start generated code]*/ - Congratulations, you've ported your first function to work with Argument Clinic! + PyDoc_STRVAR(__pickle_Pickler_dump__doc__, + "Write a pickled representation of obj to the open file.\n" + "\n" + ... + static PyObject * + _pickle_Pickler_dump_impl(PicklerObject *self, PyObject *obj) + /*[clinic end generated code: checksum=3bd30745bf206a48f8b576a1da3d90f55a0a4187]*/ + { + /* Check whether the Pickler was initialized correctly (issue3664). + Developers often forget to call __init__() in their subclasses, which + would trigger a segfault without this check. */ + if (self->write == NULL) { + PyErr_Format(PicklingError, + "Pickler.__init__() was not called by %s.__init__()", + Py_TYPE(self)->tp_name); + return NULL; + } + + if (_Pickler_ClearBuffer(self) < 0) { + return NULL; + } + + ... + +Remember the macro with the :c:type:`PyMethodDef` structure for this function? +Find the existing :c:type:`!PyMethodDef` structure for this +function and replace it with a reference to the macro. If the builtin +is at module scope, this will probably be very near the end of the file; +if the builtin is a class method, this will probably be below but relatively +near to the implementation. + +Note that the body of the macro contains a trailing comma; when you +replace the existing static :c:type:`!PyMethodDef` structure with the macro, +*don't* add a comma to the end. + +Sample:: + + static struct PyMethodDef Pickler_methods[] = { + __PICKLE_PICKLER_DUMP_METHODDEF + __PICKLE_PICKLER_CLEAR_MEMO_METHODDEF + {NULL, NULL} /* sentinel */ + }; + +Finally, compile, then run the relevant portions of the regression-test suite. +This change should not introduce any new compile-time warnings or errors, +and there should be no externally visible change to Python's behavior, +except for one difference: :py:func:`inspect.signature` run on your function +should now provide a valid signature! + +Congratulations, you've ported your first function to work with Argument Clinic! .. _clinic-howtos: @@ -902,6 +892,8 @@ you *must* not call :c:func:`PyBuffer_Release` on the provided buffer. Argument Clinic generates code that does it for you (in the parsing function). +.. _clinic-howto-advanced-converters: + How to use advanced converters ------------------------------ @@ -932,6 +924,7 @@ This restriction doesn't seem unreasonable; CPython itself always passes in stat hard-coded encoding strings for parameters whose format units start with ``e``. +.. _clinic-howto-default-values: .. _default_values: How to assign default values to parameter @@ -1042,6 +1035,8 @@ you're not permitted to use: * Tuple/list/set/dict literals. +.. _clinic-howto-return-converters: + How to use return converters ---------------------------- @@ -1189,6 +1184,8 @@ variable to the C code:: /*[python checksum:...]*/ +.. _clinic-howto-self-converter: + How to use the "self converter" ------------------------------- From cebe60bb1a488796f0fa81627861c2813b52dafd Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 26 Jul 2023 15:24:09 -0700 Subject: [PATCH 073/632] [3.11] gh-106368: Increase Argument Clinic CLI test coverage (GH-107156) (#107190) Instead of hacking into the Clinic class, use the Argument Clinic tool to run the ClinicExternalTest test suite. (cherry picked from commit 83a2837b328c58b243f7d97bec12c64ec66681c5) Co-authored-by: Erlend E. Aasland Co-authored-by: Nikita Sobolev --- Lib/test/test_clinic.py | 191 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 177 insertions(+), 14 deletions(-) diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index 6520966b345d18..1cf665b03002d8 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -4,11 +4,14 @@ from test import support, test_tools from test.support import os_helper +from test.support import SHORT_TIMEOUT, requires_subprocess +from test.support.os_helper import TESTFN, unlink from textwrap import dedent from unittest import TestCase import collections import inspect import os.path +import subprocess import sys import unittest @@ -1292,31 +1295,191 @@ def test_scaffolding(self): class ClinicExternalTest(TestCase): maxDiff = None + def _do_test(self, *args, expect_success=True): + clinic_py = os.path.join(test_tools.toolsdir, "clinic", "clinic.py") + with subprocess.Popen( + [sys.executable, "-Xutf8", clinic_py, *args], + encoding="utf-8", + bufsize=0, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) as proc: + proc.wait() + if expect_success == bool(proc.returncode): + self.fail("".join(proc.stderr)) + stdout = proc.stdout.read() + stderr = proc.stderr.read() + # Clinic never writes to stderr. + self.assertEqual(stderr, "") + return stdout + + def expect_success(self, *args): + return self._do_test(*args) + + def expect_failure(self, *args): + return self._do_test(*args, expect_success=False) + def test_external(self): CLINIC_TEST = 'clinic.test.c' - # bpo-42398: Test that the destination file is left unchanged if the - # content does not change. Moreover, check also that the file - # modification time does not change in this case. source = support.findfile(CLINIC_TEST) with open(source, 'r', encoding='utf-8') as f: orig_contents = f.read() - with os_helper.temp_dir() as tmp_dir: - testfile = os.path.join(tmp_dir, CLINIC_TEST) - with open(testfile, 'w', encoding='utf-8') as f: - f.write(orig_contents) - old_mtime_ns = os.stat(testfile).st_mtime_ns - - clinic.parse_file(testfile) + # Run clinic CLI and verify that it does not complain. + self.addCleanup(unlink, TESTFN) + out = self.expect_success("-f", "-o", TESTFN, source) + self.assertEqual(out, "") - with open(testfile, 'r', encoding='utf-8') as f: - new_contents = f.read() - new_mtime_ns = os.stat(testfile).st_mtime_ns + with open(TESTFN, 'r', encoding='utf-8') as f: + new_contents = f.read() self.assertEqual(new_contents, orig_contents) + + def test_no_change(self): + # bpo-42398: Test that the destination file is left unchanged if the + # content does not change. Moreover, check also that the file + # modification time does not change in this case. + code = dedent(""" + /*[clinic input] + [clinic start generated code]*/ + /*[clinic end generated code: output=da39a3ee5e6b4b0d input=da39a3ee5e6b4b0d]*/ + """) + with os_helper.temp_dir() as tmp_dir: + fn = os.path.join(tmp_dir, "test.c") + with open(fn, "w", encoding="utf-8") as f: + f.write(code) + pre_mtime = os.stat(fn).st_mtime_ns + self.expect_success(fn) + post_mtime = os.stat(fn).st_mtime_ns # Don't change the file modification time # if the content does not change - self.assertEqual(new_mtime_ns, old_mtime_ns) + self.assertEqual(pre_mtime, post_mtime) + + def test_cli_force(self): + invalid_input = dedent(""" + /*[clinic input] + output preset block + module test + test.fn + a: int + [clinic start generated code]*/ + + const char *hand_edited = "output block is overwritten"; + /*[clinic end generated code: output=bogus input=bogus]*/ + """) + fail_msg = dedent(""" + Checksum mismatch! + Expected: bogus + Computed: 2ed19 + Suggested fix: remove all generated code including the end marker, + or use the '-f' option. + """) + with os_helper.temp_dir() as tmp_dir: + fn = os.path.join(tmp_dir, "test.c") + with open(fn, "w", encoding="utf-8") as f: + f.write(invalid_input) + # First, run the CLI without -f and expect failure. + # Note, we cannot check the entire fail msg, because the path to + # the tmp file will change for every run. + out = self.expect_failure(fn) + self.assertTrue(out.endswith(fail_msg)) + # Then, force regeneration; success expected. + out = self.expect_success("-f", fn) + self.assertEqual(out, "") + # Verify by checking the checksum. + checksum = ( + "/*[clinic end generated code: " + "output=6c2289b73f32bc19 input=9543a8d2da235301]*/\n" + ) + with open(fn, 'r', encoding='utf-8') as f: + generated = f.read() + self.assertTrue(generated.endswith(checksum)) + + def test_cli_verbose(self): + with os_helper.temp_dir() as tmp_dir: + fn = os.path.join(tmp_dir, "test.c") + with open(fn, "w", encoding="utf-8") as f: + f.write("") + out = self.expect_success("-v", fn) + self.assertEqual(out.strip(), fn) + + def test_cli_help(self): + out = self.expect_success("-h") + self.assertIn("usage: clinic.py", out) + + def test_cli_converters(self): + prelude = dedent(""" + Legacy converters: + B C D L O S U Y Z Z# + b c d f h i l p s s# s* u u# w* y y# y* z z# z* + + Converters: + """) + expected_converters = ( + "bool", + "byte", + "char", + "defining_class", + "double", + "fildes", + "float", + "int", + "long", + "long_long", + "object", + "Py_buffer", + "Py_complex", + "Py_ssize_t", + "Py_UNICODE", + "PyByteArrayObject", + "PyBytesObject", + "self", + "short", + "size_t", + "slice_index", + "str", + "unicode", + "unsigned_char", + "unsigned_int", + "unsigned_long", + "unsigned_long_long", + "unsigned_short", + ) + finale = dedent(""" + Return converters: + bool() + double() + float() + init() + int() + long() + NoneType() + Py_ssize_t() + size_t() + unsigned_int() + unsigned_long() + + All converters also accept (c_default=None, py_default=None, annotation=None). + All return converters also accept (py_default=None). + """) + out = self.expect_success("--converters") + # We cannot simply compare the output, because the repr of the *accept* + # param may change (it's a set, thus unordered). So, let's compare the + # start and end of the expected output, and then assert that the + # converters appear lined up in alphabetical order. + self.assertTrue(out.startswith(prelude), out) + self.assertTrue(out.endswith(finale), out) + + out = out.removeprefix(prelude) + out = out.removesuffix(finale) + lines = out.split("\n") + for converter, line in zip(expected_converters, lines): + line = line.lstrip() + with self.subTest(converter=converter): + self.assertTrue( + line.startswith(converter), + f"expected converter {converter!r}, got {line!r}" + ) try: From 6bf2c29878451a4404ab9063664e6c5cde17cc2b Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 26 Jul 2023 15:37:14 -0700 Subject: [PATCH 074/632] [3.11] gh-106368: Increase Argument Clinic CLI test coverage (GH-107156) (#107190) Instead of hacking into the Clinic class, use the Argument Clinic tool to run the ClinicExternalTest test suite. (cherry picked from commit 83a2837b328c58b243f7d97bec12c64ec66681c5) Co-authored-by: Erlend E. Aasland Co-authored-by: Nikita Sobolev From 9edeeb2014c154ad450c1fe6b5d170a2951df14c Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 27 Jul 2023 01:02:24 +0200 Subject: [PATCH 075/632] [3.11] gh-106368: Increase Argument Clinic CLI test coverage (#107277) (#107326) (cherry picked from commit 579100f6d75a27429e7f8de74935d7bc3a3e44e6) --- Lib/test/test_clinic.py | 78 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index 1cf665b03002d8..32c4b1e67f9574 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -1294,18 +1294,18 @@ def test_scaffolding(self): class ClinicExternalTest(TestCase): maxDiff = None + clinic_py = os.path.join(test_tools.toolsdir, "clinic", "clinic.py") def _do_test(self, *args, expect_success=True): - clinic_py = os.path.join(test_tools.toolsdir, "clinic", "clinic.py") with subprocess.Popen( - [sys.executable, "-Xutf8", clinic_py, *args], + [sys.executable, "-Xutf8", self.clinic_py, *args], encoding="utf-8", bufsize=0, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) as proc: proc.wait() - if expect_success == bool(proc.returncode): + if expect_success and proc.returncode: self.fail("".join(proc.stderr)) stdout = proc.stdout.read() stderr = proc.stderr.read() @@ -1395,6 +1395,49 @@ def test_cli_force(self): generated = f.read() self.assertTrue(generated.endswith(checksum)) + def test_cli_make(self): + c_code = dedent(""" + /*[clinic input] + [clinic start generated code]*/ + """) + py_code = "pass" + c_files = "file1.c", "file2.c" + py_files = "file1.py", "file2.py" + + def create_files(files, srcdir, code): + for fn in files: + path = os.path.join(srcdir, fn) + with open(path, "w", encoding="utf-8") as f: + f.write(code) + + with os_helper.temp_dir() as tmp_dir: + # add some folders, some C files and a Python file + create_files(c_files, tmp_dir, c_code) + create_files(py_files, tmp_dir, py_code) + + # create C files in externals/ dir + ext_path = os.path.join(tmp_dir, "externals") + with os_helper.temp_dir(path=ext_path) as externals: + create_files(c_files, externals, c_code) + + # run clinic in verbose mode with --make on tmpdir + out = self.expect_success("-v", "--make", "--srcdir", tmp_dir) + + # expect verbose mode to only mention the C files in tmp_dir + for filename in c_files: + with self.subTest(filename=filename): + path = os.path.join(tmp_dir, filename) + self.assertIn(path, out) + for filename in py_files: + with self.subTest(filename=filename): + path = os.path.join(tmp_dir, filename) + self.assertNotIn(path, out) + # don't expect C files from the externals dir + for filename in c_files: + with self.subTest(filename=filename): + path = os.path.join(ext_path, filename) + self.assertNotIn(path, out) + def test_cli_verbose(self): with os_helper.temp_dir() as tmp_dir: fn = os.path.join(tmp_dir, "test.c") @@ -1481,6 +1524,35 @@ def test_cli_converters(self): f"expected converter {converter!r}, got {line!r}" ) + def test_cli_fail_converters_and_filename(self): + out = self.expect_failure("--converters", "test.c") + msg = ( + "Usage error: can't specify --converters " + "and a filename at the same time" + ) + self.assertIn(msg, out) + + def test_cli_fail_no_filename(self): + out = self.expect_failure() + self.assertIn("usage: clinic.py", out) + + def test_cli_fail_output_and_multiple_files(self): + out = self.expect_failure("-o", "out.c", "input.c", "moreinput.c") + msg = "Usage error: can't use -o with multiple filenames" + self.assertIn(msg, out) + + def test_cli_fail_filename_or_output_and_make(self): + for opts in ("-o", "out.c"), ("filename.c",): + with self.subTest(opts=opts): + out = self.expect_failure("--make", *opts) + msg = "Usage error: can't use -o or filenames with --make" + self.assertIn(msg, out) + + def test_cli_fail_make_without_srcdir(self): + out = self.expect_failure("--make", "--srcdir", "") + msg = "Usage error: --srcdir must not be empty with --make" + self.assertIn(msg, out) + try: import _testclinic as ac_tester From 00b65da4931e233a8dd69686947583ba0aa10e5e Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 27 Jul 2023 01:26:04 +0200 Subject: [PATCH 076/632] [3.11] Docs: Argument Clinic: Restructure "Basic concepts and usage" (#106981) (#107327) Split "Basic concepts and usage" into: - Reference - Terminology - CLI reference - Background - Basic concepts (cherry picked from commit 2ad699002e3ce09e9fa41e333ac72f16a32d94de) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Co-authored-by: Alex Waygood Co-authored-by: Ezio Melotti --- Doc/howto/clinic.rst | 172 ++++++++++++++++++++++++++++++----------- Tools/clinic/clinic.py | 20 +++-- 2 files changed, 142 insertions(+), 50 deletions(-) diff --git a/Doc/howto/clinic.rst b/Doc/howto/clinic.rst index d14713e925224a..1ec8984391bbef 100644 --- a/Doc/howto/clinic.rst +++ b/Doc/howto/clinic.rst @@ -8,6 +8,7 @@ Argument Clinic How-To :author: Larry Hastings +**Source code:** :source:`Tools/clinic/clinic.py`. .. topic:: Abstract @@ -15,10 +16,12 @@ Argument Clinic How-To Its purpose is to automate all the boilerplate involved with writing argument parsing code for "builtins", module level functions, and class methods. - This document is divided in three major sections: + This document is divided in four major sections: * :ref:`clinic-background` talks about the basic concepts and goals of Argument Clinic. + * :ref:`clinic-reference` describes the command-line interface and Argument + Clinic terminology. * :ref:`clinic-tutorial` guides you through all the steps required to adapt an existing C function to Argument Clinic. * :ref:`clinic-howtos` details how to handle specific tasks. @@ -93,39 +96,29 @@ and it should be able to do many interesting and smart things with all the information you give it. -Basic concepts and usage ------------------------- +Basic concepts +-------------- -Argument Clinic ships with CPython; you'll find it in -:source:`Tools/clinic/clinic.py`. -If you run that script, specifying a C file as an argument: - -.. code-block:: shell-session - - $ python3 Tools/clinic/clinic.py foo.c - -Argument Clinic will scan over the file looking for lines that -look exactly like this: +When Argument Clinic is run on a file, either via the :ref:`clinic-cli` +or via ``make clinic``, it will scan over the input files looking for +:term:`start lines `: .. code-block:: none /*[clinic input] -When it finds one, it reads everything up to a line that looks -exactly like this: +When it finds one, it reads everything up to the :term:`end line`: .. code-block:: none [clinic start generated code]*/ -Everything in between these two lines is input for Argument Clinic. -All of these lines, including the beginning and ending comment -lines, are collectively called an Argument Clinic "block". - -When Argument Clinic parses one of these blocks, it -generates output. This output is rewritten into the C file -immediately after the block, followed by a comment containing a checksum. -The Argument Clinic block now looks like this: +Everything in between these two lines is Argument Clinic :term:`input`. +When Argument Clinic parses input, it generates :term:`output`. +The output is rewritten into the C file immediately after the input, +followed by a :term:`checksum line`. +All of these lines, including the :term:`start line` and :term:`checksum line`, +are collectively called an Argument Clinic :term:`block`: .. code-block:: none @@ -133,28 +126,121 @@ The Argument Clinic block now looks like this: ... clinic input goes here ... [clinic start generated code]*/ ... clinic output goes here ... - /*[clinic end generated code: checksum=...]*/ + /*[clinic end generated code: ...]*/ If you run Argument Clinic on the same file a second time, Argument Clinic -will discard the old output and write out the new output with a fresh checksum -line. However, if the input hasn't changed, the output won't change either. - -You should never modify the output portion of an Argument Clinic block. Instead, -change the input until it produces the output you want. (That's the purpose of the -checksum—to detect if someone changed the output, as these edits would be lost -the next time Argument Clinic writes out fresh output.) - -For the sake of clarity, here's the terminology we'll use with Argument Clinic: - -* The first line of the comment (``/*[clinic input]``) is the *start line*. -* The last line of the initial comment (``[clinic start generated code]*/``) is the *end line*. -* The last line (``/*[clinic end generated code: checksum=...]*/``) is the *checksum line*. -* In between the start line and the end line is the *input*. -* In between the end line and the checksum line is the *output*. -* All the text collectively, from the start line to the checksum line inclusively, - is the *block*. (A block that hasn't been successfully processed by Argument - Clinic yet doesn't have output or a checksum line, but it's still considered - a block.) +will discard the old :term:`output` and write out the new output with a fresh +:term:`checksum line`. +If the :term:`input` hasn't changed, the output won't change either. + +.. note:: + + You should never modify the output of an Argument Clinic block, + as any change will be lost in future Argument Clinic runs; + Argument Clinic will detect an output checksum mismatch and regenerate the + correct output. + If you are not happy with the generated output, + you should instead change the input until it produces the output you want. + + +.. _clinic-reference: + +Reference +========= + + +.. _clinic-terminology: + +Terminology +----------- + +.. glossary:: + + start line + The line ``/*[clinic input]``. + This line marks the beginning of Argument Clinic input. + Note that the *start line* opens a C block comment. + + end line + The line ``[clinic start generated code]*/``. + The *end line* marks the _end_ of Argument Clinic :term:`input`, + but at the same time marks the _start_ of Argument Clinic :term:`output`, + thus the text *"clinic start start generated code"* + Note that the *end line* closes the C block comment opened + by the *start line*. + + checksum + A hash to distinguish unique :term:`inputs ` + and :term:`outputs `. + + checksum line + A line that looks like ``/*[clinic end generated code: ...]*/``. + The three dots will be replaced by a :term:`checksum` generated from the + :term:`input`, and a :term:`checksum` generated from the :term:`output`. + The checksum line marks the end of Argument Clinic generated code, + and is used by Argument Clinic to determine if it needs to regenerate + output. + + input + The text between the :term:`start line` and the :term:`end line`. + Note that the start and end lines open and close a C block comment; + the *input* is thus a part of that same C block comment. + + output + The text between the :term:`end line` and the :term:`checksum line`. + + block + All text from the :term:`start line` to the :term:`checksum line` inclusively. + + +.. _clinic-cli: + +Command-line interface +---------------------- + +The Argument Clinic :abbr:`CLI (Command-Line Interface)` is typically used to +process a single source file, like this: + +.. code-block:: shell-session + + $ python3 ./Tools/clinic/clinic.py foo.c + +The CLI supports the following options: + +.. program:: ./Tools/clinic/clinic.py [-h] [-f] [-o OUTPUT] [-v] \ + [--converters] [--make] [--srcdir SRCDIR] [FILE ...] + +.. option:: -h, --help + + Print CLI usage. + +.. option:: -f, --force + + Force output regeneration. + +.. option:: -o, --output OUTPUT + + Redirect file output to OUTPUT + +.. option:: -v, --verbose + + Enable verbose mode. + +.. option:: --converters + + Print a list of all supported converters and return converters. + +.. option:: --make + + Walk :option:`--srcdir` to run over all relevant files. + +.. option:: --srcdir SRCDIR + + The directory tree to walk in :option:`--make` mode. + +.. option:: FILE ... + + The list of files to process. .. _clinic-tutorial: diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index f8e89bc74f1773..4d30e66b4822a7 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -5190,15 +5190,21 @@ def main(argv): signatures ("docstrings") for CPython builtins. For more information see https://docs.python.org/3/howto/clinic.html""") - cmdline.add_argument("-f", "--force", action='store_true') - cmdline.add_argument("-o", "--output", type=str) - cmdline.add_argument("-v", "--verbose", action='store_true') - cmdline.add_argument("--converters", action='store_true') + cmdline.add_argument("-f", "--force", action='store_true', + help="force output regeneration") + cmdline.add_argument("-o", "--output", type=str, + help="redirect file output to OUTPUT") + cmdline.add_argument("-v", "--verbose", action='store_true', + help="enable verbose mode") + cmdline.add_argument("--converters", action='store_true', + help=("print a list of all supported converters " + "and return converters")) cmdline.add_argument("--make", action='store_true', - help="Walk --srcdir to run over all relevant files.") + help="walk --srcdir to run over all relevant files") cmdline.add_argument("--srcdir", type=str, default=os.curdir, - help="The directory tree to walk in --make mode.") - cmdline.add_argument("filename", type=str, nargs="*") + help="the directory tree to walk in --make mode") + cmdline.add_argument("filename", metavar="FILE", type=str, nargs="*", + help="the list of files to process") ns = cmdline.parse_args(argv) if ns.converters: From 17aada0dd0b3478f5fbf3b9c8bbc86cbae266aa2 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 27 Jul 2023 09:26:14 +0300 Subject: [PATCH 077/632] [3.11] gh-107091: Fix some uses of :attr: role (GH-107318) (GH-107331) Fix also formatting of PyMethodDef members. (cherry picked from commit d363eb5b0255c055e7b43f5e2c0847f555e1982e) --- Doc/c-api/import.rst | 20 +++++---- Doc/c-api/init.rst | 7 +++- Doc/c-api/structures.rst | 16 ++++---- Doc/c-api/tuple.rst | 3 +- Doc/c-api/typeobj.rst | 64 ++++++++++++++--------------- Doc/c-api/veryhigh.rst | 4 +- Doc/extending/newtypes.rst | 2 +- Doc/extending/newtypes_tutorial.rst | 4 +- Doc/whatsnew/2.5.rst | 2 +- 9 files changed, 64 insertions(+), 58 deletions(-) diff --git a/Doc/c-api/import.rst b/Doc/c-api/import.rst index 8b2d38582343b7..887c1793c3ec7a 100644 --- a/Doc/c-api/import.rst +++ b/Doc/c-api/import.rst @@ -283,23 +283,25 @@ Importing Modules .. c:struct:: _inittab - Structure describing a single entry in the list of built-in modules. Each of - these structures gives the name and initialization function for a module built - into the interpreter. The name is an ASCII encoded string. Programs which + Structure describing a single entry in the list of built-in modules. + Programs which embed Python may use an array of these structures in conjunction with :c:func:`PyImport_ExtendInittab` to provide additional built-in modules. - The structure is defined in :file:`Include/import.h` as:: + The structure consists of two members: - struct _inittab { - const char *name; /* ASCII encoded string */ - PyObject* (*initfunc)(void); - }; + .. c:member:: const char *name + + The module name, as an ASCII encoded string. + + .. c: member:: PyObject* (*initfunc)(void) + + Initialization function for a module built into the interpreter. .. c:function:: int PyImport_ExtendInittab(struct _inittab *newtab) Add a collection of modules to the table of built-in modules. The *newtab* - array must end with a sentinel entry which contains ``NULL`` for the :attr:`name` + array must end with a sentinel entry which contains ``NULL`` for the :c:member:`~_inittab.name` field; failure to provide the sentinel value can result in a memory fault. Returns ``0`` on success or ``-1`` if insufficient memory could be allocated to extend the internal table. In the event of failure, no modules are added to the diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index 9a42fc5a25dc22..05b9b77e4e7c93 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -914,8 +914,11 @@ code, or when embedding the Python interpreter: .. c:type:: PyThreadState This data structure represents the state of a single thread. The only public - data member is :attr:`interp` (:c:expr:`PyInterpreterState *`), which points to - this thread's interpreter state. + data member is: + + .. c:member:: PyInterpreterState *interp + + This thread's interpreter state. .. c:function:: void PyEval_InitThreads() diff --git a/Doc/c-api/structures.rst b/Doc/c-api/structures.rst index 5c85838d2dbfa0..b892a08ea0837a 100644 --- a/Doc/c-api/structures.rst +++ b/Doc/c-api/structures.rst @@ -34,7 +34,7 @@ the definition of all other Python objects. .. c:type:: PyVarObject - This is an extension of :c:type:`PyObject` that adds the :attr:`ob_size` + This is an extension of :c:type:`PyObject` that adds the :c:member:`~PyVarObject.ob_size` field. This is only used for objects that have some notion of *length*. This type does not often appear in the Python/C API. Access to the members must be done by using the macros @@ -171,7 +171,7 @@ the definition of all other Python objects. .. c:macro:: PyVarObject_HEAD_INIT(type, size) This is a macro which expands to initialization values for a new - :c:type:`PyVarObject` type, including the :attr:`ob_size` field. + :c:type:`PyVarObject` type, including the :c:member:`~PyVarObject.ob_size` field. This macro expands to:: _PyObject_EXTRA_INIT @@ -247,21 +247,21 @@ Implementing functions and methods Structure used to describe a method of an extension type. This structure has four fields: - .. c:member:: const char* ml_name + .. c:member:: const char *ml_name - name of the method + Name of the method. .. c:member:: PyCFunction ml_meth - pointer to the C implementation + Pointer to the C implementation. .. c:member:: int ml_flags - flags bits indicating how the call should be constructed + Flags bits indicating how the call should be constructed. - .. c:member:: const char* ml_doc + .. c:member:: const char *ml_doc - points to the contents of the docstring + Points to the contents of the docstring. The :c:member:`~PyMethodDef.ml_meth` is a C function pointer. The functions may be of different diff --git a/Doc/c-api/tuple.rst b/Doc/c-api/tuple.rst index 689fad683aed64..9fec738e68a9b4 100644 --- a/Doc/c-api/tuple.rst +++ b/Doc/c-api/tuple.rst @@ -164,7 +164,8 @@ type. Describes a field of a struct sequence. As a struct sequence is modeled as a tuple, all fields are typed as :c:expr:`PyObject*`. The index in the - :attr:`fields` array of the :c:type:`PyStructSequence_Desc` determines which + :c:member:`~PyStructSequence_Desc.fields` array of + the :c:type:`PyStructSequence_Desc` determines which field of the struct sequence is described. +-----------+------------------+-----------------------------------------+ diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 002603857394c6..05b2f8a36c01a5 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -483,17 +483,17 @@ PyObject Slots -------------- The type object structure extends the :c:type:`PyVarObject` structure. The -:attr:`ob_size` field is used for dynamic types (created by :func:`type_new`, +:c:member:`~PyVarObject.ob_size` field is used for dynamic types (created by :func:`type_new`, usually called from a class statement). Note that :c:data:`PyType_Type` (the metatype) initializes :c:member:`~PyTypeObject.tp_itemsize`, which means that its instances (i.e. -type objects) *must* have the :attr:`ob_size` field. +type objects) *must* have the :c:member:`~PyVarObject.ob_size` field. .. c:member:: Py_ssize_t PyObject.ob_refcnt This is the type object's reference count, initialized to ``1`` by the ``PyObject_HEAD_INIT`` macro. Note that for :ref:`statically allocated type - objects `, the type's instances (objects whose :attr:`ob_type` + objects `, the type's instances (objects whose :c:member:`~PyObject.ob_type` points back to the type) do *not* count as references. But for :ref:`dynamically allocated type objects `, the instances *do* count as references. @@ -517,8 +517,8 @@ type objects) *must* have the :attr:`ob_size` field. Foo_Type.ob_type = &PyType_Type; This should be done before any instances of the type are created. - :c:func:`PyType_Ready` checks if :attr:`ob_type` is ``NULL``, and if so, - initializes it to the :attr:`ob_type` field of the base class. + :c:func:`PyType_Ready` checks if :c:member:`~PyObject.ob_type` is ``NULL``, and if so, + initializes it to the :c:member:`~PyObject.ob_type` field of the base class. :c:func:`PyType_Ready` will not change this field if it is non-zero. **Inheritance:** @@ -617,20 +617,20 @@ and :c:data:`PyType_Type` effectively act as defaults.) instances have the same size, given in :c:member:`~PyTypeObject.tp_basicsize`. For a type with variable-length instances, the instances must have an - :attr:`ob_size` field, and the instance size is :c:member:`~PyTypeObject.tp_basicsize` plus N + :c:member:`~PyVarObject.ob_size` field, and the instance size is :c:member:`~PyTypeObject.tp_basicsize` plus N times :c:member:`~PyTypeObject.tp_itemsize`, where N is the "length" of the object. The value of - N is typically stored in the instance's :attr:`ob_size` field. There are - exceptions: for example, ints use a negative :attr:`ob_size` to indicate a + N is typically stored in the instance's :c:member:`~PyVarObject.ob_size` field. There are + exceptions: for example, ints use a negative :c:member:`~PyVarObject.ob_size` to indicate a negative number, and N is ``abs(ob_size)`` there. Also, the presence of an - :attr:`ob_size` field in the instance layout doesn't mean that the instance + :c:member:`~PyVarObject.ob_size` field in the instance layout doesn't mean that the instance structure is variable-length (for example, the structure for the list type has - fixed-length instances, yet those instances have a meaningful :attr:`ob_size` + fixed-length instances, yet those instances have a meaningful :c:member:`~PyVarObject.ob_size` field). The basic size includes the fields in the instance declared by the macro :c:macro:`PyObject_HEAD` or :c:macro:`PyObject_VAR_HEAD` (whichever is used to - declare the instance struct) and this in turn includes the :attr:`_ob_prev` and - :attr:`_ob_next` fields if they are present. This means that the only correct + declare the instance struct) and this in turn includes the :c:member:`~PyObject._ob_prev` and + :c:member:`~PyObject._ob_next` fields if they are present. This means that the only correct way to get an initializer for the :c:member:`~PyTypeObject.tp_basicsize` is to use the ``sizeof`` operator on the struct used to declare the instance layout. The basic size does not include the GC header size. @@ -762,7 +762,7 @@ and :c:data:`PyType_Type` effectively act as defaults.) **Inheritance:** - Group: :attr:`tp_getattr`, :attr:`tp_getattro` + Group: :c:member:`~PyTypeObject.tp_getattr`, :c:member:`~PyTypeObject.tp_getattro` This field is inherited by subtypes together with :c:member:`~PyTypeObject.tp_getattro`: a subtype inherits both :c:member:`~PyTypeObject.tp_getattr` and :c:member:`~PyTypeObject.tp_getattro` from its base type when @@ -779,7 +779,7 @@ and :c:data:`PyType_Type` effectively act as defaults.) **Inheritance:** - Group: :attr:`tp_setattr`, :attr:`tp_setattro` + Group: :c:member:`~PyTypeObject.tp_setattr`, :c:member:`~PyTypeObject.tp_setattro` This field is inherited by subtypes together with :c:member:`~PyTypeObject.tp_setattro`: a subtype inherits both :c:member:`~PyTypeObject.tp_setattr` and :c:member:`~PyTypeObject.tp_setattro` from its base type when @@ -881,7 +881,7 @@ and :c:data:`PyType_Type` effectively act as defaults.) normal return value; when an error occurs during the computation of the hash value, the function should set an exception and return ``-1``. - When this field is not set (*and* :attr:`tp_richcompare` is not set), + When this field is not set (*and* :c:member:`~PyTypeObject.tp_richcompare` is not set), an attempt to take the hash of the object raises :exc:`TypeError`. This is the same as setting it to :c:func:`PyObject_HashNotImplemented`. @@ -895,7 +895,7 @@ and :c:data:`PyType_Type` effectively act as defaults.) **Inheritance:** - Group: :attr:`tp_hash`, :attr:`tp_richcompare` + Group: :c:member:`~PyTypeObject.tp_hash`, :c:member:`~PyTypeObject.tp_richcompare` This field is inherited by subtypes together with :c:member:`~PyTypeObject.tp_richcompare`: a subtype inherits both of @@ -954,7 +954,7 @@ and :c:data:`PyType_Type` effectively act as defaults.) **Inheritance:** - Group: :attr:`tp_getattr`, :attr:`tp_getattro` + Group: :c:member:`~PyTypeObject.tp_getattr`, :c:member:`~PyTypeObject.tp_getattro` This field is inherited by subtypes together with :c:member:`~PyTypeObject.tp_getattr`: a subtype inherits both :c:member:`~PyTypeObject.tp_getattr` and :c:member:`~PyTypeObject.tp_getattro` from its base type when @@ -980,7 +980,7 @@ and :c:data:`PyType_Type` effectively act as defaults.) **Inheritance:** - Group: :attr:`tp_setattr`, :attr:`tp_setattro` + Group: :c:member:`~PyTypeObject.tp_setattr`, :c:member:`~PyTypeObject.tp_setattro` This field is inherited by subtypes together with :c:member:`~PyTypeObject.tp_setattr`: a subtype inherits both :c:member:`~PyTypeObject.tp_setattr` and :c:member:`~PyTypeObject.tp_setattro` from its base type when @@ -1046,7 +1046,7 @@ and :c:data:`PyType_Type` effectively act as defaults.) This bit is set when the type object itself is allocated on the heap, for example, types created dynamically using :c:func:`PyType_FromSpec`. In this - case, the :attr:`ob_type` field of its instances is considered a reference to + case, the :c:member:`~PyObject.ob_type` field of its instances is considered a reference to the type, and the type object is INCREF'ed when a new instance is created, and DECREF'ed when an instance is destroyed (this does not apply to instances of subtypes; only the type referenced by the instance's ob_type gets INCREF'ed or @@ -1099,13 +1099,13 @@ and :c:data:`PyType_Type` effectively act as defaults.) **Inheritance:** - Group: :c:macro:`Py_TPFLAGS_HAVE_GC`, :attr:`tp_traverse`, :attr:`tp_clear` + Group: :c:macro:`Py_TPFLAGS_HAVE_GC`, :c:member:`~PyTypeObject.tp_traverse`, :c:member:`~PyTypeObject.tp_clear` The :c:macro:`Py_TPFLAGS_HAVE_GC` flag bit is inherited - together with the :attr:`tp_traverse` and :attr:`tp_clear` + together with the :c:member:`~PyTypeObject.tp_traverse` and :c:member:`~PyTypeObject.tp_clear` fields, i.e. if the :c:macro:`Py_TPFLAGS_HAVE_GC` flag bit is - clear in the subtype and the :attr:`tp_traverse` and - :attr:`tp_clear` fields in the subtype exist and have ``NULL`` + clear in the subtype and the :c:member:`~PyTypeObject.tp_traverse` and + :c:member:`~PyTypeObject.tp_clear` fields in the subtype exist and have ``NULL`` values. @@ -1357,7 +1357,7 @@ and :c:data:`PyType_Type` effectively act as defaults.) **Inheritance:** - Group: :c:macro:`Py_TPFLAGS_HAVE_GC`, :attr:`tp_traverse`, :attr:`tp_clear` + Group: :c:macro:`Py_TPFLAGS_HAVE_GC`, :c:member:`~PyTypeObject.tp_traverse`, :c:member:`~PyTypeObject.tp_clear` This field is inherited by subtypes together with :c:member:`~PyTypeObject.tp_clear` and the :c:macro:`Py_TPFLAGS_HAVE_GC` flag bit: the flag bit, :c:member:`~PyTypeObject.tp_traverse`, and @@ -1424,7 +1424,7 @@ and :c:data:`PyType_Type` effectively act as defaults.) **Inheritance:** - Group: :c:macro:`Py_TPFLAGS_HAVE_GC`, :attr:`tp_traverse`, :attr:`tp_clear` + Group: :c:macro:`Py_TPFLAGS_HAVE_GC`, :c:member:`~PyTypeObject.tp_traverse`, :c:member:`~PyTypeObject.tp_clear` This field is inherited by subtypes together with :c:member:`~PyTypeObject.tp_traverse` and the :c:macro:`Py_TPFLAGS_HAVE_GC` flag bit: the flag bit, :c:member:`~PyTypeObject.tp_traverse`, and @@ -1483,7 +1483,7 @@ and :c:data:`PyType_Type` effectively act as defaults.) **Inheritance:** - Group: :attr:`tp_hash`, :attr:`tp_richcompare` + Group: :c:member:`~PyTypeObject.tp_hash`, :c:member:`~PyTypeObject.tp_richcompare` This field is inherited by subtypes together with :c:member:`~PyTypeObject.tp_hash`: a subtype inherits :c:member:`~PyTypeObject.tp_richcompare` and :c:member:`~PyTypeObject.tp_hash` when @@ -1492,9 +1492,9 @@ and :c:data:`PyType_Type` effectively act as defaults.) **Default:** - :c:data:`PyBaseObject_Type` provides a :attr:`tp_richcompare` + :c:data:`PyBaseObject_Type` provides a :c:member:`~PyTypeObject.tp_richcompare` implementation, which may be inherited. However, if only - :attr:`tp_hash` is defined, not even the inherited function is used + :c:member:`~PyTypeObject.tp_hash` is defined, not even the inherited function is used and instances of the type will not be able to participate in any comparisons. @@ -2288,9 +2288,9 @@ Sequence Object Structures This slot must be filled for the :c:func:`PySequence_Check` function to return ``1``, it can be ``NULL`` otherwise. - Negative indexes are handled as follows: if the :attr:`sq_length` slot is + Negative indexes are handled as follows: if the :c:member:`~PySequenceMethods.sq_length` slot is filled, it is called and the sequence length is used to compute a positive - index which is passed to :attr:`sq_item`. If :attr:`sq_length` is ``NULL``, + index which is passed to :c:member:`~PySequenceMethods.sq_item`. If :c:member:`!sq_length` is ``NULL``, the index is passed as is to the function. .. c:member:: ssizeobjargproc PySequenceMethods.sq_ass_item @@ -2500,8 +2500,8 @@ Slot Type typedefs The purpose of this function is to separate memory allocation from memory initialization. It should return a pointer to a block of memory of adequate length for the instance, suitably aligned, and initialized to zeros, but with - :attr:`ob_refcnt` set to ``1`` and :attr:`ob_type` set to the type argument. If - the type's :c:member:`~PyTypeObject.tp_itemsize` is non-zero, the object's :attr:`ob_size` field + :c:member:`~PyObject.ob_refcnt` set to ``1`` and :c:member:`~PyObject.ob_type` set to the type argument. If + the type's :c:member:`~PyTypeObject.tp_itemsize` is non-zero, the object's :c:member:`~PyVarObject.ob_size` field should be initialized to *nitems* and the length of the allocated memory block should be ``tp_basicsize + nitems*tp_itemsize``, rounded up to a multiple of ``sizeof(void*)``; otherwise, *nitems* is not used and the length of the block diff --git a/Doc/c-api/veryhigh.rst b/Doc/c-api/veryhigh.rst index 79515c9df07edb..9a1295b558f638 100644 --- a/Doc/c-api/veryhigh.rst +++ b/Doc/c-api/veryhigh.rst @@ -345,7 +345,7 @@ the same library that the Python runtime is using. executed, it is passed as ``PyCompilerFlags *flags``. In this case, ``from __future__ import`` can modify *flags*. - Whenever ``PyCompilerFlags *flags`` is ``NULL``, :attr:`cf_flags` is treated as + Whenever ``PyCompilerFlags *flags`` is ``NULL``, :c:member:`~PyCompilerFlags.cf_flags` is treated as equal to ``0``, and any modification due to ``from __future__ import`` is discarded. @@ -359,7 +359,7 @@ the same library that the Python runtime is using. initialized to ``PY_MINOR_VERSION``. The field is ignored by default, it is used if and only if - ``PyCF_ONLY_AST`` flag is set in *cf_flags*. + ``PyCF_ONLY_AST`` flag is set in :c:member:`~PyCompilerFlags.cf_flags`. .. versionchanged:: 3.8 Added *cf_feature_version* field. diff --git a/Doc/extending/newtypes.rst b/Doc/extending/newtypes.rst index e60db11b5f10f4..1f2a7d12bc6542 100644 --- a/Doc/extending/newtypes.rst +++ b/Doc/extending/newtypes.rst @@ -270,7 +270,7 @@ structure:: One entry should be defined for each method provided by the type; no entries are needed for methods inherited from a base type. One additional entry is needed at the end; it is a sentinel that marks the end of the array. The -:attr:`ml_name` field of the sentinel must be ``NULL``. +:c:member:`~PyMethodDef.ml_name` field of the sentinel must be ``NULL``. The second table is used to define attributes which map directly to data stored in the instance. A variety of primitive C types are supported, and access may diff --git a/Doc/extending/newtypes_tutorial.rst b/Doc/extending/newtypes_tutorial.rst index b1bf0535048ebd..92e9b97e36d770 100644 --- a/Doc/extending/newtypes_tutorial.rst +++ b/Doc/extending/newtypes_tutorial.rst @@ -176,8 +176,8 @@ Everything else in the file should be familiar, except for some code in if (PyType_Ready(&CustomType) < 0) return; -This initializes the :class:`Custom` type, filling in a number of members -to the appropriate default values, including :attr:`ob_type` that we initially +This initializes the :class:`!Custom` type, filling in a number of members +to the appropriate default values, including :c:member:`~PyObject.ob_type` that we initially set to ``NULL``. :: Py_INCREF(&CustomType); diff --git a/Doc/whatsnew/2.5.rst b/Doc/whatsnew/2.5.rst index b410fe1ce3f084..162d2c342f50fd 100644 --- a/Doc/whatsnew/2.5.rst +++ b/Doc/whatsnew/2.5.rst @@ -954,7 +954,7 @@ The return value must be either a Python integer or long integer. The interpreter will check that the type returned is correct, and raises a :exc:`TypeError` if this requirement isn't met. -A corresponding :attr:`nb_index` slot was added to the C-level +A corresponding :c:member:`~PyNumberMethods.nb_index` slot was added to the C-level :c:type:`PyNumberMethods` structure to let C extensions implement this protocol. ``PyNumber_Index(obj)`` can be used in extension code to call the :meth:`__index__` function and retrieve its result. From 9513acf737946e756cea48deb9aa1b51138f7c9b Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 27 Jul 2023 09:27:02 +0300 Subject: [PATCH 078/632] [3.11] gh-107298: Docs: add targets for some :c:member: and :c:macro: references (GH-107316) (GH-107333) Add targets for PyStructSequence_Desc and PyStructSequence_Field members and macros like Py_EQ. Fix target for Py_RETURN_RICHCOMPARE. (cherry picked from commit abec9a1b20b70d8ced401d59fc4f02b331c6568b) --- Doc/c-api/tuple.rst | 47 +++++++++++++++++++++---------------------- Doc/c-api/typeobj.rst | 32 +++++++++++++++-------------- 2 files changed, 40 insertions(+), 39 deletions(-) diff --git a/Doc/c-api/tuple.rst b/Doc/c-api/tuple.rst index 9fec738e68a9b4..d298aaf3ab5484 100644 --- a/Doc/c-api/tuple.rst +++ b/Doc/c-api/tuple.rst @@ -144,20 +144,21 @@ type. Contains the meta information of a struct sequence type to create. - +-------------------+------------------------------+--------------------------------------+ - | Field | C Type | Meaning | - +===================+==============================+======================================+ - | ``name`` | ``const char *`` | name of the struct sequence type | - +-------------------+------------------------------+--------------------------------------+ - | ``doc`` | ``const char *`` | pointer to docstring for the type | - | | | or ``NULL`` to omit | - +-------------------+------------------------------+--------------------------------------+ - | ``fields`` | ``PyStructSequence_Field *`` | pointer to ``NULL``-terminated array | - | | | with field names of the new type | - +-------------------+------------------------------+--------------------------------------+ - | ``n_in_sequence`` | ``int`` | number of fields visible to the | - | | | Python side (if used as tuple) | - +-------------------+------------------------------+--------------------------------------+ + .. c:member:: const char *name + + Name of the struct sequence type. + + .. c:member:: const char *doc + + Pointer to docstring for the type or ``NULL`` to omit. + + .. c:member:: PyStructSequence_Field *fields + + Pointer to ``NULL``-terminated array with field names of the new type. + + .. c:member:: int n_in_sequence + + Number of fields visible to the Python side (if used as tuple). .. c:type:: PyStructSequence_Field @@ -168,16 +169,14 @@ type. the :c:type:`PyStructSequence_Desc` determines which field of the struct sequence is described. - +-----------+------------------+-----------------------------------------+ - | Field | C Type | Meaning | - +===========+==================+=========================================+ - | ``name`` | ``const char *`` | name for the field or ``NULL`` to end | - | | | the list of named fields, set to | - | | | :c:data:`PyStructSequence_UnnamedField` | - | | | to leave unnamed | - +-----------+------------------+-----------------------------------------+ - | ``doc`` | ``const char *`` | field docstring or ``NULL`` to omit | - +-----------+------------------+-----------------------------------------+ + .. c:member:: const char *name + + Name for the field or ``NULL`` to end the list of named fields, + set to :c:data:`PyStructSequence_UnnamedField` to leave unnamed. + + .. c:member:: const char *doc + + Field docstring or ``NULL`` to omit. .. c:var:: const char * const PyStructSequence_UnnamedField diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 05b2f8a36c01a5..ec357450d004aa 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -1449,21 +1449,23 @@ and :c:data:`PyType_Type` effectively act as defaults.) The following constants are defined to be used as the third argument for :c:member:`~PyTypeObject.tp_richcompare` and for :c:func:`PyObject_RichCompare`: - +------------------+------------+ - | Constant | Comparison | - +==================+============+ - | :c:macro:`Py_LT` | ``<`` | - +------------------+------------+ - | :c:macro:`Py_LE` | ``<=`` | - +------------------+------------+ - | :c:macro:`Py_EQ` | ``==`` | - +------------------+------------+ - | :c:macro:`Py_NE` | ``!=`` | - +------------------+------------+ - | :c:macro:`Py_GT` | ``>`` | - +------------------+------------+ - | :c:macro:`Py_GE` | ``>=`` | - +------------------+------------+ + .. c:namespace:: NULL + + +--------------------+------------+ + | Constant | Comparison | + +====================+============+ + | .. c:macro:: Py_LT | ``<`` | + +--------------------+------------+ + | .. c:macro:: Py_LE | ``<=`` | + +--------------------+------------+ + | .. c:macro:: Py_EQ | ``==`` | + +--------------------+------------+ + | .. c:macro:: Py_NE | ``!=`` | + +--------------------+------------+ + | .. c:macro:: Py_GT | ``>`` | + +--------------------+------------+ + | .. c:macro:: Py_GE | ``>=`` | + +--------------------+------------+ The following macro is defined to ease writing rich comparison functions: From 8f0dc1878c37c7fd565371deb34fd1b3b36fdf9b Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 27 Jul 2023 01:12:17 -0700 Subject: [PATCH 079/632] [3.11] gh-106996: Rewrite turtle explanation (GH-107244) (#107336) Co-authored-by: Daniele Procida Co-authored-by: Hugo van Kemenade --- Doc/library/turtle.rst | 92 +++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 54 deletions(-) diff --git a/Doc/library/turtle.rst b/Doc/library/turtle.rst index 262739ec2bbd5d..a36948360de499 100644 --- a/Doc/library/turtle.rst +++ b/Doc/library/turtle.rst @@ -277,67 +277,16 @@ The turtle's screen can be customised, for example:: t.screen.bgcolor("orange") -.. _turtle-explanation: - -Explanation -=========== - -The :mod:`turtle` module is an extended reimplementation of the same-named -module from the Python standard distribution up to version Python 2.5. - -It tries to keep the merits of the old turtle module and to be (nearly) 100% -compatible with it. This means in the first place to enable the learning -programmer to use all the commands, classes and methods interactively when using -the module from within IDLE run with the ``-n`` switch. - -The turtle module provides turtle graphics primitives, in both object-oriented -and procedure-oriented ways. Because it uses :mod:`tkinter` for the underlying -graphics, it needs a version of Python installed with Tk support. - -The object-oriented interface uses essentially two+two classes: - -1. The :class:`TurtleScreen` class defines graphics windows as a playground for - the drawing turtles. Its constructor needs a :class:`tkinter.Canvas` or a - :class:`ScrolledCanvas` as argument. It should be used when :mod:`turtle` is - used as part of some application. - - The function :func:`Screen` returns a singleton object of a - :class:`TurtleScreen` subclass. This function should be used when - :mod:`turtle` is used as a standalone tool for doing graphics. - As a singleton object, inheriting from its class is not possible. - - All methods of TurtleScreen/Screen also exist as functions, i.e. as part of - the procedure-oriented interface. - -2. :class:`RawTurtle` (alias: :class:`RawPen`) defines Turtle objects which draw - on a :class:`TurtleScreen`. Its constructor needs a Canvas, ScrolledCanvas - or TurtleScreen as argument, so the RawTurtle objects know where to draw. - - Derived from RawTurtle is the subclass :class:`Turtle` (alias: :class:`Pen`), - which draws on "the" :class:`Screen` instance which is automatically - created, if not already present. - - All methods of RawTurtle/Turtle also exist as functions, i.e. part of the - procedure-oriented interface. - -The procedural interface provides functions which are derived from the methods -of the classes :class:`Screen` and :class:`Turtle`. They have the same names as -the corresponding methods. A screen object is automatically created whenever a -function derived from a Screen method is called. An (unnamed) turtle object is -automatically created whenever any of the functions derived from a Turtle method -is called. - -To use multiple turtles on a screen one has to use the object-oriented interface. +Turtle graphics reference +========================= .. note:: + In the following documentation the argument list for functions is given. Methods, of course, have the additional first argument *self* which is omitted here. -Turtle graphics reference -========================= - Turtle methods -------------- @@ -2434,6 +2383,41 @@ Public classes * ``a.rotate(angle)`` rotation +.. _turtle-explanation: + +Explanation +=========== + +A turtle object draws on a screen object, and there a number of key classes in +the turtle object-oriented interface that can be used to create them and relate +them to each other. + +A :class:`Turtle` instance will automatically create a :class:`Screen` +instance if one is not already present. + +``Turtle`` is a subclass of :class:`RawTurtle`, which *doesn't* automatically +create a drawing surface - a *canvas* will need to be provided or created for +it. The *canvas* can be a :class:`tkinter.Canvas`, :class:`ScrolledCanvas` +or :class:`TurtleScreen`. + + +:class:`TurtleScreen` is the basic drawing surface for a +turtle. :class:`Screen` is a subclass of ``TurtleScreen``, and +includes :ref:`some additional methods ` for managing its +appearance (including size and title) and behaviour. ``TurtleScreen``'s +constructor needs a :class:`tkinter.Canvas` or a +:class:`ScrolledCanvas` as an argument. + +The functional interface for turtle graphics uses the various methods of +``Turtle`` and ``TurtleScreen``/``Screen``. Behind the scenes, a screen +object is automatically created whenever a function derived from a ``Screen`` +method is called. Similarly, a turtle object is automatically created +whenever any of the functions derived from a Turtle method is called. + +To use multiple turtles on a screen, the object-oriented interface must be +used. + + Help and configuration ====================== From f106254e0b3c83d9064763d6132b3fe997da901b Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 27 Jul 2023 05:15:06 -0700 Subject: [PATCH 080/632] [3.11] Bump some docs dependencies to resolve a Dependabot security alert (GH-107341) (#107343) Bump some docs dependencies to resolve a Dependabot security alert (GH-107341) (cherry picked from commit f84d77b4e07aeb6241c1ff9932627d3ba059efa8) Co-authored-by: Alex Waygood --- Doc/requirements-oldest-sphinx.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Doc/requirements-oldest-sphinx.txt b/Doc/requirements-oldest-sphinx.txt index d0390a04ea6dd8..94611ca22f09fe 100644 --- a/Doc/requirements-oldest-sphinx.txt +++ b/Doc/requirements-oldest-sphinx.txt @@ -14,11 +14,10 @@ python-docs-theme>=2022.1 # Docutils<0.17, Jinja2<3, and MarkupSafe<2 are additionally specified as # Sphinx 3.2 is incompatible with newer releases of these packages. -Sphinx==3.2.1 alabaster==0.7.13 Babel==2.12.1 -certifi==2022.12.7 -charset-normalizer==3.1.0 +certifi==2023.7.22 +charset-normalizer==3.2.0 colorama==0.4.6 docutils==0.16 idna==3.4 @@ -27,12 +26,13 @@ Jinja2==2.11.3 MarkupSafe==1.1.1 packaging==23.1 Pygments==2.15.1 -requests==2.29.0 +requests==2.31.0 snowballstemmer==2.2.0 +Sphinx==3.2.1 sphinxcontrib-applehelp==1.0.4 sphinxcontrib-devhelp==1.0.2 sphinxcontrib-htmlhelp==2.0.1 sphinxcontrib-jsmath==1.0.1 sphinxcontrib-qthelp==1.0.3 sphinxcontrib-serializinghtml==1.1.5 -urllib3==1.26.15 +urllib3==2.0.4 From 51b302e3f322eba7933da87ff1e6dd462c925859 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 27 Jul 2023 17:36:34 -0700 Subject: [PATCH 081/632] [3.11] gh-106368: Argument clinic tests: improve failure message when tests in `ClinicExternalTests` fail (GH-107364) (#107366) gh-106368: Argument clinic tests: improve failure message when tests in `ClinicExternalTests` fail (GH-107364) (cherry picked from commit 76c26eaca4147ba7e3e8d7379c5a828f0b512a46) Co-authored-by: Alex Waygood --- Lib/test/test_clinic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index 32c4b1e67f9574..1c01fb0ed36888 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -1306,7 +1306,7 @@ def _do_test(self, *args, expect_success=True): ) as proc: proc.wait() if expect_success and proc.returncode: - self.fail("".join(proc.stderr)) + self.fail("".join([*proc.stdout, *proc.stderr])) stdout = proc.stdout.read() stderr = proc.stderr.read() # Clinic never writes to stderr. From 8492aae9bd60174386a374563594a2ba6512f1f6 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 27 Jul 2023 22:50:56 -0700 Subject: [PATCH 082/632] [3.11] gh-106723: forward -Xfrozen_modules option to spawned process interpreters (GH-106724) (#107368) gh-106723: forward -Xfrozen_modules option to spawned process interpreters (GH-106724) (cherry picked from commit 3dcac785810df4d9db50abe90847eaf03bbdaaf4) Co-authored-by: Felipe A. Hernandez Co-authored-by: Kumar Aditya Co-authored-by: Gregory P. Smith --- Lib/subprocess.py | 2 +- .../2023-07-13-14-55-45.gh-issue-106723.KsMufQ.rst | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-07-13-14-55-45.gh-issue-106723.KsMufQ.rst diff --git a/Lib/subprocess.py b/Lib/subprocess.py index fbc76b8d0f14b2..6df5dd551ea67e 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -346,7 +346,7 @@ def _args_from_interpreter_flags(): if dev_mode: args.extend(('-X', 'dev')) for opt in ('faulthandler', 'tracemalloc', 'importtime', - 'showrefcount', 'utf8'): + 'frozen_modules', 'showrefcount', 'utf8'): if opt in xoptions: value = xoptions[opt] if value is True: diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-07-13-14-55-45.gh-issue-106723.KsMufQ.rst b/Misc/NEWS.d/next/Core and Builtins/2023-07-13-14-55-45.gh-issue-106723.KsMufQ.rst new file mode 100644 index 00000000000000..207f397f17d3f3 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-07-13-14-55-45.gh-issue-106723.KsMufQ.rst @@ -0,0 +1 @@ +Propagate ``frozen_modules`` to multiprocessing spawned process interpreters. From 50d26138e67ce30cb61f271ae35f60b2e43d7d38 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 28 Jul 2023 09:21:50 +0300 Subject: [PATCH 083/632] [3.11] gh-107298: Fix doc references to undocumented modules (GH-107300) (GH-107371) (cherry picked from commit 87b39028e5f453a949a1675526c439f6479a04a8) Co-authored-by: Victor Stinner --- Doc/c-api/arg.rst | 2 +- Doc/c-api/codec.rst | 2 +- Doc/c-api/typeobj.rst | 2 +- Doc/c-api/unicode.rst | 2 +- Doc/extending/extending.rst | 4 ++-- Doc/extending/newtypes_tutorial.rst | 8 ++++---- Doc/install/index.rst | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst index 9f7fd2fa8629e6..dfbec82c457bc3 100644 --- a/Doc/c-api/arg.rst +++ b/Doc/c-api/arg.rst @@ -506,7 +506,7 @@ API Functions will be set if there was a failure. This is an example of the use of this function, taken from the sources for the - :mod:`_weakref` helper module for weak references:: + :mod:`!_weakref` helper module for weak references:: static PyObject * weakref_ref(PyObject *self, PyObject *args) diff --git a/Doc/c-api/codec.rst b/Doc/c-api/codec.rst index 235c77c945cc5b..8ae5c4fecd6248 100644 --- a/Doc/c-api/codec.rst +++ b/Doc/c-api/codec.rst @@ -7,7 +7,7 @@ Codec registry and support functions Register a new codec search function. - As side effect, this tries to load the :mod:`encodings` package, if not yet + As side effect, this tries to load the :mod:`!encodings` package, if not yet done, to make sure that it is always first in the list of search functions. .. c:function:: int PyCodec_Unregister(PyObject *search_function) diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index ec357450d004aa..d4b3293ae59e25 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -577,7 +577,7 @@ and :c:data:`PyType_Type` effectively act as defaults.) name, followed by a dot, followed by the type name; for built-in types, it should be just the type name. If the module is a submodule of a package, the full package name is part of the full module name. For example, a type named - :class:`T` defined in module :mod:`M` in subpackage :mod:`Q` in package :mod:`P` + :class:`T` defined in module :mod:`!M` in subpackage :mod:`!Q` in package :mod:`!P` should have the :c:member:`~PyTypeObject.tp_name` initializer ``"P.Q.M.T"``. For :ref:`dynamically allocated type objects `, diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst index a96e0738f716e2..62628c33a2bbdf 100644 --- a/Doc/c-api/unicode.rst +++ b/Doc/c-api/unicode.rst @@ -1304,7 +1304,7 @@ Character Map Codecs This codec is special in that it can be used to implement many different codecs (and this is in fact what was done to obtain most of the standard codecs -included in the :mod:`encodings` package). The codec uses mappings to encode and +included in the :mod:`!encodings` package). The codec uses mappings to encode and decode characters. The mapping objects provided must support the :meth:`__getitem__` mapping interface; dictionaries and sequences work well. diff --git a/Doc/extending/extending.rst b/Doc/extending/extending.rst index 097d86e30269cc..068f6fcdcaccd1 100644 --- a/Doc/extending/extending.rst +++ b/Doc/extending/extending.rst @@ -367,7 +367,7 @@ Note that PyMODINIT_FUNC declares the function as ``PyObject *`` return type, declares any special linkage declarations required by the platform, and for C++ declares the function as ``extern "C"``. -When the Python program imports module :mod:`spam` for the first time, +When the Python program imports module :mod:`!spam` for the first time, :c:func:`PyInit_spam` is called. (See below for comments about embedding Python.) It calls :c:func:`PyModule_Create`, which returns a module object, and inserts built-in function objects into the newly created module based upon the @@ -1208,7 +1208,7 @@ file corresponding to the module provides a macro that takes care of importing the module and retrieving its C API pointers; client modules only have to call this macro before accessing the C API. -The exporting module is a modification of the :mod:`spam` module from section +The exporting module is a modification of the :mod:`!spam` module from section :ref:`extending-simpleexample`. The function :func:`spam.system` does not call the C library function :c:func:`system` directly, but a function :c:func:`PySpam_System`, which would of course do something more complicated in diff --git a/Doc/extending/newtypes_tutorial.rst b/Doc/extending/newtypes_tutorial.rst index 92e9b97e36d770..6bf63c8b9d7bff 100644 --- a/Doc/extending/newtypes_tutorial.rst +++ b/Doc/extending/newtypes_tutorial.rst @@ -37,7 +37,7 @@ object. This sort of thing can only be explained by example, so here's a minimal, but complete, module that defines a new type named :class:`Custom` inside a C -extension module :mod:`custom`: +extension module :mod:`!custom`: .. note:: What we're showing here is the traditional way of defining *static* @@ -55,7 +55,7 @@ from the previous chapter. This file defines three things: #. How the :class:`Custom` **type** behaves: this is the ``CustomType`` struct, which defines a set of flags and function pointers that the interpreter inspects when specific operations are requested. -#. How to initialize the :mod:`custom` module: this is the ``PyInit_custom`` +#. How to initialize the :mod:`!custom` module: this is the ``PyInit_custom`` function and the associated ``custommodule`` struct. The first bit is:: @@ -127,7 +127,7 @@ our objects and in some error messages, for example: TypeError: can only concatenate str (not "custom.Custom") to str Note that the name is a dotted name that includes both the module name and the -name of the type within the module. The module in this case is :mod:`custom` and +name of the type within the module. The module in this case is :mod:`!custom` and the type is :class:`Custom`, so we set the type name to :class:`custom.Custom`. Using the real dotted import path is important to make your type compatible with the :mod:`pydoc` and :mod:`pickle` modules. :: @@ -231,7 +231,7 @@ Adding data and methods to the Basic example ============================================ Let's extend the basic example to add some data and methods. Let's also make -the type usable as a base class. We'll create a new module, :mod:`custom2` that +the type usable as a base class. We'll create a new module, :mod:`!custom2` that adds these capabilities: .. literalinclude:: ../includes/custom2.c diff --git a/Doc/install/index.rst b/Doc/install/index.rst index 6c401c4c13d1ca..fc4377e0d2b823 100644 --- a/Doc/install/index.rst +++ b/Doc/install/index.rst @@ -370,7 +370,7 @@ will expand this to your home directory:: To make Python find the distributions installed with this scheme, you may have to :ref:`modify Python's search path ` or edit -:mod:`sitecustomize` (see :mod:`site`) to call :func:`site.addsitedir` or edit +:mod:`!sitecustomize` (see :mod:`site`) to call :func:`site.addsitedir` or edit :data:`sys.path`. The :option:`!--home` option defines the installation base directory. Files are From c7d9976094e5a16140b5279b1197678529121516 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 27 Jul 2023 23:36:45 -0700 Subject: [PATCH 084/632] [3.11] gh-104432: Use `memcpy()` to avoid misaligned loads (GH-104433) (#107356) gh-104432: Use `memcpy()` to avoid misaligned loads (GH-104433) Fix potential unaligned memory access on C APIs involving returned sequences of `char *` pointers within the :mod:`grp` and :mod:`socket` modules. These were revealed using a ``-fsaniziter=alignment`` build on ARM macOS. (cherry picked from commit f01e4cedba1a17d321664834bb255d9d04ad16ce) Co-authored-by: Christopher Chavez --- ...-07-27-11-47-29.gh-issue-104432.oGHF-z.rst | 4 ++++ Modules/grpmodule.c | 10 +++++++-- Modules/socketmodule.c | 22 ++++++++++++++----- 3 files changed, 29 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-07-27-11-47-29.gh-issue-104432.oGHF-z.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-07-27-11-47-29.gh-issue-104432.oGHF-z.rst b/Misc/NEWS.d/next/Core and Builtins/2023-07-27-11-47-29.gh-issue-104432.oGHF-z.rst new file mode 100644 index 00000000000000..a9ab5cd43f0ffb --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-07-27-11-47-29.gh-issue-104432.oGHF-z.rst @@ -0,0 +1,4 @@ +Fix potential unaligned memory access on C APIs involving returned sequences +of ``char *`` pointers within the :mod:`grp` and :mod:`socket` modules. These +were revealed using a ``-fsaniziter=alignment`` build on ARM macOS. Patch by +Christopher Chavez. diff --git a/Modules/grpmodule.c b/Modules/grpmodule.c index f6298ca0ee84c1..738076c9d1730b 100644 --- a/Modules/grpmodule.c +++ b/Modules/grpmodule.c @@ -65,8 +65,14 @@ mkgrent(PyObject *module, struct group *p) Py_DECREF(v); return NULL; } - for (member = p->gr_mem; *member != NULL; member++) { - PyObject *x = PyUnicode_DecodeFSDefault(*member); + for (member = p->gr_mem; ; member++) { + char *group_member; + // member can be misaligned + memcpy(&group_member, member, sizeof(group_member)); + if (group_member == NULL) { + break; + } + PyObject *x = PyUnicode_DecodeFSDefault(group_member); if (x == NULL || PyList_Append(w, x) != 0) { Py_XDECREF(x); Py_DECREF(w); diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 65d0e10fd253aa..e6d983afa7da85 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -5657,9 +5657,15 @@ gethost_common(struct hostent *h, struct sockaddr *addr, size_t alen, int af) /* SF #1511317: h_aliases can be NULL */ if (h->h_aliases) { - for (pch = h->h_aliases; *pch != NULL; pch++) { + for (pch = h->h_aliases; ; pch++) { int status; - tmp = PyUnicode_FromString(*pch); + char *host_alias; + // pch can be misaligned + memcpy(&host_alias, pch, sizeof(host_alias)); + if (host_alias == NULL) { + break; + } + tmp = PyUnicode_FromString(host_alias); if (tmp == NULL) goto err; @@ -5671,8 +5677,14 @@ gethost_common(struct hostent *h, struct sockaddr *addr, size_t alen, int af) } } - for (pch = h->h_addr_list; *pch != NULL; pch++) { + for (pch = h->h_addr_list; ; pch++) { int status; + char *host_address; + // pch can be misaligned + memcpy(&host_address, pch, sizeof(host_address)); + if (host_address == NULL) { + break; + } switch (af) { @@ -5684,7 +5696,7 @@ gethost_common(struct hostent *h, struct sockaddr *addr, size_t alen, int af) #ifdef HAVE_SOCKADDR_SA_LEN sin.sin_len = sizeof(sin); #endif - memcpy(&sin.sin_addr, *pch, sizeof(sin.sin_addr)); + memcpy(&sin.sin_addr, host_address, sizeof(sin.sin_addr)); tmp = make_ipv4_addr(&sin); if (pch == h->h_addr_list && alen >= sizeof(sin)) @@ -5701,7 +5713,7 @@ gethost_common(struct hostent *h, struct sockaddr *addr, size_t alen, int af) #ifdef HAVE_SOCKADDR_SA_LEN sin6.sin6_len = sizeof(sin6); #endif - memcpy(&sin6.sin6_addr, *pch, sizeof(sin6.sin6_addr)); + memcpy(&sin6.sin6_addr, host_address, sizeof(sin6.sin6_addr)); tmp = make_ipv6_addr(&sin6); if (pch == h->h_addr_list && alen >= sizeof(sin6)) From dcfdfa539964a66fcc39722bada52e440f06a877 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 28 Jul 2023 09:44:54 +0300 Subject: [PATCH 085/632] [3.11] gh-107298: Fix Sphinx warnings in the C API doc (GH-107302) (GH-107373) (cherry picked from commit 391e03fa05b80d17a14ac88d30c974fa2fa00adb) Co-authored-by: Victor Stinner --- Doc/c-api/apiabiversion.rst | 2 +- Doc/c-api/buffer.rst | 2 +- Doc/c-api/bytes.rst | 24 ++++++++++++------------ Doc/c-api/cell.rst | 2 +- Doc/c-api/gcsupport.rst | 2 +- Doc/c-api/iterator.rst | 4 ++-- Doc/c-api/type.rst | 2 +- Doc/c-api/typehints.rst | 2 +- Doc/c-api/unicode.rst | 4 ++-- Doc/c-api/weakref.rst | 8 ++++---- 10 files changed, 26 insertions(+), 26 deletions(-) diff --git a/Doc/c-api/apiabiversion.rst b/Doc/c-api/apiabiversion.rst index 62d542966622ce..f6c8284daeacb0 100644 --- a/Doc/c-api/apiabiversion.rst +++ b/Doc/c-api/apiabiversion.rst @@ -60,7 +60,7 @@ See :ref:`stable` for a discussion of API and ABI stability across versions. Use this for numeric comparisons, e.g. ``#if PY_VERSION_HEX >= ...``. - This version is also available via the symbol :data:`Py_Version`. + This version is also available via the symbol :c:var:`Py_Version`. .. c:var:: const unsigned long Py_Version diff --git a/Doc/c-api/buffer.rst b/Doc/c-api/buffer.rst index 6e5443f0d6cdc5..02b53ec149c733 100644 --- a/Doc/c-api/buffer.rst +++ b/Doc/c-api/buffer.rst @@ -44,7 +44,7 @@ the elements exposed by an :class:`array.array` can be multi-byte values. An example consumer of the buffer interface is the :meth:`~io.BufferedIOBase.write` method of file objects: any object that can export a series of bytes through -the buffer interface can be written to a file. While :meth:`write` only +the buffer interface can be written to a file. While :meth:`!write` only needs read-only access to the internal contents of the object passed to it, other methods such as :meth:`~io.BufferedIOBase.readinto` need write access to the contents of their argument. The buffer interface allows objects to diff --git a/Doc/c-api/bytes.rst b/Doc/c-api/bytes.rst index 21a5ab931c8bfb..110a98e19d8237 100644 --- a/Doc/c-api/bytes.rst +++ b/Doc/c-api/bytes.rst @@ -67,39 +67,39 @@ called with a non-bytes parameter. +-------------------+---------------+--------------------------------+ | Format Characters | Type | Comment | +===================+===============+================================+ - | :attr:`%%` | *n/a* | The literal % character. | + | ``%%`` | *n/a* | The literal % character. | +-------------------+---------------+--------------------------------+ - | :attr:`%c` | int | A single byte, | + | ``%c`` | int | A single byte, | | | | represented as a C int. | +-------------------+---------------+--------------------------------+ - | :attr:`%d` | int | Equivalent to | + | ``%d`` | int | Equivalent to | | | | ``printf("%d")``. [1]_ | +-------------------+---------------+--------------------------------+ - | :attr:`%u` | unsigned int | Equivalent to | + | ``%u`` | unsigned int | Equivalent to | | | | ``printf("%u")``. [1]_ | +-------------------+---------------+--------------------------------+ - | :attr:`%ld` | long | Equivalent to | + | ``%ld`` | long | Equivalent to | | | | ``printf("%ld")``. [1]_ | +-------------------+---------------+--------------------------------+ - | :attr:`%lu` | unsigned long | Equivalent to | + | ``%lu`` | unsigned long | Equivalent to | | | | ``printf("%lu")``. [1]_ | +-------------------+---------------+--------------------------------+ - | :attr:`%zd` | :c:type:`\ | Equivalent to | + | ``%zd`` | :c:type:`\ | Equivalent to | | | Py_ssize_t` | ``printf("%zd")``. [1]_ | +-------------------+---------------+--------------------------------+ - | :attr:`%zu` | size_t | Equivalent to | + | ``%zu`` | size_t | Equivalent to | | | | ``printf("%zu")``. [1]_ | +-------------------+---------------+--------------------------------+ - | :attr:`%i` | int | Equivalent to | + | ``%i`` | int | Equivalent to | | | | ``printf("%i")``. [1]_ | +-------------------+---------------+--------------------------------+ - | :attr:`%x` | int | Equivalent to | + | ``%x`` | int | Equivalent to | | | | ``printf("%x")``. [1]_ | +-------------------+---------------+--------------------------------+ - | :attr:`%s` | const char\* | A null-terminated C character | + | ``%s`` | const char\* | A null-terminated C character | | | | array. | +-------------------+---------------+--------------------------------+ - | :attr:`%p` | const void\* | The hex representation of a C | + | ``%p`` | const void\* | The hex representation of a C | | | | pointer. Mostly equivalent to | | | | ``printf("%p")`` except that | | | | it is guaranteed to start with | diff --git a/Doc/c-api/cell.rst b/Doc/c-api/cell.rst index ac4ef5adc5cc20..f8cd0344fdd1c0 100644 --- a/Doc/c-api/cell.rst +++ b/Doc/c-api/cell.rst @@ -25,7 +25,7 @@ Cell objects are not likely to be useful elsewhere. The type object corresponding to cell objects. -.. c:function:: int PyCell_Check(ob) +.. c:function:: int PyCell_Check(PyObject *ob) Return true if *ob* is a cell object; *ob* must not be ``NULL``. This function always succeeds. diff --git a/Doc/c-api/gcsupport.rst b/Doc/c-api/gcsupport.rst index fc690fd85c9f74..ac938735a67dde 100644 --- a/Doc/c-api/gcsupport.rst +++ b/Doc/c-api/gcsupport.rst @@ -124,7 +124,7 @@ rules: .. versionchanged:: 3.8 - The :c:func:`_PyObject_GC_TRACK` and :c:func:`_PyObject_GC_UNTRACK` macros + The :c:func:`!_PyObject_GC_TRACK` and :c:func:`!_PyObject_GC_UNTRACK` macros have been removed from the public C API. The :c:member:`~PyTypeObject.tp_traverse` handler accepts a function parameter of this type: diff --git a/Doc/c-api/iterator.rst b/Doc/c-api/iterator.rst index 95952237ca746f..6b7ba8c9979163 100644 --- a/Doc/c-api/iterator.rst +++ b/Doc/c-api/iterator.rst @@ -19,7 +19,7 @@ sentinel value is returned. types. -.. c:function:: int PySeqIter_Check(op) +.. c:function:: int PySeqIter_Check(PyObject *op) Return true if the type of *op* is :c:data:`PySeqIter_Type`. This function always succeeds. @@ -38,7 +38,7 @@ sentinel value is returned. two-argument form of the :func:`iter` built-in function. -.. c:function:: int PyCallIter_Check(op) +.. c:function:: int PyCallIter_Check(PyObject *op) Return true if the type of *op* is :c:data:`PyCallIter_Type`. This function always succeeds. diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst index d6f32ed88b673b..01d7fc4dc1ecf1 100644 --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -292,7 +292,7 @@ The following functions and structs are used to create Setting :c:data:`Py_tp_bases` or :c:data:`Py_tp_base` may be problematic on some platforms. To avoid issues, use the *bases* argument of - :py:func:`PyType_FromSpecWithBases` instead. + :c:func:`PyType_FromSpecWithBases` instead. .. versionchanged:: 3.9 diff --git a/Doc/c-api/typehints.rst b/Doc/c-api/typehints.rst index 4c1957a2a1dbca..98fe68737deb81 100644 --- a/Doc/c-api/typehints.rst +++ b/Doc/c-api/typehints.rst @@ -35,7 +35,7 @@ two types exist -- :ref:`GenericAlias ` and ... } - .. seealso:: The data model method :meth:`__class_getitem__`. + .. seealso:: The data model method :meth:`~object.__class_getitem__`. .. versionadded:: 3.9 diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst index 62628c33a2bbdf..1ed1f06f4595d8 100644 --- a/Doc/c-api/unicode.rst +++ b/Doc/c-api/unicode.rst @@ -1306,7 +1306,7 @@ This codec is special in that it can be used to implement many different codecs (and this is in fact what was done to obtain most of the standard codecs included in the :mod:`!encodings` package). The codec uses mappings to encode and decode characters. The mapping objects provided must support the -:meth:`__getitem__` mapping interface; dictionaries and sequences work well. +:meth:`~object.__getitem__` mapping interface; dictionaries and sequences work well. These are the mapping codec APIs: @@ -1349,7 +1349,7 @@ The following codec API is special in that maps Unicode to Unicode. The mapping table must map Unicode ordinal integers to Unicode ordinal integers or ``None`` (causing deletion of the character). - Mapping tables need only provide the :meth:`__getitem__` interface; dictionaries + Mapping tables need only provide the :meth:`~object.__getitem__` interface; dictionaries and sequences work well. Unmapped character ordinals (ones which cause a :exc:`LookupError`) are left untouched and are copied as-is. diff --git a/Doc/c-api/weakref.rst b/Doc/c-api/weakref.rst index f27ec4411b4a26..f46507608606b9 100644 --- a/Doc/c-api/weakref.rst +++ b/Doc/c-api/weakref.rst @@ -11,18 +11,18 @@ simple reference object, and the second acts as a proxy for the original object as much as it can. -.. c:function:: int PyWeakref_Check(ob) +.. c:function:: int PyWeakref_Check(PyObject *ob) Return true if *ob* is either a reference or proxy object. This function always succeeds. -.. c:function:: int PyWeakref_CheckRef(ob) +.. c:function:: int PyWeakref_CheckRef(PyObject *ob) Return true if *ob* is a reference object. This function always succeeds. -.. c:function:: int PyWeakref_CheckProxy(ob) +.. c:function:: int PyWeakref_CheckProxy(PyObject *ob) Return true if *ob* is a proxy object. This function always succeeds. @@ -54,7 +54,7 @@ as much as it can. .. c:function:: PyObject* PyWeakref_GetObject(PyObject *ref) Return the referenced object from a weak reference, *ref*. If the referent is - no longer live, returns :const:`Py_None`. + no longer live, returns ``Py_None``. .. note:: From 32e17d4a3c1da2a8ff082b5cb4f3cd13688a95a1 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 28 Jul 2023 10:03:47 +0300 Subject: [PATCH 086/632] [3.11] gh-107298: Fix more Sphinx warnings in the C API doc (GH-107329) (GH-107377) Declare the following functions as macros, since they are actually macros. It avoids a warning on "TYPE" or "macro" argument. * PyMem_New() * PyMem_Resize() * PyModule_AddIntMacro() * PyModule_AddStringMacro() * PyObject_GC_New() * PyObject_GC_NewVar() * PyObject_New() * PyObject_NewVar() (cherry picked from commit 8d61a71f9c81619e34d4a30b625922ebc83c561b) Co-authored-by: Victor Stinner --- Doc/c-api/allocation.rst | 19 +++++---- Doc/c-api/capsule.rst | 2 +- Doc/c-api/complex.rst | 4 +- Doc/c-api/conversion.rst | 4 +- Doc/c-api/exceptions.rst | 18 ++++----- Doc/c-api/gcsupport.rst | 18 ++++----- Doc/c-api/import.rst | 6 +-- Doc/c-api/init.rst | 18 ++++----- Doc/c-api/init_config.rst | 4 +- Doc/c-api/memory.rst | 16 ++++---- Doc/c-api/module.rst | 8 ++-- Doc/c-api/set.rst | 2 +- Doc/c-api/structures.rst | 2 +- Doc/c-api/sys.rst | 2 +- Doc/c-api/typeobj.rst | 8 ++-- Doc/c-api/unicode.rst | 6 +-- Doc/conf.py | 5 ++- Doc/extending/extending.rst | 20 +++++----- Doc/extending/newtypes_tutorial.rst | 60 ++++++++++++++--------------- Doc/whatsnew/2.3.rst | 2 +- Doc/whatsnew/3.8.rst | 4 +- Doc/whatsnew/3.9.rst | 4 +- 22 files changed, 119 insertions(+), 113 deletions(-) diff --git a/Doc/c-api/allocation.rst b/Doc/c-api/allocation.rst index 0a8fcc5ae5fcdf..44747e29643661 100644 --- a/Doc/c-api/allocation.rst +++ b/Doc/c-api/allocation.rst @@ -27,22 +27,25 @@ Allocating Objects on the Heap length information for a variable-size object. -.. c:function:: TYPE* PyObject_New(TYPE, PyTypeObject *type) +.. c:macro:: PyObject_New(TYPE, typeobj) Allocate a new Python object using the C structure type *TYPE* and the - Python type object *type*. Fields not defined by the Python object header + Python type object *typeobj* (``PyTypeObject*``). + Fields not defined by the Python object header are not initialized; the object's reference count will be one. The size of the memory allocation is determined from the :c:member:`~PyTypeObject.tp_basicsize` field of the type object. -.. c:function:: TYPE* PyObject_NewVar(TYPE, PyTypeObject *type, Py_ssize_t size) +.. c:macro:: PyObject_NewVar(TYPE, typeobj, size) Allocate a new Python object using the C structure type *TYPE* and the - Python type object *type*. Fields not defined by the Python object header + Python type object *typeobj* (``PyTypeObject*``). + Fields not defined by the Python object header are not initialized. The allocated memory allows for the *TYPE* structure - plus *size* fields of the size given by the :c:member:`~PyTypeObject.tp_itemsize` field of - *type*. This is useful for implementing objects like tuples, which are + plus *size* (``Py_ssize_t``) fields of the size + given by the :c:member:`~PyTypeObject.tp_itemsize` field of + *typeobj*. This is useful for implementing objects like tuples, which are able to determine their size at construction time. Embedding the array of fields into the same allocation decreases the number of allocations, improving the memory management efficiency. @@ -50,8 +53,8 @@ Allocating Objects on the Heap .. c:function:: void PyObject_Del(void *op) - Releases memory allocated to an object using :c:func:`PyObject_New` or - :c:func:`PyObject_NewVar`. This is normally called from the + Releases memory allocated to an object using :c:macro:`PyObject_New` or + :c:macro:`PyObject_NewVar`. This is normally called from the :c:member:`~PyTypeObject.tp_dealloc` handler specified in the object's type. The fields of the object should not be accessed after this call as the memory is no longer a valid Python object. diff --git a/Doc/c-api/capsule.rst b/Doc/c-api/capsule.rst index 427ed959c58568..2a1b602dc79c0f 100644 --- a/Doc/c-api/capsule.rst +++ b/Doc/c-api/capsule.rst @@ -64,7 +64,7 @@ Refer to :ref:`using-capsules` for more information on using these objects. The *name* parameter must compare exactly to the name stored in the capsule. If the name stored in the capsule is ``NULL``, the *name* passed in must also - be ``NULL``. Python uses the C function :c:func:`strcmp` to compare capsule + be ``NULL``. Python uses the C function :c:func:`!strcmp` to compare capsule names. diff --git a/Doc/c-api/complex.rst b/Doc/c-api/complex.rst index 6679ce76f1dc6f..e3fd001c599c80 100644 --- a/Doc/c-api/complex.rst +++ b/Doc/c-api/complex.rst @@ -64,7 +64,7 @@ pointers. This is consistent throughout the API. representation. If *divisor* is null, this method returns zero and sets - :c:data:`errno` to :c:macro:`EDOM`. + :c:data:`errno` to :c:macro:`!EDOM`. .. c:function:: Py_complex _Py_c_pow(Py_complex num, Py_complex exp) @@ -73,7 +73,7 @@ pointers. This is consistent throughout the API. representation. If *num* is null and *exp* is not a positive real number, - this method returns zero and sets :c:data:`errno` to :c:macro:`EDOM`. + this method returns zero and sets :c:data:`errno` to :c:macro:`!EDOM`. Complex Numbers as Python Objects diff --git a/Doc/c-api/conversion.rst b/Doc/c-api/conversion.rst index fdb321fe7ab3f2..c5350123dfdfdc 100644 --- a/Doc/c-api/conversion.rst +++ b/Doc/c-api/conversion.rst @@ -119,10 +119,10 @@ The following functions provide locale-independent string to number conversions. .. c:function:: int PyOS_stricmp(const char *s1, const char *s2) Case insensitive comparison of strings. The function works almost - identically to :c:func:`strcmp` except that it ignores the case. + identically to :c:func:`!strcmp` except that it ignores the case. .. c:function:: int PyOS_strnicmp(const char *s1, const char *s2, Py_ssize_t size) Case insensitive comparison of strings. The function works almost - identically to :c:func:`strncmp` except that it ignores the case. + identically to :c:func:`!strncmp` except that it ignores the case. diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst index 6ca53bd29f9e84..8d8b0ac7dd9a93 100644 --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -78,7 +78,7 @@ Printing and clearing This utility function prints a warning message to ``sys.stderr`` when an exception has been set but it is impossible for the interpreter to actually raise the exception. It is used, for example, when an exception occurs in an - :meth:`__del__` method. + :meth:`~object.__del__` method. The function is called with a single argument *obj* that identifies the context in which the unraisable exception occurred. If possible, @@ -154,7 +154,7 @@ For convenience, some of these functions will always return a tuple object whose first item is the integer :c:data:`errno` value and whose second item is the corresponding error message (gotten from :c:func:`!strerror`), and then calls ``PyErr_SetObject(type, object)``. On Unix, when the - :c:data:`errno` value is :c:macro:`EINTR`, indicating an interrupted system call, + :c:data:`errno` value is :c:macro:`!EINTR`, indicating an interrupted system call, this calls :c:func:`PyErr_CheckSignals`, and if that set the error indicator, leaves it set to that. The function always returns ``NULL``, so a wrapper function around a system call can write ``return PyErr_SetFromErrno(type);`` @@ -166,7 +166,7 @@ For convenience, some of these functions will always return a Similar to :c:func:`PyErr_SetFromErrno`, with the additional behavior that if *filenameObject* is not ``NULL``, it is passed to the constructor of *type* as a third parameter. In the case of :exc:`OSError` exception, - this is used to define the :attr:`filename` attribute of the + this is used to define the :attr:`!filename` attribute of the exception instance. @@ -189,12 +189,12 @@ For convenience, some of these functions will always return a .. c:function:: PyObject* PyErr_SetFromWindowsErr(int ierr) This is a convenience function to raise :exc:`WindowsError`. If called with - *ierr* of ``0``, the error code returned by a call to :c:func:`GetLastError` - is used instead. It calls the Win32 function :c:func:`FormatMessage` to retrieve - the Windows description of error code given by *ierr* or :c:func:`GetLastError`, + *ierr* of ``0``, the error code returned by a call to :c:func:`!GetLastError` + is used instead. It calls the Win32 function :c:func:`!FormatMessage` to retrieve + the Windows description of error code given by *ierr* or :c:func:`!GetLastError`, then it constructs a tuple object whose first item is the *ierr* value and whose second item is the corresponding error message (gotten from - :c:func:`FormatMessage`), and then calls ``PyErr_SetObject(PyExc_WindowsError, + :c:func:`!FormatMessage`), and then calls ``PyErr_SetObject(PyExc_WindowsError, object)``. This function always returns ``NULL``. .. availability:: Windows. @@ -567,7 +567,7 @@ Signal Handling be interruptible by user requests (such as by pressing Ctrl-C). .. note:: - The default Python signal handler for :c:macro:`SIGINT` raises the + The default Python signal handler for :c:macro:`!SIGINT` raises the :exc:`KeyboardInterrupt` exception. @@ -578,7 +578,7 @@ Signal Handling single: SIGINT single: KeyboardInterrupt (built-in exception) - Simulate the effect of a :c:macro:`SIGINT` signal arriving. + Simulate the effect of a :c:macro:`!SIGINT` signal arriving. This is equivalent to ``PyErr_SetInterruptEx(SIGINT)``. .. note:: diff --git a/Doc/c-api/gcsupport.rst b/Doc/c-api/gcsupport.rst index ac938735a67dde..6bdbb008f8e74a 100644 --- a/Doc/c-api/gcsupport.rst +++ b/Doc/c-api/gcsupport.rst @@ -25,8 +25,8 @@ include the :c:macro:`Py_TPFLAGS_HAVE_GC` and provide an implementation of the Constructors for container types must conform to two rules: -#. The memory for the object must be allocated using :c:func:`PyObject_GC_New` - or :c:func:`PyObject_GC_NewVar`. +#. The memory for the object must be allocated using :c:macro:`PyObject_GC_New` + or :c:macro:`PyObject_GC_NewVar`. #. Once all the fields which may contain references to other containers are initialized, it must call :c:func:`PyObject_GC_Track`. @@ -52,21 +52,21 @@ rules: class that implements the garbage collector protocol and the child class does *not* include the :c:macro:`Py_TPFLAGS_HAVE_GC` flag. -.. c:function:: TYPE* PyObject_GC_New(TYPE, PyTypeObject *type) +.. c:macro:: PyObject_GC_New(TYPE, typeobj) - Analogous to :c:func:`PyObject_New` but for container objects with the + Analogous to :c:macro:`PyObject_New` but for container objects with the :c:macro:`Py_TPFLAGS_HAVE_GC` flag set. -.. c:function:: TYPE* PyObject_GC_NewVar(TYPE, PyTypeObject *type, Py_ssize_t size) +.. c:macro:: PyObject_GC_NewVar(TYPE, typeobj, size) - Analogous to :c:func:`PyObject_NewVar` but for container objects with the + Analogous to :c:macro:`PyObject_NewVar` but for container objects with the :c:macro:`Py_TPFLAGS_HAVE_GC` flag set. .. c:function:: TYPE* PyObject_GC_Resize(TYPE, PyVarObject *op, Py_ssize_t newsize) - Resize an object allocated by :c:func:`PyObject_NewVar`. Returns the + Resize an object allocated by :c:macro:`PyObject_NewVar`. Returns the resized object or ``NULL`` on failure. *op* must not be tracked by the collector yet. @@ -109,8 +109,8 @@ rules: .. c:function:: void PyObject_GC_Del(void *op) - Releases memory allocated to an object using :c:func:`PyObject_GC_New` or - :c:func:`PyObject_GC_NewVar`. + Releases memory allocated to an object using :c:macro:`PyObject_GC_New` or + :c:macro:`PyObject_GC_NewVar`. .. c:function:: void PyObject_GC_UnTrack(void *op) diff --git a/Doc/c-api/import.rst b/Doc/c-api/import.rst index 887c1793c3ec7a..f2a60bdb023644 100644 --- a/Doc/c-api/import.rst +++ b/Doc/c-api/import.rst @@ -135,10 +135,10 @@ Importing Modules The module's :attr:`__spec__` and :attr:`__loader__` will be set, if not set already, with the appropriate values. The spec's loader will be set to the module's ``__loader__`` (if set) and to an instance of - :class:`SourceFileLoader` otherwise. + :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:`!co_filename`. If applicable, :attr:`__cached__` will also be set. This function will reload the module if it was already imported. See @@ -214,7 +214,7 @@ Importing Modules .. c:function:: PyObject* PyImport_GetImporter(PyObject *path) - Return a finder object for a :data:`sys.path`/:attr:`pkg.__path__` item + Return a finder object for a :data:`sys.path`/:attr:`!pkg.__path__` item *path*, possibly by fetching it from the :data:`sys.path_importer_cache` dict. If it wasn't yet cached, traverse :data:`sys.path_hooks` until a hook is found that can handle the path item. Return ``None`` if no hook could; diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index 05b9b77e4e7c93..88f5944db97c4c 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -25,7 +25,7 @@ The following functions can be safely called before Python is initialized: * :c:func:`PyImport_AppendInittab` * :c:func:`PyImport_ExtendInittab` - * :c:func:`PyInitFrozenExtensions` + * :c:func:`!PyInitFrozenExtensions` * :c:func:`PyMem_SetAllocator` * :c:func:`PyMem_SetupDebugHooks` * :c:func:`PyObject_SetArenaAllocator` @@ -122,7 +122,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. .. c:var:: int Py_IgnoreEnvironmentFlag - Ignore all :envvar:`PYTHON*` environment variables, e.g. + Ignore all :envvar:`!PYTHON*` environment variables, e.g. :envvar:`PYTHONPATH` and :envvar:`PYTHONHOME`, that might be set. Set by the :option:`-E` and :option:`-I` options. @@ -165,7 +165,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. .. c:var:: int Py_LegacyWindowsStdioFlag If the flag is non-zero, use :class:`io.FileIO` instead of - :class:`WindowsConsoleIO` for :mod:`sys` standard streams. + :class:`!io._WindowsConsoleIO` for :mod:`sys` standard streams. Set to ``1`` if the :envvar:`PYTHONLEGACYWINDOWSSTDIO` environment variable is set to a non-empty string. @@ -292,7 +292,7 @@ Initializing and finalizing the interpreter the application. **Bugs and caveats:** The destruction of modules and objects in modules is done - in random order; this may cause destructors (:meth:`__del__` methods) to fail + in random order; this may cause destructors (:meth:`~object.__del__` methods) to fail when they depend on other objects (even functions) or modules. Dynamically loaded extension modules loaded by Python are not unloaded. Small amounts of memory allocated by the Python interpreter may not be freed (if you find a leak, @@ -381,7 +381,7 @@ Process-wide parameters .. deprecated:: 3.11 -.. c:function:: wchar* Py_GetProgramName() +.. c:function:: wchar_t* Py_GetProgramName() .. index:: single: Py_SetProgramName() @@ -872,7 +872,7 @@ the fork, and releasing them afterwards. In addition, it resets any :ref:`lock-objects` in the child. When extending or embedding Python, there is no way to inform Python of additional (non-Python) locks that need to be acquired before or reset after a fork. OS facilities such as -:c:func:`pthread_atfork` would need to be used to accomplish the same thing. +:c:func:`!pthread_atfork` would need to be used to accomplish the same thing. Additionally, when extending or embedding Python, calling :c:func:`fork` directly rather than through :func:`os.fork` (and returning to or calling into Python) may result in a deadlock by one of Python's internal locks @@ -978,7 +978,7 @@ code, or when embedding the Python interpreter: .. note:: Calling this function from a thread when the runtime is finalizing will terminate the thread, even if the thread was not created by Python. - You can use :c:func:`_Py_IsFinalizing` or :func:`sys.is_finalizing` to + You can use :c:func:`!_Py_IsFinalizing` or :func:`sys.is_finalizing` to check if the interpreter is in process of being finalized before calling this function to avoid unwanted termination. @@ -1024,7 +1024,7 @@ with sub-interpreters: .. note:: Calling this function from a thread when the runtime is finalizing will terminate the thread, even if the thread was not created by Python. - You can use :c:func:`_Py_IsFinalizing` or :func:`sys.is_finalizing` to + You can use :c:func:`!_Py_IsFinalizing` or :func:`sys.is_finalizing` to check if the interpreter is in process of being finalized before calling this function to avoid unwanted termination. @@ -1306,7 +1306,7 @@ All of the following functions must be called after :c:func:`Py_Initialize`. .. note:: Calling this function from a thread when the runtime is finalizing will terminate the thread, even if the thread was not created by Python. - You can use :c:func:`_Py_IsFinalizing` or :func:`sys.is_finalizing` to + You can use :c:func:`!_Py_IsFinalizing` or :func:`sys.is_finalizing` to check if the interpreter is in process of being finalized before calling this function to avoid unwanted termination. diff --git a/Doc/c-api/init_config.rst b/Doc/c-api/init_config.rst index 715ad47f13eb45..216755a1f948dc 100644 --- a/Doc/c-api/init_config.rst +++ b/Doc/c-api/init_config.rst @@ -850,7 +850,7 @@ PyConfig .. c:member:: int legacy_windows_stdio If non-zero, use :class:`io.FileIO` instead of - :class:`io.WindowsConsoleIO` for :data:`sys.stdin`, :data:`sys.stdout` + :class:`!io._WindowsConsoleIO` for :data:`sys.stdin`, :data:`sys.stdout` and :data:`sys.stderr`. Set to ``1`` if the :envvar:`PYTHONLEGACYWINDOWSSTDIO` environment @@ -1096,7 +1096,7 @@ PyConfig Set to ``0`` by the :option:`-S` command line option. - :data:`sys.flags.no_site` is set to the inverted value of + :data:`sys.flags.no_site ` is set to the inverted value of :c:member:`~PyConfig.site_import`. Default: ``1``. diff --git a/Doc/c-api/memory.rst b/Doc/c-api/memory.rst index 4ca3b8804427f8..c51aba3f555367 100644 --- a/Doc/c-api/memory.rst +++ b/Doc/c-api/memory.rst @@ -136,7 +136,7 @@ need to be held. The :ref:`default raw memory allocator ` uses the following functions: :c:func:`malloc`, :c:func:`calloc`, :c:func:`realloc` -and :c:func:`free`; call ``malloc(1)`` (or ``calloc(1, 1)``) when requesting +and :c:func:`!free`; call ``malloc(1)`` (or ``calloc(1, 1)``) when requesting zero bytes. .. versionadded:: 3.4 @@ -264,14 +264,14 @@ The following type-oriented macros are provided for convenience. Note that *TYPE* refers to any C type. -.. c:function:: TYPE* PyMem_New(TYPE, size_t n) +.. c:macro:: PyMem_New(TYPE, n) Same as :c:func:`PyMem_Malloc`, but allocates ``(n * sizeof(TYPE))`` bytes of memory. Returns a pointer cast to :c:expr:`TYPE*`. The memory will not have been initialized in any way. -.. c:function:: TYPE* PyMem_Resize(void *p, TYPE, size_t n) +.. c:macro:: PyMem_Resize(p, TYPE, n) Same as :c:func:`PyMem_Realloc`, but the memory block is resized to ``(n * sizeof(TYPE))`` bytes. Returns a pointer cast to :c:expr:`TYPE*`. On return, @@ -423,7 +423,7 @@ Customize Memory Allocators +----------------------------------------------------------+---------------------------------------+ .. versionchanged:: 3.5 - The :c:type:`PyMemAllocator` structure was renamed to + The :c:type:`!PyMemAllocator` structure was renamed to :c:type:`PyMemAllocatorEx` and a new ``calloc`` field was added. @@ -627,8 +627,8 @@ with a fixed size of 256 KiB. It falls back to :c:func:`PyMem_RawMalloc` and The arena allocator uses the following functions: -* :c:func:`VirtualAlloc` and :c:func:`VirtualFree` on Windows, -* :c:func:`mmap` and :c:func:`munmap` if available, +* :c:func:`!VirtualAlloc` and :c:func:`!VirtualFree` on Windows, +* :c:func:`!mmap` and :c:func:`!munmap` if available, * :c:func:`malloc` and :c:func:`free` otherwise. This allocator is disabled if Python is configured with the @@ -732,8 +732,8 @@ allocators operating on different heaps. :: free(buf1); /* Fatal -- should be PyMem_Del() */ In addition to the functions aimed at handling raw memory blocks from the Python -heap, objects in Python are allocated and released with :c:func:`PyObject_New`, -:c:func:`PyObject_NewVar` and :c:func:`PyObject_Del`. +heap, objects in Python are allocated and released with :c:macro:`PyObject_New`, +:c:macro:`PyObject_NewVar` and :c:func:`PyObject_Del`. These will be explained in the next chapter on defining and implementing new object types in C. diff --git a/Doc/c-api/module.rst b/Doc/c-api/module.rst index 6abd550ab80b21..fc4dd4ce053e68 100644 --- a/Doc/c-api/module.rst +++ b/Doc/c-api/module.rst @@ -282,7 +282,7 @@ An alternate way to specify extensions is to request "multi-phase initialization Extension modules created this way behave more like Python modules: the initialization is split between the *creation phase*, when the module object is created, and the *execution phase*, when it is populated. -The distinction is similar to the :py:meth:`__new__` and :py:meth:`__init__` methods +The distinction is similar to the :py:meth:`!__new__` and :py:meth:`!__init__` methods of classes. Unlike modules created using single-phase initialization, these modules are not @@ -293,7 +293,7 @@ By default, multiple modules created from the same definition should be independent: changes to one should not affect the others. This means that all state should be specific to the module object (using e.g. using :c:func:`PyModule_GetState`), or its contents (such as the module's -:attr:`__dict__` or individual classes created with :c:func:`PyType_FromSpec`). +:attr:`~object.__dict__` or individual classes created with :c:func:`PyType_FromSpec`). All modules created using multi-phase initialization are expected to support :ref:`sub-interpreters `. Making sure multiple modules @@ -555,7 +555,7 @@ state: ``NULL``-terminated. Return ``-1`` on error, ``0`` on success. -.. c:function:: int PyModule_AddIntMacro(PyObject *module, macro) +.. c:macro:: PyModule_AddIntMacro(module, macro) Add an int constant to *module*. The name and the value are taken from *macro*. For example ``PyModule_AddIntMacro(module, AF_INET)`` adds the int @@ -563,7 +563,7 @@ state: Return ``-1`` on error, ``0`` on success. -.. c:function:: int PyModule_AddStringMacro(PyObject *module, macro) +.. c:macro:: PyModule_AddStringMacro(module, macro) Add a string constant to *module*. diff --git a/Doc/c-api/set.rst b/Doc/c-api/set.rst index d642a5f1902e2e..7e0ebd2f791a4d 100644 --- a/Doc/c-api/set.rst +++ b/Doc/c-api/set.rst @@ -122,7 +122,7 @@ or :class:`frozenset` or instances of their subtypes. .. c:function:: int PySet_Contains(PyObject *anyset, PyObject *key) Return ``1`` if found, ``0`` if not found, and ``-1`` if an error is encountered. Unlike - the Python :meth:`__contains__` method, this function does not automatically + the Python :meth:`~object.__contains__` method, this function does not automatically convert unhashable sets into temporary frozensets. Raise a :exc:`TypeError` if the *key* is unhashable. Raise :exc:`PyExc_SystemError` if *anyset* is not a :class:`set`, :class:`frozenset`, or an instance of a subtype. diff --git a/Doc/c-api/structures.rst b/Doc/c-api/structures.rst index b892a08ea0837a..c2903b9ec3285b 100644 --- a/Doc/c-api/structures.rst +++ b/Doc/c-api/structures.rst @@ -412,7 +412,7 @@ definition with the same method name. *METH_COEXIST*, the default is to skip repeated definitions. Since slot wrappers are loaded before the method table, the existence of a *sq_contains* slot, for example, would generate a wrapped method named - :meth:`__contains__` and preclude the loading of a corresponding + :meth:`~object.__contains__` and preclude the loading of a corresponding PyCFunction with the same name. With the flag defined, the PyCFunction will be loaded in place of the wrapper object and will co-exist with the slot. This is helpful because calls to PyCFunctions are optimized more diff --git a/Doc/c-api/sys.rst b/Doc/c-api/sys.rst index 57a36c038b592b..34028d6c87e97b 100644 --- a/Doc/c-api/sys.rst +++ b/Doc/c-api/sys.rst @@ -412,7 +412,7 @@ Process Control This function should only be invoked when a condition is detected that would make it dangerous to continue using the Python interpreter; e.g., when the object administration appears to be corrupted. On Unix, the standard C library - function :c:func:`abort` is called which will attempt to produce a :file:`core` + function :c:func:`!abort` is called which will attempt to produce a :file:`core` file. The ``Py_FatalError()`` function is replaced with a macro which logs diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index d4b3293ae59e25..3577e8dc84502e 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -577,7 +577,7 @@ and :c:data:`PyType_Type` effectively act as defaults.) name, followed by a dot, followed by the type name; for built-in types, it should be just the type name. If the module is a submodule of a package, the full package name is part of the full module name. For example, a type named - :class:`T` defined in module :mod:`!M` in subpackage :mod:`!Q` in package :mod:`!P` + :class:`!T` defined in module :mod:`!M` in subpackage :mod:`!Q` in package :mod:`!P` should have the :c:member:`~PyTypeObject.tp_name` initializer ``"P.Q.M.T"``. For :ref:`dynamically allocated type objects `, @@ -671,9 +671,9 @@ and :c:data:`PyType_Type` effectively act as defaults.) permissible to call the object deallocator directly instead of via :c:member:`~PyTypeObject.tp_free`. The object deallocator should be the one used to allocate the instance; this is normally :c:func:`PyObject_Del` if the instance was allocated - using :c:func:`PyObject_New` or :c:func:`PyObject_VarNew`, or + using :c:macro:`PyObject_New` or :c:macro:`PyObject_NewVar`, or :c:func:`PyObject_GC_Del` if the instance was allocated using - :c:func:`PyObject_GC_New` or :c:func:`PyObject_GC_NewVar`. + :c:macro:`PyObject_GC_New` or :c:macro:`PyObject_GC_NewVar`. If the type supports garbage collection (has the :c:macro:`Py_TPFLAGS_HAVE_GC` flag bit set), the destructor should call :c:func:`PyObject_GC_UnTrack` @@ -1091,7 +1091,7 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:macro:: Py_TPFLAGS_HAVE_GC This bit is set when the object supports garbage collection. If this bit - is set, instances must be created using :c:func:`PyObject_GC_New` and + is set, instances must be created using :c:macro:`PyObject_GC_New` and destroyed using :c:func:`PyObject_GC_Del`. More information in section :ref:`supporting-cycle-detection`. This bit also implies that the GC-related fields :c:member:`~PyTypeObject.tp_traverse` and :c:member:`~PyTypeObject.tp_clear` are present in diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst index 1ed1f06f4595d8..8b4252f08dd4cd 100644 --- a/Doc/c-api/unicode.rst +++ b/Doc/c-api/unicode.rst @@ -613,7 +613,7 @@ APIs: Py_ssize_t how_many) Copy characters from one Unicode object into another. This function performs - character conversion when necessary and falls back to :c:func:`memcpy` if + character conversion when necessary and falls back to :c:func:`!memcpy` if possible. Returns ``-1`` and sets an exception on error, otherwise returns the number of copied characters. @@ -800,7 +800,7 @@ system. .. c:function:: PyObject* PyUnicode_DecodeLocale(const char *str, const char *errors) Similar to :c:func:`PyUnicode_DecodeLocaleAndSize`, but compute the string - length using :c:func:`strlen`. + length using :c:func:`!strlen`. .. versionadded:: 3.3 @@ -968,7 +968,7 @@ wchar_t Support most C functions. If *size* is ``NULL`` and the :c:expr:`wchar_t*` string contains null characters a :exc:`ValueError` is raised. - Returns a buffer allocated by :c:func:`PyMem_Alloc` (use + Returns a buffer allocated by :c:macro:`PyMem_New` (use :c:func:`PyMem_Free` to free it) on success. On error, returns ``NULL`` and *\*size* is undefined. Raises a :exc:`MemoryError` if memory allocation is failed. diff --git a/Doc/conf.py b/Doc/conf.py index c0e20c801f183b..d9e7c9f11e7c8c 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -92,7 +92,7 @@ ('c:func', 'vsnprintf'), # Standard C types ('c:type', 'FILE'), - ('c:type', '__int'), + ('c:type', 'int64_t'), ('c:type', 'intmax_t'), ('c:type', 'off_t'), ('c:type', 'ptrdiff_t'), @@ -100,9 +100,12 @@ ('c:type', 'size_t'), ('c:type', 'ssize_t'), ('c:type', 'time_t'), + ('c:type', 'uint64_t'), ('c:type', 'uintmax_t'), + ('c:type', 'uintptr_t'), ('c:type', 'va_list'), ('c:type', 'wchar_t'), + # Standard C structures ('c:struct', 'in6_addr'), ('c:struct', 'in_addr'), ('c:struct', 'stat'), diff --git a/Doc/extending/extending.rst b/Doc/extending/extending.rst index 068f6fcdcaccd1..c13b9371297ee6 100644 --- a/Doc/extending/extending.rst +++ b/Doc/extending/extending.rst @@ -230,7 +230,7 @@ with an exception object:: return m; } -Note that the Python name for the exception object is :exc:`spam.error`. The +Note that the Python name for the exception object is :exc:`!spam.error`. The :c:func:`PyErr_NewException` function may create a class with the base class being :exc:`Exception` (unless another class is passed in instead of ``NULL``), described in :ref:`bltin-exceptions`. @@ -245,7 +245,7 @@ raises the exception could cause a core dump or other unintended side effects. We discuss the use of ``PyMODINIT_FUNC`` as a function return type later in this sample. -The :exc:`spam.error` exception can be raised in your extension module using a +The :exc:`!spam.error` exception can be raised in your extension module using a call to :c:func:`PyErr_SetString` as shown below:: static PyObject * @@ -315,7 +315,7 @@ contexts, as we have seen. The Module's Method Table and Initialization Function ===================================================== -I promised to show how :c:func:`spam_system` is called from Python programs. +I promised to show how :c:func:`!spam_system` is called from Python programs. First, we need to list its name and address in a "method table":: static PyMethodDef SpamMethods[] = { @@ -1030,13 +1030,13 @@ Let's follow the control flow into :c:func:`PyList_SetItem`. The list owns references to all its items, so when item 1 is replaced, it has to dispose of the original item 1. Now let's suppose the original item 1 was an instance of a user-defined class, and let's further suppose that the class defined a -:meth:`__del__` method. If this class instance has a reference count of 1, -disposing of it will call its :meth:`__del__` method. +:meth:`!__del__` method. If this class instance has a reference count of 1, +disposing of it will call its :meth:`!__del__` method. -Since it is written in Python, the :meth:`__del__` method can execute arbitrary +Since it is written in Python, the :meth:`!__del__` method can execute arbitrary Python code. Could it perhaps do something to invalidate the reference to -``item`` in :c:func:`bug`? You bet! Assuming that the list passed into -:c:func:`bug` is accessible to the :meth:`__del__` method, it could execute a +``item`` in :c:func:`!bug`? You bet! Assuming that the list passed into +:c:func:`!bug` is accessible to the :meth:`!__del__` method, it could execute a statement to the effect of ``del list[0]``, and assuming this was the last reference to that object, it would free the memory associated with it, thereby invalidating ``item``. @@ -1057,7 +1057,7 @@ increment the reference count. The correct version of the function reads:: This is a true story. An older version of Python contained variants of this bug and someone spent a considerable amount of time in a C debugger to figure out -why his :meth:`__del__` methods would fail... +why his :meth:`!__del__` methods would fail... The second case of problems with a borrowed reference is a variant involving threads. Normally, multiple threads in the Python interpreter can't get in each @@ -1224,7 +1224,7 @@ The function :c:func:`PySpam_System` is a plain C function, declared return system(command); } -The function :c:func:`spam_system` is modified in a trivial way:: +The function :c:func:`!spam_system` is modified in a trivial way:: static PyObject * spam_system(PyObject *self, PyObject *args) diff --git a/Doc/extending/newtypes_tutorial.rst b/Doc/extending/newtypes_tutorial.rst index 6bf63c8b9d7bff..e35175c9825997 100644 --- a/Doc/extending/newtypes_tutorial.rst +++ b/Doc/extending/newtypes_tutorial.rst @@ -36,7 +36,7 @@ So, if you want to define a new extension type, you need to create a new type object. This sort of thing can only be explained by example, so here's a minimal, but -complete, module that defines a new type named :class:`Custom` inside a C +complete, module that defines a new type named :class:`!Custom` inside a C extension module :mod:`!custom`: .. note:: @@ -50,9 +50,9 @@ extension module :mod:`!custom`: Now that's quite a bit to take in at once, but hopefully bits will seem familiar from the previous chapter. This file defines three things: -#. What a :class:`Custom` **object** contains: this is the ``CustomObject`` - struct, which is allocated once for each :class:`Custom` instance. -#. How the :class:`Custom` **type** behaves: this is the ``CustomType`` struct, +#. What a :class:`!Custom` **object** contains: this is the ``CustomObject`` + struct, which is allocated once for each :class:`!Custom` instance. +#. How the :class:`!Custom` **type** behaves: this is the ``CustomType`` struct, which defines a set of flags and function pointers that the interpreter inspects when specific operations are requested. #. How to initialize the :mod:`!custom` module: this is the ``PyInit_custom`` @@ -128,7 +128,7 @@ our objects and in some error messages, for example: Note that the name is a dotted name that includes both the module name and the name of the type within the module. The module in this case is :mod:`!custom` and -the type is :class:`Custom`, so we set the type name to :class:`custom.Custom`. +the type is :class:`!Custom`, so we set the type name to :class:`!custom.Custom`. Using the real dotted import path is important to make your type compatible with the :mod:`pydoc` and :mod:`pickle` modules. :: @@ -136,7 +136,7 @@ with the :mod:`pydoc` and :mod:`pickle` modules. :: .tp_itemsize = 0, This is so that Python knows how much memory to allocate when creating -new :class:`Custom` instances. :c:member:`~PyTypeObject.tp_itemsize` is +new :class:`!Custom` instances. :c:member:`~PyTypeObject.tp_itemsize` is only used for variable-sized objects and should otherwise be zero. .. note:: @@ -188,7 +188,7 @@ set to ``NULL``. :: } This adds the type to the module dictionary. This allows us to create -:class:`Custom` instances by calling the :class:`Custom` class: +:class:`!Custom` instances by calling the :class:`!Custom` class: .. code-block:: pycon @@ -246,7 +246,7 @@ We've added an extra include:: This include provides declarations that we use to handle attributes, as described a bit later. -The :class:`Custom` type now has three data attributes in its C struct, +The :class:`!Custom` type now has three data attributes in its C struct, *first*, *last*, and *number*. The *first* and *last* variables are Python strings containing first and last names. The *number* attribute is a C integer. @@ -321,7 +321,7 @@ The ``tp_new`` handler is responsible for creating (as opposed to initializing) objects of the type. It is exposed in Python as the :meth:`__new__` method. It is not required to define a ``tp_new`` member, and indeed many extension types will simply reuse :c:func:`PyType_GenericNew` as done in the first -version of the ``Custom`` type above. In this case, we use the ``tp_new`` +version of the :class:`!Custom` type above. In this case, we use the ``tp_new`` handler to initialize the ``first`` and ``last`` attributes to non-``NULL`` default values. @@ -460,7 +460,7 @@ Further, the attributes can be deleted, setting the C pointers to ``NULL``. Eve though we can make sure the members are initialized to non-``NULL`` values, the members can be set to ``NULL`` if the attributes are deleted. -We define a single method, :meth:`Custom.name()`, that outputs the objects name as the +We define a single method, :meth:`!Custom.name()`, that outputs the objects name as the concatenation of the first and last names. :: static PyObject * @@ -477,8 +477,8 @@ concatenation of the first and last names. :: return PyUnicode_FromFormat("%S %S", self->first, self->last); } -The method is implemented as a C function that takes a :class:`Custom` (or -:class:`Custom` subclass) instance as the first argument. Methods always take an +The method is implemented as a C function that takes a :class:`!Custom` (or +:class:`!Custom` subclass) instance as the first argument. Methods always take an instance as the first argument. Methods often take positional and keyword arguments as well, but in this case we don't take any and don't need to accept a positional argument tuple or keyword argument dictionary. This method is @@ -489,8 +489,8 @@ equivalent to the Python method: def name(self): return "%s %s" % (self.first, self.last) -Note that we have to check for the possibility that our :attr:`first` and -:attr:`last` members are ``NULL``. This is because they can be deleted, in which +Note that we have to check for the possibility that our :attr:`!first` and +:attr:`!last` members are ``NULL``. This is because they can be deleted, in which case they are set to ``NULL``. It would be better to prevent deletion of these attributes and to restrict the attribute values to be strings. We'll see how to do that in the next section. @@ -519,7 +519,7 @@ to add the :c:macro:`Py_TPFLAGS_BASETYPE` to our class flag definition:: .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, -We rename :c:func:`PyInit_custom` to :c:func:`PyInit_custom2`, update the +We rename :c:func:`!PyInit_custom` to :c:func:`!PyInit_custom2`, update the module name in the :c:type:`PyModuleDef` struct, and update the full class name in the :c:type:`PyTypeObject` struct. @@ -538,18 +538,18 @@ Finally, we update our :file:`setup.py` file to build the new module: Providing finer control over data attributes ============================================ -In this section, we'll provide finer control over how the :attr:`first` and -:attr:`last` attributes are set in the :class:`Custom` example. In the previous -version of our module, the instance variables :attr:`first` and :attr:`last` +In this section, we'll provide finer control over how the :attr:`!first` and +:attr:`!last` attributes are set in the :class:`!Custom` example. In the previous +version of our module, the instance variables :attr:`!first` and :attr:`!last` could be set to non-string values or even deleted. We want to make sure that these attributes always contain strings. .. literalinclude:: ../includes/custom3.c -To provide greater control, over the :attr:`first` and :attr:`last` attributes, +To provide greater control, over the :attr:`!first` and :attr:`!last` attributes, we'll use custom getter and setter functions. Here are the functions for -getting and setting the :attr:`first` attribute:: +getting and setting the :attr:`!first` attribute:: static PyObject * Custom_getfirst(CustomObject *self, void *closure) @@ -578,13 +578,13 @@ getting and setting the :attr:`first` attribute:: return 0; } -The getter function is passed a :class:`Custom` object and a "closure", which is +The getter function is passed a :class:`!Custom` object and a "closure", which is a void pointer. In this case, the closure is ignored. (The closure supports an advanced usage in which definition data is passed to the getter and setter. This could, for example, be used to allow a single set of getter and setter functions that decide the attribute to get or set based on data in the closure.) -The setter function is passed the :class:`Custom` object, the new value, and the +The setter function is passed the :class:`!Custom` object, the new value, and the closure. The new value may be ``NULL``, in which case the attribute is being deleted. In our setter, we raise an error if the attribute is deleted or if its new value is not a string. @@ -673,11 +673,11 @@ still has a reference from itself. Its reference count doesn't drop to zero. Fortunately, Python's cyclic garbage collector will eventually figure out that the list is garbage and free it. -In the second version of the :class:`Custom` example, we allowed any kind of -object to be stored in the :attr:`first` or :attr:`last` attributes [#]_. +In the second version of the :class:`!Custom` example, we allowed any kind of +object to be stored in the :attr:`!first` or :attr:`!last` attributes [#]_. Besides, in the second and third versions, we allowed subclassing -:class:`Custom`, and subclasses may add arbitrary attributes. For any of -those two reasons, :class:`Custom` objects can participate in cycles: +:class:`!Custom`, and subclasses may add arbitrary attributes. For any of +those two reasons, :class:`!Custom` objects can participate in cycles: .. code-block:: pycon @@ -687,8 +687,8 @@ those two reasons, :class:`Custom` objects can participate in cycles: >>> n = Derived() >>> n.some_attribute = n -To allow a :class:`Custom` instance participating in a reference cycle to -be properly detected and collected by the cyclic GC, our :class:`Custom` type +To allow a :class:`!Custom` instance participating in a reference cycle to +be properly detected and collected by the cyclic GC, our :class:`!Custom` type needs to fill two additional slots and to enable a flag that enables these slots: .. literalinclude:: ../includes/custom4.c @@ -818,7 +818,7 @@ increases an internal counter: .. literalinclude:: ../includes/sublist.c -As you can see, the source code closely resembles the :class:`Custom` examples in +As you can see, the source code closely resembles the :class:`!Custom` examples in previous sections. We will break down the main differences between them. :: typedef struct { @@ -886,7 +886,7 @@ slot with :c:func:`PyType_GenericNew` -- the allocation function from the base type will be inherited. After that, calling :c:func:`PyType_Ready` and adding the type object to the -module is the same as with the basic :class:`Custom` examples. +module is the same as with the basic :class:`!Custom` examples. .. rubric:: Footnotes diff --git a/Doc/whatsnew/2.3.rst b/Doc/whatsnew/2.3.rst index 8d5603073005b0..68a8917a943e78 100644 --- a/Doc/whatsnew/2.3.rst +++ b/Doc/whatsnew/2.3.rst @@ -1847,7 +1847,7 @@ specifically for allocating Python objects. :c:func:`PyObject_Malloc`, :c:func:`PyObject_Realloc`, and :c:func:`PyObject_Free`. * To allocate and free Python objects, use the "object" family - :c:func:`PyObject_New`, :c:func:`PyObject_NewVar`, and :c:func:`PyObject_Del`. + :c:macro:`PyObject_New`, :c:macro:`PyObject_NewVar`, and :c:func:`PyObject_Del`. Thanks to lots of work by Tim Peters, pymalloc in 2.3 also provides debugging features to catch memory overwrites and doubled frees in both extension modules diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index 99291ba48390ce..7bfff94c423f80 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -2058,8 +2058,8 @@ Changes in the C API * Remove :c:macro:`Py_INCREF` on the type object after allocating an instance - if any. - This may happen after calling :c:func:`PyObject_New`, - :c:func:`PyObject_NewVar`, :c:func:`PyObject_GC_New`, + This may happen after calling :c:macro:`PyObject_New`, + :c:macro:`PyObject_NewVar`, :c:func:`PyObject_GC_New`, :c:func:`PyObject_GC_NewVar`, or any other custom allocator that uses :c:func:`PyObject_Init` or :c:func:`PyObject_INIT`. diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index 6de432d6c036bc..666d53ed6da7ba 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -1389,8 +1389,8 @@ Porting to Python 3.9 * :c:func:`PyObject_IS_GC` macro was converted to a function. * The :c:func:`PyObject_NEW` macro becomes an alias to the - :c:func:`PyObject_New` macro, and the :c:func:`PyObject_NEW_VAR` macro - becomes an alias to the :c:func:`PyObject_NewVar` macro. They no longer + :c:macro:`PyObject_New` macro, and the :c:func:`PyObject_NEW_VAR` macro + becomes an alias to the :c:macro:`PyObject_NewVar` macro. They no longer access directly the :c:member:`PyTypeObject.tp_basicsize` member. * :c:func:`PyObject_GET_WEAKREFS_LISTPTR` macro was converted to a function: From d9392c0c0aeedd4d23027ecf20e14af4c227435a Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 28 Jul 2023 11:31:49 +0300 Subject: [PATCH 087/632] [3.11] gh-107298: Fix yet more Sphinx warnings in the C API doc (GH-107345) (GH-107381) (cherry picked from commit 983305268e2291b0a7835621b81bf40cba7c27f3) --- Doc/c-api/capsule.rst | 2 +- Doc/c-api/init_config.rst | 12 +- Doc/c-api/intro.rst | 2 +- Doc/c-api/memory.rst | 2 + Doc/c-api/module.rst | 2 +- Doc/c-api/none.rst | 2 +- Doc/c-api/object.rst | 6 +- Doc/c-api/set.rst | 6 +- Doc/c-api/typeobj.rst | 31 ++-- Doc/extending/embedding.rst | 2 +- Doc/extending/extending.rst | 24 +-- Doc/extending/newtypes.rst | 24 +-- Doc/extending/newtypes_tutorial.rst | 32 ++-- Doc/howto/descriptor.rst | 10 +- Doc/tools/.nitignore | 239 ++++++++++++++++++++++++++++ Doc/whatsnew/2.5.rst | 4 +- Doc/whatsnew/3.8.rst | 2 +- 17 files changed, 327 insertions(+), 75 deletions(-) create mode 100644 Doc/tools/.nitignore diff --git a/Doc/c-api/capsule.rst b/Doc/c-api/capsule.rst index 2a1b602dc79c0f..cdb8aa33e9fd32 100644 --- a/Doc/c-api/capsule.rst +++ b/Doc/c-api/capsule.rst @@ -121,7 +121,7 @@ Refer to :ref:`using-capsules` for more information on using these objects. compared.) In other words, if :c:func:`PyCapsule_IsValid` returns a true value, calls to - any of the accessors (any function starting with :c:func:`PyCapsule_Get`) are + any of the accessors (any function starting with ``PyCapsule_Get``) are guaranteed to succeed. Return a nonzero value if the object is valid and matches the name passed in. diff --git a/Doc/c-api/init_config.rst b/Doc/c-api/init_config.rst index 216755a1f948dc..c6798d68b0e8a7 100644 --- a/Doc/c-api/init_config.rst +++ b/Doc/c-api/init_config.rst @@ -135,6 +135,8 @@ PyStatus Name of the function which created an error, can be ``NULL``. + .. c:namespace:: NULL + Functions to create a status: .. c:function:: PyStatus PyStatus_Ok(void) @@ -210,6 +212,8 @@ PyPreConfig Structure used to preinitialize Python. + .. c:namespace:: NULL + Function to initialize a preconfiguration: .. c:function:: void PyPreConfig_InitPythonConfig(PyPreConfig *preconfig) @@ -222,6 +226,8 @@ PyPreConfig Initialize the preconfiguration with :ref:`Isolated Configuration `. + .. c:namespace:: PyPreConfig + Structure fields: .. c:member:: int allocator @@ -429,6 +435,8 @@ PyConfig When done, the :c:func:`PyConfig_Clear` function must be used to release the configuration memory. + .. c:namespace:: NULL + Structure methods: .. c:function:: void PyConfig_InitPythonConfig(PyConfig *config) @@ -527,6 +535,8 @@ PyConfig The caller of these methods is responsible to handle exceptions (error or exit) using ``PyStatus_Exception()`` and ``Py_ExitStatusException()``. + .. c:namespace:: PyConfig + Structure fields: .. c:member:: PyWideStringList argv @@ -899,7 +909,7 @@ PyConfig .. c:member:: wchar_t* pythonpath_env Module search paths (:data:`sys.path`) as a string separated by ``DELIM`` - (:data:`os.path.pathsep`). + (:data:`os.pathsep`). Set by the :envvar:`PYTHONPATH` environment variable. diff --git a/Doc/c-api/intro.rst b/Doc/c-api/intro.rst index 17710b02abbc4e..a3ccfa08baf706 100644 --- a/Doc/c-api/intro.rst +++ b/Doc/c-api/intro.rst @@ -616,7 +616,7 @@ and lose important information about the exact cause of the error. .. index:: single: sum_sequence() A simple example of detecting exceptions and passing them on is shown in the -:c:func:`sum_sequence` example above. It so happens that this example doesn't +:c:func:`!sum_sequence` example above. It so happens that this example doesn't need to clean up any owned references when it detects an error. The following example function shows some error cleanup. First, to remind you why you like Python, we show the equivalent Python code:: diff --git a/Doc/c-api/memory.rst b/Doc/c-api/memory.rst index c51aba3f555367..8968b26b64320a 100644 --- a/Doc/c-api/memory.rst +++ b/Doc/c-api/memory.rst @@ -431,6 +431,8 @@ Customize Memory Allocators Enum used to identify an allocator domain. Domains: + .. c:namespace:: NULL + .. c:macro:: PYMEM_DOMAIN_RAW Functions: diff --git a/Doc/c-api/module.rst b/Doc/c-api/module.rst index fc4dd4ce053e68..8ca48c852d4e6f 100644 --- a/Doc/c-api/module.rst +++ b/Doc/c-api/module.rst @@ -119,7 +119,7 @@ Module Objects encoded to 'utf-8'. .. deprecated:: 3.2 - :c:func:`PyModule_GetFilename` raises :c:type:`UnicodeEncodeError` on + :c:func:`PyModule_GetFilename` raises :exc:`UnicodeEncodeError` on unencodable filenames, use :c:func:`PyModule_GetFilenameObject` instead. diff --git a/Doc/c-api/none.rst b/Doc/c-api/none.rst index b84a16a28ead56..e238fd1c2d8dc6 100644 --- a/Doc/c-api/none.rst +++ b/Doc/c-api/none.rst @@ -9,7 +9,7 @@ The ``None`` Object Note that the :c:type:`PyTypeObject` for ``None`` is not directly exposed in the Python/C API. Since ``None`` is a singleton, testing for object identity (using -``==`` in C) is sufficient. There is no :c:func:`PyNone_Check` function for the +``==`` in C) is sufficient. There is no :c:func:`!PyNone_Check` function for the same reason. diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index 191a252808bda4..e7d80dbf8c5c13 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -256,7 +256,7 @@ Object Protocol Normally only class objects, i.e. instances of :class:`type` or a derived class, are considered classes. However, objects can override this by having - a :attr:`__bases__` attribute (which must be a tuple of base classes). + a :attr:`~class.__bases__` attribute (which must be a tuple of base classes). .. c:function:: int PyObject_IsInstance(PyObject *inst, PyObject *cls) @@ -273,10 +273,10 @@ Object Protocol is an instance of *cls* if its class is a subclass of *cls*. An instance *inst* can override what is considered its class by having a - :attr:`__class__` attribute. + :attr:`~instance.__class__` attribute. An object *cls* can override if it is considered a class, and what its base - classes are, by having a :attr:`__bases__` attribute (which must be a tuple + classes are, by having a :attr:`~class.__bases__` attribute (which must be a tuple of base classes). diff --git a/Doc/c-api/set.rst b/Doc/c-api/set.rst index 7e0ebd2f791a4d..1e8a09509032f5 100644 --- a/Doc/c-api/set.rst +++ b/Doc/c-api/set.rst @@ -110,7 +110,7 @@ or :class:`frozenset` or instances of their subtypes. .. index:: pair: built-in function; len Return the length of a :class:`set` or :class:`frozenset` object. Equivalent to - ``len(anyset)``. Raises a :exc:`PyExc_SystemError` if *anyset* is not a + ``len(anyset)``. Raises a :exc:`SystemError` if *anyset* is not a :class:`set`, :class:`frozenset`, or an instance of a subtype. @@ -124,7 +124,7 @@ or :class:`frozenset` or instances of their subtypes. Return ``1`` if found, ``0`` if not found, and ``-1`` if an error is encountered. Unlike the Python :meth:`~object.__contains__` method, this function does not automatically convert unhashable sets into temporary frozensets. Raise a :exc:`TypeError` if - the *key* is unhashable. Raise :exc:`PyExc_SystemError` if *anyset* is not a + the *key* is unhashable. Raise :exc:`SystemError` if *anyset* is not a :class:`set`, :class:`frozenset`, or an instance of a subtype. @@ -149,7 +149,7 @@ subtypes but not for instances of :class:`frozenset` or its subtypes. 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` method, this function does not automatically convert unhashable sets into - temporary frozensets. Raise :exc:`PyExc_SystemError` if *set* is not an + temporary frozensets. Raise :exc:`SystemError` if *set* is not an instance of :class:`set` or its subtype. diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 3577e8dc84502e..11f6034b9e330f 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -483,7 +483,7 @@ PyObject Slots -------------- The type object structure extends the :c:type:`PyVarObject` structure. The -:c:member:`~PyVarObject.ob_size` field is used for dynamic types (created by :func:`type_new`, +:c:member:`~PyVarObject.ob_size` field is used for dynamic types (created by :c:func:`!type_new`, usually called from a class statement). Note that :c:data:`PyType_Type` (the metatype) initializes :c:member:`~PyTypeObject.tp_itemsize`, which means that its instances (i.e. type objects) *must* have the :c:member:`~PyVarObject.ob_size` field. @@ -1305,8 +1305,8 @@ and :c:data:`PyType_Type` effectively act as defaults.) The :c:member:`~PyTypeObject.tp_traverse` pointer is used by the garbage collector to detect reference cycles. A typical implementation of a :c:member:`~PyTypeObject.tp_traverse` function simply calls :c:func:`Py_VISIT` on each of the instance's members that are Python - objects that the instance owns. For example, this is function :c:func:`local_traverse` from the - :mod:`_thread` extension module:: + objects that the instance owns. For example, this is function :c:func:`!local_traverse` from the + :mod:`!_thread` extension module:: static int local_traverse(localobject *self, visitproc visit, void *arg) @@ -1658,7 +1658,7 @@ and :c:data:`PyType_Type` effectively act as defaults.) called; it may also be initialized to a dictionary containing initial attributes for the type. Once :c:func:`PyType_Ready` has initialized the type, extra attributes for the type may be added to this dictionary only if they don't - correspond to overloaded operations (like :meth:`__add__`). + correspond to overloaded operations (like :meth:`~object.__add__`). **Inheritance:** @@ -1759,17 +1759,17 @@ and :c:data:`PyType_Type` effectively act as defaults.) **Default:** This slot has no default. For :ref:`static types `, if the - field is ``NULL`` then no :attr:`__dict__` gets created for instances. + field is ``NULL`` then no :attr:`~object.__dict__` gets created for instances. .. c:member:: initproc PyTypeObject.tp_init An optional pointer to an instance initialization function. - This function corresponds to the :meth:`__init__` method of classes. Like - :meth:`__init__`, it is possible to create an instance without calling - :meth:`__init__`, and it is possible to reinitialize an instance by calling its - :meth:`__init__` method again. + This function corresponds to the :meth:`~object.__init__` method of classes. Like + :meth:`!__init__`, it is possible to create an instance without calling + :meth:`!__init__`, and it is possible to reinitialize an instance by calling its + :meth:`!__init__` method again. The function signature is:: @@ -1777,7 +1777,7 @@ and :c:data:`PyType_Type` effectively act as defaults.) The self argument is the instance to be initialized; the *args* and *kwds* arguments represent positional and keyword arguments of the call to - :meth:`__init__`. + :meth:`~object.__init__`. The :c:member:`~PyTypeObject.tp_init` function, if not ``NULL``, is called when an instance is created normally by calling its type, after the type's :c:member:`~PyTypeObject.tp_new` function @@ -2051,7 +2051,7 @@ and :c:data:`PyType_Type` effectively act as defaults.) In other words, it is used to implement :ref:`vectorcall ` for ``type.__call__``. If ``tp_vectorcall`` is ``NULL``, the default call implementation - using :attr:`__new__` and :attr:`__init__` is used. + using :meth:`~object.__new__` and :meth:`~object.__init__` is used. **Inheritance:** @@ -2243,8 +2243,8 @@ Mapping Object Structures .. c:member:: objobjargproc PyMappingMethods.mp_ass_subscript This function is used by :c:func:`PyObject_SetItem`, - :c:func:`PyObject_DelItem`, :c:func:`PyObject_SetSlice` and - :c:func:`PyObject_DelSlice`. It has the same signature as + :c:func:`PyObject_DelItem`, :c:func:`PySequence_SetSlice` and + :c:func:`PySequence_DelSlice`. It has the same signature as :c:func:`!PyObject_SetItem`, but *v* can also be set to ``NULL`` to delete an item. If this slot is ``NULL``, the object does not support item assignment and deletion. @@ -2466,7 +2466,7 @@ Async Object Structures PyObject *am_aiter(PyObject *self); Must return an :term:`asynchronous iterator` object. - See :meth:`__anext__` for details. + See :meth:`~object.__anext__` for details. This slot may be set to ``NULL`` if an object does not implement asynchronous iteration protocol. @@ -2477,7 +2477,8 @@ Async Object Structures PyObject *am_anext(PyObject *self); - Must return an :term:`awaitable` object. See :meth:`__anext__` for details. + Must return an :term:`awaitable` object. + See :meth:`~object.__anext__` for details. This slot may be set to ``NULL``. .. c:member:: sendfunc PyAsyncMethods.am_send diff --git a/Doc/extending/embedding.rst b/Doc/extending/embedding.rst index e64db373344038..4c7c7ec98a6c95 100644 --- a/Doc/extending/embedding.rst +++ b/Doc/extending/embedding.rst @@ -250,7 +250,7 @@ following two statements before the call to :c:func:`Py_Initialize`:: PyImport_AppendInittab("emb", &PyInit_emb); These two lines initialize the ``numargs`` variable, and make the -:func:`emb.numargs` function accessible to the embedded Python interpreter. +:func:`!emb.numargs` function accessible to the embedded Python interpreter. With these extensions, the Python script can do things like .. code-block:: python diff --git a/Doc/extending/extending.rst b/Doc/extending/extending.rst index c13b9371297ee6..72e30a68828072 100644 --- a/Doc/extending/extending.rst +++ b/Doc/extending/extending.rst @@ -195,7 +195,7 @@ The choice of which exception to raise is entirely yours. There are predeclared C objects corresponding to all built-in Python exceptions, such as :c:data:`PyExc_ZeroDivisionError`, which you can use directly. Of course, you should choose exceptions wisely --- don't use :c:data:`PyExc_TypeError` to mean -that a file couldn't be opened (that should probably be :c:data:`PyExc_IOError`). +that a file couldn't be opened (that should probably be :c:data:`PyExc_OSError`). If something's wrong with the argument list, the :c:func:`PyArg_ParseTuple` function usually raises :c:data:`PyExc_TypeError`. If you have an argument whose value must be in a particular range or must satisfy other conditions, @@ -206,7 +206,7 @@ usually declare a static object variable at the beginning of your file:: static PyObject *SpamError; -and initialize it in your module's initialization function (:c:func:`PyInit_spam`) +and initialize it in your module's initialization function (:c:func:`!PyInit_spam`) with an exception object:: PyMODINIT_FUNC @@ -354,7 +354,7 @@ The method table must be referenced in the module definition structure:: This structure, in turn, must be passed to the interpreter in the module's initialization function. The initialization function must be named -:c:func:`PyInit_name`, where *name* is the name of the module, and should be the +:c:func:`!PyInit_name`, where *name* is the name of the module, and should be the only non-\ ``static`` item defined in the module file:: PyMODINIT_FUNC @@ -368,7 +368,7 @@ declares any special linkage declarations required by the platform, and for C++ declares the function as ``extern "C"``. When the Python program imports module :mod:`!spam` for the first time, -:c:func:`PyInit_spam` is called. (See below for comments about embedding Python.) +:c:func:`!PyInit_spam` is called. (See below for comments about embedding Python.) It calls :c:func:`PyModule_Create`, which returns a module object, and inserts built-in function objects into the newly created module based upon the table (an array of :c:type:`PyMethodDef` structures) found in the module definition. @@ -378,7 +378,7 @@ certain errors, or return ``NULL`` if the module could not be initialized satisfactorily. The init function must return the module object to its caller, so that it then gets inserted into ``sys.modules``. -When embedding Python, the :c:func:`PyInit_spam` function is not called +When embedding Python, the :c:func:`!PyInit_spam` function is not called automatically unless there's an entry in the :c:data:`PyImport_Inittab` table. To add the module to the initialization table, use :c:func:`PyImport_AppendInittab`, optionally followed by an import of the module:: @@ -1209,13 +1209,13 @@ the module and retrieving its C API pointers; client modules only have to call this macro before accessing the C API. The exporting module is a modification of the :mod:`!spam` module from section -:ref:`extending-simpleexample`. The function :func:`spam.system` does not call +:ref:`extending-simpleexample`. The function :func:`!spam.system` does not call the C library function :c:func:`system` directly, but a function -:c:func:`PySpam_System`, which would of course do something more complicated in +:c:func:`!PySpam_System`, which would of course do something more complicated in reality (such as adding "spam" to every command). This function -:c:func:`PySpam_System` is also exported to other extension modules. +:c:func:`!PySpam_System` is also exported to other extension modules. -The function :c:func:`PySpam_System` is a plain C function, declared +The function :c:func:`!PySpam_System` is a plain C function, declared ``static`` like everything else:: static int @@ -1278,7 +1278,7 @@ function must take care of initializing the C API pointer array:: } Note that ``PySpam_API`` is declared ``static``; otherwise the pointer -array would disappear when :func:`PyInit_spam` terminates! +array would disappear when :c:func:`!PyInit_spam` terminates! The bulk of the work is in the header file :file:`spammodule.h`, which looks like this:: @@ -1332,8 +1332,8 @@ like this:: #endif /* !defined(Py_SPAMMODULE_H) */ All that a client module must do in order to have access to the function -:c:func:`PySpam_System` is to call the function (or rather macro) -:c:func:`import_spam` in its initialization function:: +:c:func:`!PySpam_System` is to call the function (or rather macro) +:c:func:`!import_spam` in its initialization function:: PyMODINIT_FUNC PyInit_client(void) diff --git a/Doc/extending/newtypes.rst b/Doc/extending/newtypes.rst index 1f2a7d12bc6542..c9d2301d1470c1 100644 --- a/Doc/extending/newtypes.rst +++ b/Doc/extending/newtypes.rst @@ -286,9 +286,9 @@ be read-only or read-write. The structures in the table are defined as:: For each entry in the table, a :term:`descriptor` will be constructed and added to the type which will be able to extract a value from the instance structure. The -:attr:`type` field should contain one of the type codes defined in the +:c:member:`~PyMemberDef.type` field should contain one of the type codes defined in the :file:`structmember.h` header; the value will be used to determine how to -convert Python values to and from C values. The :attr:`flags` field is used to +convert Python values to and from C values. The :c:member:`~PyMemberDef.flags` field is used to store flags which control how the attribute can be accessed. The following flag constants are defined in :file:`structmember.h`; they may be @@ -305,10 +305,10 @@ combined using bitwise-OR. +---------------------------+----------------------------------------------+ .. versionchanged:: 3.10 - :const:`RESTRICTED`, :const:`READ_RESTRICTED` and :const:`WRITE_RESTRICTED` - are deprecated. However, :const:`READ_RESTRICTED` is an alias for - :const:`PY_AUDIT_READ`, so fields that specify either :const:`RESTRICTED` - or :const:`READ_RESTRICTED` will also raise an audit event. + :c:macro:`RESTRICTED`, :c:macro:`READ_RESTRICTED` and :c:macro:`WRITE_RESTRICTED` + are deprecated. However, :c:macro:`READ_RESTRICTED` is an alias for + :c:macro:`PY_AUDIT_READ`, so fields that specify either :c:macro:`RESTRICTED` + or :c:macro:`READ_RESTRICTED` will also raise an audit event. .. index:: single: READONLY @@ -323,7 +323,7 @@ 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. -As with the :c:member:`~PyTypeObject.tp_methods` table, a sentinel entry with a :attr:`name` value +As with the :c:member:`~PyTypeObject.tp_methods` table, a sentinel entry with a :c:member:`~PyMethodDef.name` value of ``NULL`` is required. .. XXX Descriptors need to be explained in more detail somewhere, but not here. @@ -348,7 +348,7 @@ called, so that if you do need to extend their functionality, you'll understand what needs to be done. The :c:member:`~PyTypeObject.tp_getattr` handler is called when the object requires an attribute -look-up. It is called in the same situations where the :meth:`__getattr__` +look-up. It is called in the same situations where the :meth:`~object.__getattr__` method of a class would be called. Here is an example:: @@ -367,8 +367,8 @@ Here is an example:: return NULL; } -The :c:member:`~PyTypeObject.tp_setattr` handler is called when the :meth:`__setattr__` or -:meth:`__delattr__` method of a class instance would be called. When an +The :c:member:`~PyTypeObject.tp_setattr` handler is called when the :meth:`~object.__setattr__` or +:meth:`~object.__delattr__` method of a class instance would be called. When an attribute should be deleted, the third parameter will be ``NULL``. Here is an example that simply raises an exception; if this were really all you wanted, the :c:member:`~PyTypeObject.tp_setattr` handler should be set to ``NULL``. :: @@ -389,7 +389,7 @@ Object Comparison The :c:member:`~PyTypeObject.tp_richcompare` handler is called when comparisons are needed. It is analogous to the :ref:`rich comparison methods `, like -:meth:`__lt__`, and also called by :c:func:`PyObject_RichCompare` and +:meth:`!__lt__`, and also called by :c:func:`PyObject_RichCompare` and :c:func:`PyObject_RichCompareBool`. This function is called with two Python objects and the operator as arguments, @@ -530,7 +530,7 @@ These functions provide support for the iterator protocol. Both handlers take exactly one parameter, the instance for which they are being called, and return a new reference. In the case of an error, they should set an exception and return ``NULL``. :c:member:`~PyTypeObject.tp_iter` corresponds -to the Python :meth:`__iter__` method, while :c:member:`~PyTypeObject.tp_iternext` +to the Python :meth:`~object.__iter__` method, while :c:member:`~PyTypeObject.tp_iternext` corresponds to the Python :meth:`~iterator.__next__` method. Any :term:`iterable` object must implement the :c:member:`~PyTypeObject.tp_iter` diff --git a/Doc/extending/newtypes_tutorial.rst b/Doc/extending/newtypes_tutorial.rst index e35175c9825997..f4684ceb1a72a4 100644 --- a/Doc/extending/newtypes_tutorial.rst +++ b/Doc/extending/newtypes_tutorial.rst @@ -145,7 +145,7 @@ only used for variable-sized objects and should otherwise be zero. :c:member:`~PyTypeObject.tp_basicsize` as its base type, you may have problems with multiple inheritance. A Python subclass of your type will have to list your type first in its :attr:`~class.__bases__`, or else it will not be able to call your type's - :meth:`__new__` method without getting an error. You can avoid this problem by + :meth:`~object.__new__` method without getting an error. You can avoid this problem by ensuring that your type has a larger value for :c:member:`~PyTypeObject.tp_basicsize` than its base type does. Most of the time, this will be true anyway, because either your base type will be :class:`object`, or else you will be adding data members to @@ -164,14 +164,14 @@ We provide a doc string for the type in :c:member:`~PyTypeObject.tp_doc`. :: .tp_doc = PyDoc_STR("Custom objects"), To enable object creation, we have to provide a :c:member:`~PyTypeObject.tp_new` -handler. This is the equivalent of the Python method :meth:`__new__`, but +handler. This is the equivalent of the Python method :meth:`~object.__new__`, but has to be specified explicitly. In this case, we can just use the default implementation provided by the API function :c:func:`PyType_GenericNew`. :: .tp_new = PyType_GenericNew, Everything else in the file should be familiar, except for some code in -:c:func:`PyInit_custom`:: +:c:func:`!PyInit_custom`:: if (PyType_Ready(&CustomType) < 0) return; @@ -220,7 +220,7 @@ Of course, the current Custom type is pretty uninteresting. It has no data and doesn't do anything. It can't even be subclassed. .. note:: - While this documentation showcases the standard :mod:`distutils` module + While this documentation showcases the standard :mod:`!distutils` module for building C extensions, it is recommended in real-world use cases to use the newer and better-maintained ``setuptools`` library. Documentation on how to do this is out of scope for this document and can be found in @@ -279,7 +279,7 @@ This method first clears the reference counts of the two Python attributes. ``NULL`` (which might happen here if ``tp_new`` failed midway). It then calls the :c:member:`~PyTypeObject.tp_free` member of the object's type (computed by ``Py_TYPE(self)``) to free the object's memory. Note that -the object's type might not be :class:`CustomType`, because the object may +the object's type might not be :class:`!CustomType`, because the object may be an instance of a subclass. .. note:: @@ -318,7 +318,7 @@ and install it in the :c:member:`~PyTypeObject.tp_new` member:: .tp_new = Custom_new, The ``tp_new`` handler is responsible for creating (as opposed to initializing) -objects of the type. It is exposed in Python as the :meth:`__new__` method. +objects of the type. It is exposed in Python as the :meth:`~object.__new__` method. It is not required to define a ``tp_new`` member, and indeed many extension types will simply reuse :c:func:`PyType_GenericNew` as done in the first version of the :class:`!Custom` type above. In this case, we use the ``tp_new`` @@ -352,7 +352,7 @@ result against ``NULL`` before proceeding. .. note:: If you are creating a co-operative :c:member:`~PyTypeObject.tp_new` (one - that calls a base type's :c:member:`~PyTypeObject.tp_new` or :meth:`__new__`), + that calls a base type's :c:member:`~PyTypeObject.tp_new` or :meth:`~object.__new__`), you must *not* try to determine what method to call using method resolution order at runtime. Always statically determine what type you are going to call, and call its :c:member:`~PyTypeObject.tp_new` directly, or via @@ -395,14 +395,14 @@ by filling the :c:member:`~PyTypeObject.tp_init` slot. :: .tp_init = (initproc) Custom_init, The :c:member:`~PyTypeObject.tp_init` slot is exposed in Python as the -:meth:`__init__` method. It is used to initialize an object after it's +:meth:`~object.__init__` method. It is used to initialize an object after it's created. Initializers always accept positional and keyword arguments, and they should return either ``0`` on success or ``-1`` on error. Unlike the ``tp_new`` handler, there is no guarantee that ``tp_init`` is called at all (for example, the :mod:`pickle` module by default -doesn't call :meth:`__init__` on unpickled instances). It can also be -called multiple times. Anyone can call the :meth:`__init__` method on +doesn't call :meth:`~object.__init__` on unpickled instances). It can also be +called multiple times. Anyone can call the :meth:`!__init__` method on our objects. For this reason, we have to be extra careful when assigning the new attribute values. We might be tempted, for example to assign the ``first`` member like this:: @@ -715,8 +715,8 @@ participate in cycles:: } For each subobject that can participate in cycles, we need to call the -:c:func:`visit` function, which is passed to the traversal method. The -:c:func:`visit` function takes as arguments the subobject and the extra argument +:c:func:`!visit` function, which is passed to the traversal method. The +:c:func:`!visit` function takes as arguments the subobject and the extra argument *arg* passed to the traversal method. It returns an integer value that must be returned if it is non-zero. @@ -798,9 +798,9 @@ types. It is easiest to inherit from the built in types, since an extension can easily use the :c:type:`PyTypeObject` it needs. It can be difficult to share these :c:type:`PyTypeObject` structures between extension modules. -In this example we will create a :class:`SubList` type that inherits from the +In this example we will create a :class:`!SubList` type that inherits from the built-in :class:`list` type. The new type will be completely compatible with -regular lists, but will have an additional :meth:`increment` method that +regular lists, but will have an additional :meth:`!increment` method that increases an internal counter: .. code-block:: pycon @@ -830,7 +830,7 @@ The primary difference for derived type objects is that the base type's object structure must be the first value. The base type will already include the :c:func:`PyObject_HEAD` at the beginning of its structure. -When a Python object is a :class:`SubList` instance, its ``PyObject *`` pointer +When a Python object is a :class:`!SubList` instance, its ``PyObject *`` pointer can be safely cast to both ``PyListObject *`` and ``SubListObject *``:: static int @@ -842,7 +842,7 @@ can be safely cast to both ``PyListObject *`` and ``SubListObject *``:: return 0; } -We see above how to call through to the :attr:`__init__` method of the base +We see above how to call through to the :meth:`~object.__init__` method of the base type. This pattern is important when writing a type with custom diff --git a/Doc/howto/descriptor.rst b/Doc/howto/descriptor.rst index 3688c47f0d6ec9..1d9424cb735a46 100644 --- a/Doc/howto/descriptor.rst +++ b/Doc/howto/descriptor.rst @@ -779,8 +779,8 @@ by a search through the class's :term:`method resolution order`. If a descriptor is found, it is invoked with ``desc.__get__(None, A)``. -The full C implementation can be found in :c:func:`type_getattro()` and -:c:func:`_PyType_Lookup()` in :source:`Objects/typeobject.c`. +The full C implementation can be found in :c:func:`!type_getattro` and +:c:func:`!_PyType_Lookup` in :source:`Objects/typeobject.c`. Invocation from super @@ -794,7 +794,7 @@ for the base class ``B`` immediately following ``A`` and then returns ``B.__dict__['m'].__get__(obj, A)``. If not a descriptor, ``m`` is returned unchanged. -The full C implementation can be found in :c:func:`super_getattro()` in +The full C implementation can be found in :c:func:`!super_getattro` in :source:`Objects/typeobject.c`. A pure Python equivalent can be found in `Guido's Tutorial `_. @@ -836,8 +836,8 @@ and if they define :meth:`__set_name__`, that method is called with two arguments. The *owner* is the class where the descriptor is used, and the *name* is the class variable the descriptor was assigned to. -The implementation details are in :c:func:`type_new()` and -:c:func:`set_names()` in :source:`Objects/typeobject.c`. +The implementation details are in :c:func:`!type_new` and +:c:func:`!set_names` in :source:`Objects/typeobject.c`. Since the update logic is in :meth:`type.__new__`, notifications only take place at the time of class creation. If descriptors are added to the class diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore new file mode 100644 index 00000000000000..27f483b74184b2 --- /dev/null +++ b/Doc/tools/.nitignore @@ -0,0 +1,239 @@ +# All RST files under Doc/ -- except these -- must pass Sphinx nit-picky mode, +# as tested on the CI via check-warnings.py in reusable-docs.yml. +# Keep lines sorted lexicographically to help avoid merge conflicts. + +Doc/c-api/bool.rst +Doc/c-api/buffer.rst +Doc/c-api/datetime.rst +Doc/c-api/descriptor.rst +Doc/c-api/exceptions.rst +Doc/c-api/file.rst +Doc/c-api/float.rst +Doc/c-api/gcsupport.rst +Doc/c-api/init.rst +Doc/c-api/init_config.rst +Doc/c-api/intro.rst +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/c-api/unicode.rst +Doc/extending/extending.rst +Doc/extending/newtypes.rst +Doc/faq/design.rst +Doc/faq/extending.rst +Doc/faq/gui.rst +Doc/faq/library.rst +Doc/faq/programming.rst +Doc/glossary.rst +Doc/howto/curses.rst +Doc/howto/descriptor.rst +Doc/howto/enum.rst +Doc/howto/isolating-extensions.rst +Doc/howto/logging-cookbook.rst +Doc/howto/logging.rst +Doc/howto/urllib2.rst +Doc/install/index.rst +Doc/library/__future__.rst +Doc/library/abc.rst +Doc/library/ast.rst +Doc/library/asyncio-dev.rst +Doc/library/asyncio-eventloop.rst +Doc/library/asyncio-extending.rst +Doc/library/asyncio-future.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 +Doc/library/bisect.rst +Doc/library/bz2.rst +Doc/library/calendar.rst +Doc/library/cmd.rst +Doc/library/code.rst +Doc/library/codecs.rst +Doc/library/collections.abc.rst +Doc/library/collections.rst +Doc/library/concurrent.futures.rst +Doc/library/concurrent.rst +Doc/library/configparser.rst +Doc/library/contextlib.rst +Doc/library/copy.rst +Doc/library/csv.rst +Doc/library/ctypes.rst +Doc/library/datetime.rst +Doc/library/dbm.rst +Doc/library/decimal.rst +Doc/library/devmode.rst +Doc/library/difflib.rst +Doc/library/dis.rst +Doc/library/doctest.rst +Doc/library/email.charset.rst +Doc/library/email.compat32-message.rst +Doc/library/email.encoders.rst +Doc/library/email.errors.rst +Doc/library/email.generator.rst +Doc/library/email.headerregistry.rst +Doc/library/email.message.rst +Doc/library/email.mime.rst +Doc/library/email.parser.rst +Doc/library/email.policy.rst +Doc/library/enum.rst +Doc/library/exceptions.rst +Doc/library/faulthandler.rst +Doc/library/fcntl.rst +Doc/library/filecmp.rst +Doc/library/fileinput.rst +Doc/library/fractions.rst +Doc/library/ftplib.rst +Doc/library/functions.rst +Doc/library/functools.rst +Doc/library/getpass.rst +Doc/library/gettext.rst +Doc/library/graphlib.rst +Doc/library/gzip.rst +Doc/library/hashlib.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.abc.rst +Doc/library/importlib.resources.rst +Doc/library/importlib.rst +Doc/library/inspect.rst +Doc/library/io.rst +Doc/library/json.rst +Doc/library/locale.rst +Doc/library/logging.config.rst +Doc/library/logging.handlers.rst +Doc/library/logging.rst +Doc/library/lzma.rst +Doc/library/mailbox.rst +Doc/library/mmap.rst +Doc/library/msvcrt.rst +Doc/library/multiprocessing.rst +Doc/library/multiprocessing.shared_memory.rst +Doc/library/netrc.rst +Doc/library/numbers.rst +Doc/library/operator.rst +Doc/library/optparse.rst +Doc/library/os.path.rst +Doc/library/os.rst +Doc/library/pickle.rst +Doc/library/pickletools.rst +Doc/library/platform.rst +Doc/library/plistlib.rst +Doc/library/poplib.rst +Doc/library/pprint.rst +Doc/library/profile.rst +Doc/library/pty.rst +Doc/library/pyclbr.rst +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/sched.rst +Doc/library/select.rst +Doc/library/selectors.rst +Doc/library/shelve.rst +Doc/library/shutil.rst +Doc/library/signal.rst +Doc/library/site.rst +Doc/library/smtplib.rst +Doc/library/socket.rst +Doc/library/socketserver.rst +Doc/library/ssl.rst +Doc/library/stat.rst +Doc/library/stdtypes.rst +Doc/library/string.rst +Doc/library/subprocess.rst +Doc/library/sys.rst +Doc/library/sys_path_init.rst +Doc/library/sysconfig.rst +Doc/library/syslog.rst +Doc/library/tarfile.rst +Doc/library/tempfile.rst +Doc/library/termios.rst +Doc/library/test.rst +Doc/library/textwrap.rst +Doc/library/threading.rst +Doc/library/time.rst +Doc/library/tkinter.rst +Doc/library/tkinter.scrolledtext.rst +Doc/library/tkinter.ttk.rst +Doc/library/traceback.rst +Doc/library/tty.rst +Doc/library/turtle.rst +Doc/library/unittest.mock-examples.rst +Doc/library/unittest.mock.rst +Doc/library/unittest.rst +Doc/library/urllib.error.rst +Doc/library/urllib.parse.rst +Doc/library/urllib.request.rst +Doc/library/uuid.rst +Doc/library/weakref.rst +Doc/library/winreg.rst +Doc/library/winsound.rst +Doc/library/wsgiref.rst +Doc/library/xml.dom.minidom.rst +Doc/library/xml.dom.pulldom.rst +Doc/library/xml.dom.rst +Doc/library/xml.etree.elementtree.rst +Doc/library/xml.rst +Doc/library/xml.sax.handler.rst +Doc/library/xml.sax.reader.rst +Doc/library/xml.sax.rst +Doc/library/xml.sax.utils.rst +Doc/library/xmlrpc.client.rst +Doc/library/xmlrpc.rst +Doc/library/xmlrpc.server.rst +Doc/library/zlib.rst +Doc/license.rst +Doc/reference/compound_stmts.rst +Doc/reference/datamodel.rst +Doc/reference/expressions.rst +Doc/reference/import.rst +Doc/reference/lexical_analysis.rst +Doc/reference/simple_stmts.rst +Doc/tutorial/appendix.rst +Doc/tutorial/classes.rst +Doc/tutorial/controlflow.rst +Doc/tutorial/datastructures.rst +Doc/tutorial/errors.rst +Doc/tutorial/inputoutput.rst +Doc/tutorial/interactive.rst +Doc/tutorial/introduction.rst +Doc/tutorial/modules.rst +Doc/tutorial/stdlib2.rst +Doc/using/cmdline.rst +Doc/using/configure.rst +Doc/using/windows.rst +Doc/whatsnew/2.0.rst +Doc/whatsnew/2.1.rst +Doc/whatsnew/2.2.rst +Doc/whatsnew/2.3.rst +Doc/whatsnew/2.4.rst +Doc/whatsnew/2.5.rst +Doc/whatsnew/2.6.rst +Doc/whatsnew/2.7.rst +Doc/whatsnew/3.0.rst +Doc/whatsnew/3.1.rst +Doc/whatsnew/3.2.rst +Doc/whatsnew/3.3.rst +Doc/whatsnew/3.4.rst +Doc/whatsnew/3.5.rst +Doc/whatsnew/3.6.rst +Doc/whatsnew/3.7.rst +Doc/whatsnew/3.8.rst +Doc/whatsnew/3.9.rst +Doc/whatsnew/3.10.rst +Doc/whatsnew/3.11.rst diff --git a/Doc/whatsnew/2.5.rst b/Doc/whatsnew/2.5.rst index 162d2c342f50fd..a15eefc2953f40 100644 --- a/Doc/whatsnew/2.5.rst +++ b/Doc/whatsnew/2.5.rst @@ -2151,8 +2151,8 @@ Changes to Python's build process and to the C API include: Previously these different families all reduced to the platform's :c:func:`malloc` and :c:func:`free` functions. This meant it didn't matter if - you got things wrong and allocated memory with the :c:func:`PyMem` function but - freed it with the :c:func:`PyObject` function. With 2.5's changes to obmalloc, + you got things wrong and allocated memory with the ``PyMem`` function but + freed it with the ``PyObject`` function. With 2.5's changes to obmalloc, these families now do different things and mismatches will probably result in a segfault. You should carefully test your C extension modules with Python 2.5. diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index 7bfff94c423f80..b3a6f114dd4445 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -1847,7 +1847,7 @@ Changes in Python behavior finalizing, making them consistent with :c:func:`PyEval_RestoreThread`, :c:func:`Py_END_ALLOW_THREADS`, and :c:func:`PyGILState_Ensure`. If this behavior is not desired, guard the call by checking :c:func:`_Py_IsFinalizing` - or :c:func:`sys.is_finalizing`. + or :func:`sys.is_finalizing`. (Contributed by Joannah Nanjekye in :issue:`36475`.) From c3432523d13dbbabe58bd0a13747b801d52e4f5a Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 28 Jul 2023 03:36:19 -0700 Subject: [PATCH 088/632] [3.11] gh-107091: Fix some uses of :const: role (GH-107379) (GH-107385) It is for references, not for literals. (cherry picked from commit 0aa58fa7a62cd0ee7ec27fa87122425aeff0467d) Co-authored-by: Serhiy Storchaka --- Doc/library/fcntl.rst | 6 +++--- Doc/library/fractions.rst | 2 +- Doc/library/logging.handlers.rst | 4 ++-- Doc/library/os.rst | 4 ++-- Doc/library/signal.rst | 2 +- Doc/library/ssl.rst | 2 +- Doc/library/subprocess.rst | 10 +++++----- Doc/library/sys.rst | 8 ++++---- Doc/tools/.nitignore | 1 - Doc/whatsnew/3.3.rst | 20 ++++++++++---------- 10 files changed, 29 insertions(+), 30 deletions(-) diff --git a/Doc/library/fcntl.rst b/Doc/library/fcntl.rst index 13de0c7bc33bf6..1951f80f91e663 100644 --- a/Doc/library/fcntl.rst +++ b/Doc/library/fcntl.rst @@ -166,9 +166,9 @@ The module defines the following functions: which the lock starts, relative to *whence*, and *whence* is as with :func:`io.IOBase.seek`, specifically: - * :const:`0` -- relative to the start of the file (:const:`os.SEEK_SET`) - * :const:`1` -- relative to the current buffer position (:const:`os.SEEK_CUR`) - * :const:`2` -- relative to the end of the file (:const:`os.SEEK_END`) + * ``0`` -- relative to the start of the file (:const:`os.SEEK_SET`) + * ``1`` -- relative to the current buffer position (:const:`os.SEEK_CUR`) + * ``2`` -- relative to the end of the file (:const:`os.SEEK_END`) The default for *start* is 0, which means to start at the beginning of the file. The default for *len* is 0 which means to lock to the end of the file. The diff --git a/Doc/library/fractions.rst b/Doc/library/fractions.rst index 85bec73eafc9f5..c751253a51c86b 100644 --- a/Doc/library/fractions.rst +++ b/Doc/library/fractions.rst @@ -25,7 +25,7 @@ another rational number, or from a string. The first version requires that *numerator* and *denominator* are instances of :class:`numbers.Rational` and returns a new :class:`Fraction` instance - with value ``numerator/denominator``. If *denominator* is :const:`0`, it + with value ``numerator/denominator``. If *denominator* is ``0``, it raises a :exc:`ZeroDivisionError`. The second version requires that *other_fraction* is an instance of :class:`numbers.Rational` and returns a :class:`Fraction` instance with the same value. The next two versions accept diff --git a/Doc/library/logging.handlers.rst b/Doc/library/logging.handlers.rst index d1566957e7ed14..f9b3b9b9abe656 100644 --- a/Doc/library/logging.handlers.rst +++ b/Doc/library/logging.handlers.rst @@ -97,7 +97,7 @@ sends logging output to a disk file. It inherits the output functionality from Returns a new instance of the :class:`FileHandler` class. The specified file is opened and used as the stream for logging. If *mode* is not specified, - :const:`'a'` is used. If *encoding* is not ``None``, it is used to open the file + ``'a'`` is used. If *encoding* is not ``None``, it is used to open the file with that encoding. If *delay* is true, then file opening is deferred until the first call to :meth:`emit`. By default, the file grows indefinitely. If *errors* is specified, it's used to determine how encoding errors are handled. @@ -182,7 +182,7 @@ for this value. Returns a new instance of the :class:`WatchedFileHandler` class. The specified file is opened and used as the stream for logging. If *mode* is not specified, - :const:`'a'` is used. If *encoding* is not ``None``, it is used to open the file + ``'a'`` is used. If *encoding* is not ``None``, it is used to open the file with that encoding. If *delay* is true, then file opening is deferred until the first call to :meth:`emit`. By default, the file grows indefinitely. If *errors* is provided, it determines how encoding errors are handled. diff --git a/Doc/library/os.rst b/Doc/library/os.rst index bb9cbae2b13450..5577921478452a 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -401,11 +401,11 @@ process and user. On macOS, :func:`getgroups` behavior differs somewhat from other Unix platforms. If the Python interpreter was built with a - deployment target of :const:`10.5` or earlier, :func:`getgroups` returns + deployment target of ``10.5`` or earlier, :func:`getgroups` returns the list of effective group ids associated with the current user process; this list is limited to a system-defined number of entries, typically 16, and may be modified by calls to :func:`setgroups` if suitably privileged. - If built with a deployment target greater than :const:`10.5`, + If built with a deployment target greater than ``10.5``, :func:`getgroups` returns the current group access list for the user associated with the effective user id of the process; the group access list may change over the lifetime of the process, it is not affected by diff --git a/Doc/library/signal.rst b/Doc/library/signal.rst index e53315bee3ea3e..7ee5ece8859825 100644 --- a/Doc/library/signal.rst +++ b/Doc/library/signal.rst @@ -656,7 +656,7 @@ The :mod:`signal` module defines the following functions: .. function:: sigtimedwait(sigset, timeout) Like :func:`sigwaitinfo`, but takes an additional *timeout* argument - specifying a timeout. If *timeout* is specified as :const:`0`, a poll is + specifying a timeout. If *timeout* is specified as ``0``, a poll is performed. Returns :const:`None` if a timeout occurs. .. availability:: Unix. diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index fe896be2faeb5d..25369969f544b5 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -344,7 +344,7 @@ Random generation Mix the given *bytes* into the SSL pseudo-random number generator. The parameter *entropy* (a float) is a lower bound on the entropy contained in - string (so you can always use :const:`0.0`). See :rfc:`1750` for more + string (so you can always use ``0.0``). See :rfc:`1750` for more information on sources of entropy. .. versionchanged:: 3.5 diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst index 78718f7983e041..6ef3c34b4fb81d 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -463,9 +463,9 @@ functions. :func:`open` function when creating the stdin/stdout/stderr pipe file objects: - - :const:`0` means unbuffered (read and write are one + - ``0`` means unbuffered (read and write are one system call and can return short) - - :const:`1` means line buffered + - ``1`` means line buffered (only usable if ``text=True`` or ``universal_newlines=True``) - any other positive value means use a buffer of approximately that size @@ -475,7 +475,7 @@ functions. .. versionchanged:: 3.3.1 *bufsize* now defaults to -1 to enable buffering by default to match the behavior that most code expects. In versions prior to Python 3.2.4 and - 3.3.1 it incorrectly defaulted to :const:`0` which was unbuffered + 3.3.1 it incorrectly defaulted to ``0`` which was unbuffered and allowed short reads. This was unintentional and did not match the behavior of Python 2 as most code expected. @@ -540,8 +540,8 @@ functions. :exc:`RuntimeError`. The new restriction may affect applications that are deployed in mod_wsgi, uWSGI, and other embedded environments. - If *close_fds* is true, all file descriptors except :const:`0`, :const:`1` and - :const:`2` will be closed before the child process is executed. Otherwise + If *close_fds* is true, all file descriptors except ``0``, ``1`` and + ``2`` will be closed before the child process is executed. Otherwise when *close_fds* is false, file descriptors obey their inheritable flag as described in :ref:`fd_inheritance`. diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index dca35cc731b695..b03860603c2862 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -850,19 +850,19 @@ always available. ``sys.getwindowsversion().major``. For compatibility with prior versions, only the first 5 elements are retrievable by indexing. - *platform* will be :const:`2 (VER_PLATFORM_WIN32_NT)`. + *platform* will be ``2`` (VER_PLATFORM_WIN32_NT). *product_type* may be one of the following values: +---------------------------------------+---------------------------------+ | Constant | Meaning | +=======================================+=================================+ - | :const:`1 (VER_NT_WORKSTATION)` | The system is a workstation. | + | ``1`` (VER_NT_WORKSTATION) | The system is a workstation. | +---------------------------------------+---------------------------------+ - | :const:`2 (VER_NT_DOMAIN_CONTROLLER)` | The system is a domain | + | ``2`` (VER_NT_DOMAIN_CONTROLLER) | The system is a domain | | | controller. | +---------------------------------------+---------------------------------+ - | :const:`3 (VER_NT_SERVER)` | The system is a server, but not | + | ``3`` (VER_NT_SERVER) | The system is a server, but not | | | a domain controller. | +---------------------------------------+---------------------------------+ diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 27f483b74184b2..22eb47856f5714 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -90,7 +90,6 @@ Doc/library/faulthandler.rst Doc/library/fcntl.rst Doc/library/filecmp.rst Doc/library/fileinput.rst -Doc/library/fractions.rst Doc/library/ftplib.rst Doc/library/functions.rst Doc/library/functools.rst diff --git a/Doc/whatsnew/3.3.rst b/Doc/whatsnew/3.3.rst index 1fda66cf9f0eaf..9fb06c1d1f807b 100644 --- a/Doc/whatsnew/3.3.rst +++ b/Doc/whatsnew/3.3.rst @@ -1134,20 +1134,20 @@ API changes * The C module has the following context limits, depending on the machine architecture: - +-------------------+---------------------+------------------------------+ - | | 32-bit | 64-bit | - +===================+=====================+==============================+ - | :const:`MAX_PREC` | :const:`425000000` | :const:`999999999999999999` | - +-------------------+---------------------+------------------------------+ - | :const:`MAX_EMAX` | :const:`425000000` | :const:`999999999999999999` | - +-------------------+---------------------+------------------------------+ - | :const:`MIN_EMIN` | :const:`-425000000` | :const:`-999999999999999999` | - +-------------------+---------------------+------------------------------+ + +-------------------+----------------+-------------------------+ + | | 32-bit | 64-bit | + +===================+================+=========================+ + | :const:`MAX_PREC` | ``425000000`` | ``999999999999999999`` | + +-------------------+----------------+-------------------------+ + | :const:`MAX_EMAX` | ``425000000`` | ``999999999999999999`` | + +-------------------+----------------+-------------------------+ + | :const:`MIN_EMIN` | ``-425000000`` | ``-999999999999999999`` | + +-------------------+----------------+-------------------------+ * In the context templates (:class:`~decimal.DefaultContext`, :class:`~decimal.BasicContext` and :class:`~decimal.ExtendedContext`) the magnitude of :attr:`~decimal.Context.Emax` and - :attr:`~decimal.Context.Emin` has changed to :const:`999999`. + :attr:`~decimal.Context.Emin` has changed to ``999999``. * The :class:`~decimal.Decimal` constructor in decimal.py does not observe the context limits and converts values with arbitrary exponents or precision From 4049c5d6f85627f117e9be6a1729a17fce899645 Mon Sep 17 00:00:00 2001 From: justdan6 <134341009+justdan6@users.noreply.github.com> Date: Fri, 28 Jul 2023 17:36:54 -0600 Subject: [PATCH 089/632] [3.11] gh-106881: Check for linux/limits.h before including it (#107397) (#107415) * [3.11] gh-106881: Check for linux/limits.h before including it (#107397) * Check for linux/limits.h before including it Co-authored-by: Erlend E. Aasland (cherry picked from commit 11c055f5ff1a353de6d2e77f2af24aaa782878ba) * Fix sphinx-lint error in NEWS entry --- .../next/Build/2023-07-28-18-17-33.gh-issue-106881.U3Ezdq.rst | 1 + Modules/posixmodule.c | 2 +- configure | 2 +- configure.ac | 2 +- pyconfig.h.in | 3 +++ 5 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2023-07-28-18-17-33.gh-issue-106881.U3Ezdq.rst diff --git a/Misc/NEWS.d/next/Build/2023-07-28-18-17-33.gh-issue-106881.U3Ezdq.rst b/Misc/NEWS.d/next/Build/2023-07-28-18-17-33.gh-issue-106881.U3Ezdq.rst new file mode 100644 index 00000000000000..7febf99c48a79b --- /dev/null +++ b/Misc/NEWS.d/next/Build/2023-07-28-18-17-33.gh-issue-106881.U3Ezdq.rst @@ -0,0 +1 @@ +Check for ``linux/limits.h`` before including it in ``Modules/posixmodule.c``. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index c000a320322875..9d4228896d230e 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -280,7 +280,7 @@ corresponding Unix manual entries for more information on calls."); # undef HAVE_SCHED_SETAFFINITY #endif -#if defined(HAVE_SYS_XATTR_H) && defined(__linux__) && !defined(__FreeBSD_kernel__) && !defined(__GNU__) +#if defined(HAVE_SYS_XATTR_H) && defined(HAVE_LINUX_LIMITS_H) && !defined(__FreeBSD_kernel__) && !defined(__GNU__) # define USE_XATTRS # include // Needed for XATTR_SIZE_MAX on musl libc. #endif diff --git a/configure b/configure index d686e09d81c2dd..af4a5bbfdfa1a4 100755 --- a/configure +++ b/configure @@ -9191,7 +9191,7 @@ $as_echo "#define STDC_HEADERS 1" >>confdefs.h # checks for header files for ac_header in \ alloca.h asm/types.h bluetooth.h conio.h crypt.h direct.h dlfcn.h endian.h errno.h fcntl.h grp.h \ - ieeefp.h io.h langinfo.h libintl.h libutil.h linux/auxvec.h sys/auxv.h linux/memfd.h \ + ieeefp.h io.h langinfo.h libintl.h libutil.h linux/auxvec.h sys/auxv.h linux/limits.h linux/memfd.h \ linux/random.h linux/soundcard.h \ linux/tipc.h linux/wait.h netdb.h netinet/in.h netpacket/packet.h poll.h process.h pthread.h pty.h \ sched.h setjmp.h shadow.h signal.h spawn.h stropts.h sys/audioio.h sys/bsdtty.h sys/devpoll.h \ diff --git a/configure.ac b/configure.ac index 35026f7a2369a7..e1cbb7c7fbe9d9 100644 --- a/configure.ac +++ b/configure.ac @@ -2668,7 +2668,7 @@ AC_DEFINE(STDC_HEADERS, 1, [Define to 1 if you have the ANSI C header files.]) # checks for header files AC_CHECK_HEADERS([ \ alloca.h asm/types.h bluetooth.h conio.h crypt.h direct.h dlfcn.h endian.h errno.h fcntl.h grp.h \ - ieeefp.h io.h langinfo.h libintl.h libutil.h linux/auxvec.h sys/auxv.h linux/memfd.h \ + ieeefp.h io.h langinfo.h libintl.h libutil.h linux/auxvec.h sys/auxv.h linux/limits.h linux/memfd.h \ linux/random.h linux/soundcard.h \ linux/tipc.h linux/wait.h netdb.h netinet/in.h netpacket/packet.h poll.h process.h pthread.h pty.h \ sched.h setjmp.h shadow.h signal.h spawn.h stropts.h sys/audioio.h sys/bsdtty.h sys/devpoll.h \ diff --git a/pyconfig.h.in b/pyconfig.h.in index 75f1d90e9bd376..0536047f573ce6 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -718,6 +718,9 @@ /* Define if compiling using Linux 4.1 or later. */ #undef HAVE_LINUX_CAN_RAW_JOIN_FILTERS +/* Define to 1 if you have the header file. */ +#undef HAVE_LINUX_LIMITS_H + /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_MEMFD_H From 733a2a90466e879fa4b87fcbb4f0a61ac70003dc Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 29 Jul 2023 08:56:26 +0300 Subject: [PATCH 090/632] [3.11] gh-107091: Fix some uses of :func: role (GH-107378) (GH-107417) :c:func: or :c:macro: should be used instead. (cherry picked from commit 413ba8943e2f1d896a0568eb571a041b88589440) --- Doc/whatsnew/2.0.rst | 12 ++++++------ Doc/whatsnew/2.1.rst | 18 +++++++++--------- Misc/NEWS.d/3.11.0b1.rst | 2 +- Misc/NEWS.d/3.9.0a1.rst | 2 +- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Doc/whatsnew/2.0.rst b/Doc/whatsnew/2.0.rst index cd9fa89ca8a904..096248e41b8496 100644 --- a/Doc/whatsnew/2.0.rst +++ b/Doc/whatsnew/2.0.rst @@ -664,7 +664,7 @@ extra set of parentheses to pass both values as a tuple: ``L.append( (1,2) )``. The earlier versions of these methods were more forgiving because they used an old function in Python's C interface to parse their arguments; 2.0 modernizes -them to use :func:`PyArg_ParseTuple`, the current argument parsing function, +them to use :c:func:`PyArg_ParseTuple`, the current argument parsing function, which provides more helpful error messages and treats multi-argument calls as errors. If you absolutely must use 2.0 but can't fix your code, you can edit :file:`Objects/listobject.c` and define the preprocessor symbol @@ -766,7 +766,7 @@ file, :file:`Include/pyport.h`. Vladimir Marangozov's long-awaited malloc restructuring was completed, to make it easy to have the Python interpreter use a custom allocator instead of C's -standard :func:`malloc`. For documentation, read the comments in +standard :c:func:`malloc`. For documentation, read the comments in :file:`Include/pymem.h` and :file:`Include/objimpl.h`. For the lengthy discussions during which the interface was hammered out, see the web archives of the 'patches' and 'python-dev' lists at python.org. @@ -794,15 +794,15 @@ are generating Python code would run into this limit. A patch by Charles G. Waldman raises the limit from ``2**16`` to ``2**32``. Three new convenience functions intended for adding constants to a module's -dictionary at module initialization time were added: :func:`PyModule_AddObject`, -:func:`PyModule_AddIntConstant`, and :func:`PyModule_AddStringConstant`. Each +dictionary at module initialization time were added: :c:func:`PyModule_AddObject`, +:c:func:`PyModule_AddIntConstant`, and :c:func:`PyModule_AddStringConstant`. Each of these functions takes a module object, a null-terminated C string containing the name to be added, and a third argument for the value to be assigned to the name. This third argument is, respectively, a Python object, a C long, or a C string. -A wrapper API was added for Unix-style signal handlers. :func:`PyOS_getsig` gets -a signal handler and :func:`PyOS_setsig` will set a new handler. +A wrapper API was added for Unix-style signal handlers. :c:func:`PyOS_getsig` gets +a signal handler and :c:func:`PyOS_setsig` will set a new handler. .. ====================================================================== diff --git a/Doc/whatsnew/2.1.rst b/Doc/whatsnew/2.1.rst index 676da702b39693..f0e1ded75a9d27 100644 --- a/Doc/whatsnew/2.1.rst +++ b/Doc/whatsnew/2.1.rst @@ -692,8 +692,8 @@ applied, and 136 bugs fixed; both figures are likely to be underestimates. Some of the more notable changes are: * A specialized object allocator is now optionally available, that should be - faster than the system :func:`malloc` and have less memory overhead. The - allocator uses C's :func:`malloc` function to get large pools of memory, and + faster than the system :c:func:`malloc` and have less memory overhead. The + allocator uses C's :c:func:`!malloc` function to get large pools of memory, and then fulfills smaller memory requests from these pools. It can be enabled by providing the :option:`!--with-pymalloc` option to the :program:`configure` script; see :file:`Objects/obmalloc.c` for the implementation details. @@ -701,13 +701,13 @@ of the more notable changes are: Authors of C extension modules should test their code with the object allocator enabled, because some incorrect code may break, causing core dumps at runtime. There are a bunch of memory allocation functions in Python's C API that have - previously been just aliases for the C library's :func:`malloc` and - :func:`free`, meaning that if you accidentally called mismatched functions, the + previously been just aliases for the C library's :c:func:`malloc` and + :c:func:`free`, meaning that if you accidentally called mismatched functions, the error wouldn't be noticeable. When the object allocator is enabled, these - functions aren't aliases of :func:`malloc` and :func:`free` any more, and + functions aren't aliases of :c:func:`!malloc` and :c:func:`!free` any more, and calling the wrong function to free memory will get you a core dump. For - example, if memory was allocated using :func:`PyMem_New`, it has to be freed - using :func:`PyMem_Del`, not :func:`free`. A few modules included with Python + example, if memory was allocated using :c:macro:`PyMem_New`, it has to be freed + using :c:func:`PyMem_Del`, not :c:func:`!free`. A few modules included with Python fell afoul of this and had to be fixed; doubtless there are more third-party modules that will have the same problem. @@ -717,7 +717,7 @@ of the more notable changes are: complain about its lack of speed, and because it's often been used as a naïve benchmark. The :meth:`readline` method of file objects has therefore been rewritten to be much faster. The exact amount of the speedup will vary from - platform to platform depending on how slow the C library's :func:`getc` was, but + platform to platform depending on how slow the C library's :c:func:`!getc` was, but is around 66%, and potentially much faster on some particular operating systems. Tim Peters did much of the benchmarking and coding for this change, motivated by a discussion in comp.lang.python. @@ -770,7 +770,7 @@ of the more notable changes are: reorganization done by Jeremy Hylton. * C extensions which import other modules have been changed to use - :func:`PyImport_ImportModule`, which means that they will use any import hooks + :c:func:`PyImport_ImportModule`, which means that they will use any import hooks that have been installed. This is also encouraged for third-party extensions that need to import some other module from C code. diff --git a/Misc/NEWS.d/3.11.0b1.rst b/Misc/NEWS.d/3.11.0b1.rst index ad52bd14dbfd1d..6b601489a77285 100644 --- a/Misc/NEWS.d/3.11.0b1.rst +++ b/Misc/NEWS.d/3.11.0b1.rst @@ -1799,7 +1799,7 @@ The documentation now lists which members of C structs are part of the .. nonce: FIVe9I .. section: Documentation -All docstrings in code snippets are now wrapped into :func:`PyDoc_STR` to +All docstrings in code snippets are now wrapped into :c:macro:`PyDoc_STR` to follow the guideline of `PEP 7's Documentation Strings paragraph `_. Patch by Oleg Iarygin. diff --git a/Misc/NEWS.d/3.9.0a1.rst b/Misc/NEWS.d/3.9.0a1.rst index 97e07a643e59f8..64410a1d34dfeb 100644 --- a/Misc/NEWS.d/3.9.0a1.rst +++ b/Misc/NEWS.d/3.9.0a1.rst @@ -5686,7 +5686,7 @@ positional argument. .. nonce: zrmgki .. section: C API -Add :func:`PyConfig_SetWideStringList` function. +Add :c:func:`PyConfig_SetWideStringList` function. .. From 3c1bcae0753736d79e6c994d1c20144cad430008 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 28 Jul 2023 23:17:20 -0700 Subject: [PATCH 091/632] [3.11] gh-101100: Sphinx warnings: pick the low hanging fruits (GH-107386) (GH-107418) (cherry picked from commit f2d07d3289947d10b065b2bb7670c8fb6b6582f2) Co-authored-by: Serhiy Storchaka --- Doc/c-api/bool.rst | 6 +++++ Doc/faq/extending.rst | 4 +-- Doc/howto/curses.rst | 2 +- Doc/install/index.rst | 2 +- Doc/library/asyncio-extending.rst | 2 +- Doc/library/asyncio-future.rst | 2 +- Doc/library/bz2.rst | 3 ++- Doc/library/code.rst | 4 +-- Doc/library/concurrent.rst | 4 +-- Doc/library/curses.rst | 2 ++ Doc/library/email.charset.rst | 2 +- Doc/library/email.encoders.rst | 2 +- Doc/library/email.generator.rst | 4 +-- Doc/library/email.message.rst | 8 +++--- Doc/library/email.parser.rst | 4 +-- Doc/library/filecmp.rst | 2 +- Doc/library/fileinput.rst | 2 +- Doc/library/graphlib.rst | 4 +-- Doc/library/gzip.rst | 4 +-- Doc/library/hashlib.rst | 6 ++--- Doc/library/importlib.resources.abc.rst | 6 ++--- Doc/library/json.rst | 6 ++--- Doc/library/logging.rst | 30 ++++++++++----------- Doc/library/lzma.rst | 3 ++- Doc/library/msvcrt.rst | 2 +- Doc/library/netrc.rst | 16 ++++++++--- Doc/library/operator.rst | 6 ++--- Doc/library/poplib.rst | 2 +- Doc/library/pprint.rst | 2 +- Doc/library/pty.rst | 2 +- Doc/library/sched.rst | 2 +- Doc/library/selectors.rst | 8 +++--- Doc/library/shutil.rst | 2 +- Doc/library/stat.rst | 8 +++--- Doc/library/stdtypes.rst | 2 +- Doc/library/sysconfig.rst | 2 +- Doc/library/textwrap.rst | 4 +-- Doc/library/urllib.error.rst | 6 +++-- Doc/library/winreg.rst | 8 +++--- Doc/library/winsound.rst | 4 +-- Doc/library/xml.rst | 8 +++--- Doc/library/xml.sax.handler.rst | 2 +- Doc/library/xml.sax.utils.rst | 2 +- Doc/library/xmlrpc.rst | 4 +-- Doc/reference/datamodel.rst | 8 +++--- Doc/reference/expressions.rst | 2 +- Doc/reference/lexical_analysis.rst | 2 +- Doc/tools/.nitignore | 36 ------------------------- Doc/tutorial/errors.rst | 4 +-- Doc/tutorial/interactive.rst | 2 +- Doc/whatsnew/3.0.rst | 2 +- Misc/NEWS.d/3.11.0a6.rst | 2 +- 52 files changed, 125 insertions(+), 139 deletions(-) diff --git a/Doc/c-api/bool.rst b/Doc/c-api/bool.rst index c197d447e9618c..b15ee156c52c21 100644 --- a/Doc/c-api/bool.rst +++ b/Doc/c-api/bool.rst @@ -11,6 +11,12 @@ creation and deletion functions don't apply to booleans. The following macros are available, however. +.. c:var:: PyTypeObject PyBool_Type + + This instance of :c:type:`PyTypeObject` represents the Python boolean type; it + is the same object as :class:`bool` in the Python layer. + + .. c:function:: int PyBool_Check(PyObject *o) Return true if *o* is of type :c:data:`PyBool_Type`. This function always diff --git a/Doc/faq/extending.rst b/Doc/faq/extending.rst index bc3080f60ee237..2a8b976925d042 100644 --- a/Doc/faq/extending.rst +++ b/Doc/faq/extending.rst @@ -81,13 +81,13 @@ How do I extract C values from a Python object? That depends on the object's type. If it's a tuple, :c:func:`PyTuple_Size` returns its length and :c:func:`PyTuple_GetItem` returns the item at a specified -index. Lists have similar functions, :c:func:`PyListSize` and +index. Lists have similar functions, :c:func:`PyList_Size` and :c:func:`PyList_GetItem`. For bytes, :c:func:`PyBytes_Size` returns its length and :c:func:`PyBytes_AsStringAndSize` provides a pointer to its value and its length. Note that Python bytes objects may contain null bytes so C's -:c:func:`strlen` should not be used. +:c:func:`!strlen` should not be used. To test the type of an object, first make sure it isn't ``NULL``, and then use :c:func:`PyBytes_Check`, :c:func:`PyTuple_Check`, :c:func:`PyList_Check`, etc. diff --git a/Doc/howto/curses.rst b/Doc/howto/curses.rst index a3068d86d85bc4..4828e2fa29bd24 100644 --- a/Doc/howto/curses.rst +++ b/Doc/howto/curses.rst @@ -527,7 +527,7 @@ If you're in doubt about the detailed behavior of the curses functions, consult the manual pages for your curses implementation, whether it's ncurses or a proprietary Unix vendor's. The manual pages will document any quirks, and provide complete lists of all the -functions, attributes, and :const:`ACS_\*` characters available to +functions, attributes, and :ref:`ACS_\* ` characters available to you. Because the curses API is so large, some functions aren't supported in diff --git a/Doc/install/index.rst b/Doc/install/index.rst index fc4377e0d2b823..283070d36badfc 100644 --- a/Doc/install/index.rst +++ b/Doc/install/index.rst @@ -774,7 +774,7 @@ Notes: (2) On Unix, if the :envvar:`HOME` environment variable is not defined, the user's - home directory will be determined with the :func:`getpwuid` function from the + home directory will be determined with the :func:`~pwd.getpwuid` function from the standard :mod:`pwd` module. This is done by the :func:`os.path.expanduser` function used by Distutils. diff --git a/Doc/library/asyncio-extending.rst b/Doc/library/asyncio-extending.rst index 8ffd356f2d1cc3..e7b293f484f8de 100644 --- a/Doc/library/asyncio-extending.rst +++ b/Doc/library/asyncio-extending.rst @@ -69,7 +69,7 @@ Task lifetime support ===================== A third party task implementation should call the following functions to keep a task -visible by :func:`asyncio.get_tasks` and :func:`asyncio.current_task`: +visible by :func:`asyncio.all_tasks` and :func:`asyncio.current_task`: .. function:: _register_task(task) diff --git a/Doc/library/asyncio-future.rst b/Doc/library/asyncio-future.rst index 70cec9b2f90248..893ae5518f757d 100644 --- a/Doc/library/asyncio-future.rst +++ b/Doc/library/asyncio-future.rst @@ -276,4 +276,4 @@ the Future has a result:: :func:`concurrent.futures.as_completed` functions. - :meth:`asyncio.Future.cancel` accepts an optional ``msg`` argument, - but :func:`concurrent.futures.cancel` does not. + but :meth:`concurrent.futures.Future.cancel` does not. diff --git a/Doc/library/bz2.rst b/Doc/library/bz2.rst index ae5a1598f84b44..d03f2c4f0ca97a 100644 --- a/Doc/library/bz2.rst +++ b/Doc/library/bz2.rst @@ -87,7 +87,8 @@ The :mod:`bz2` module contains: compressed streams. :class:`BZ2File` provides all of the members specified by the - :class:`io.BufferedIOBase`, except for :meth:`detach` and :meth:`truncate`. + :class:`io.BufferedIOBase`, except for :meth:`~io.BufferedIOBase.detach` + and :meth:`~io.IOBase.truncate`. Iteration and the :keyword:`with` statement are supported. :class:`BZ2File` also provides the following method: diff --git a/Doc/library/code.rst b/Doc/library/code.rst index 538e5afc7822aa..3d7f43c86a0557 100644 --- a/Doc/library/code.rst +++ b/Doc/library/code.rst @@ -163,12 +163,12 @@ interpreter objects as well as the following additions. Push a line of source text to the interpreter. The line should not have a trailing newline; it may have internal newlines. The line is appended to a - buffer and the interpreter's :meth:`runsource` method is called with the + buffer and the interpreter's :meth:`~InteractiveInterpreter.runsource` method is called with the concatenated contents of the buffer as source. If this indicates that the command was executed or invalid, the buffer is reset; otherwise, the command is incomplete, and the buffer is left as it was after the line was appended. The return value is ``True`` if more input is required, ``False`` if the line was - dealt with in some way (this is the same as :meth:`runsource`). + dealt with in some way (this is the same as :meth:`!runsource`). .. method:: InteractiveConsole.resetbuffer() diff --git a/Doc/library/concurrent.rst b/Doc/library/concurrent.rst index 2eba5365125805..8caea78bbb57e8 100644 --- a/Doc/library/concurrent.rst +++ b/Doc/library/concurrent.rst @@ -1,5 +1,5 @@ -The :mod:`concurrent` package -============================= +The :mod:`!concurrent` package +============================== Currently, there is only one module in this package: diff --git a/Doc/library/curses.rst b/Doc/library/curses.rst index e0bc462fb1c958..a17d528793eff9 100644 --- a/Doc/library/curses.rst +++ b/Doc/library/curses.rst @@ -1651,6 +1651,8 @@ keys); also, the following keypad mappings are standard: | :kbd:`Page Down` | KEY_NPAGE | +------------------+-----------+ +.. _curses-acs-codes: + The following table lists characters from the alternate character set. These are inherited from the VT100 terminal, and will generally be available on software emulations such as X terminals. When there is no graphic available, curses diff --git a/Doc/library/email.charset.rst b/Doc/library/email.charset.rst index 38fda23bd8237f..1b1cd17bc81ffe 100644 --- a/Doc/library/email.charset.rst +++ b/Doc/library/email.charset.rst @@ -150,7 +150,7 @@ Import this class from the :mod:`email.charset` module. .. method:: __str__() Returns *input_charset* as a string coerced to lower - case. :meth:`__repr__` is an alias for :meth:`__str__`. + case. :meth:`!__repr__` is an alias for :meth:`!__str__`. .. method:: __eq__(other) diff --git a/Doc/library/email.encoders.rst b/Doc/library/email.encoders.rst index 5d68b104f3a45c..3bd377e33f6c15 100644 --- a/Doc/library/email.encoders.rst +++ b/Doc/library/email.encoders.rst @@ -25,7 +25,7 @@ is especially true for :mimetype:`image/\*` and :mimetype:`text/\*` type message containing binary data. The :mod:`email` package provides some convenient encoders in its -:mod:`encoders` module. These encoders are actually used by the +:mod:`~email.encoders` module. These encoders are actually used by the :class:`~email.mime.audio.MIMEAudio` and :class:`~email.mime.image.MIMEImage` class constructors to provide default encodings. All encoder functions take exactly one argument, the message object to encode. They usually extract the diff --git a/Doc/library/email.generator.rst b/Doc/library/email.generator.rst index 34ad7b7f200af3..91d9d69a63d73f 100644 --- a/Doc/library/email.generator.rst +++ b/Doc/library/email.generator.rst @@ -274,9 +274,9 @@ in with information about the part. .. rubric:: Footnotes .. [#] This statement assumes that you use the appropriate setting for - ``unixfrom``, and that there are no :mod:`policy` settings calling for + ``unixfrom``, and that there are no :mod:`email.policy` settings calling for automatic adjustments (for example, - :attr:`~email.policy.Policy.refold_source` must be ``none``, which is + :attr:`~email.policy.EmailPolicy.refold_source` must be ``none``, which is *not* the default). It is also not 100% true, since if the message does not conform to the RFC standards occasionally information about the exact original text is lost during parsing error recovery. It is a goal diff --git a/Doc/library/email.message.rst b/Doc/library/email.message.rst index 5e0509f4181199..225f498781fa86 100644 --- a/Doc/library/email.message.rst +++ b/Doc/library/email.message.rst @@ -67,7 +67,7 @@ message objects. with the base :class:`~email.message.Message` class *maxheaderlen* is accepted, but defaults to ``None``, which means that by default the line length is controlled by the - :attr:`~email.policy.EmailPolicy.max_line_length` of the policy. The + :attr:`~email.policy.Policy.max_line_length` of the policy. The *policy* argument may be used to override the default policy obtained from the message instance. This can be used to control some of the formatting produced by the method, since the specified *policy* will be @@ -213,7 +213,7 @@ message objects. del msg['subject'] msg['subject'] = 'Python roolz!' - If the :mod:`policy` defines certain headers to be unique (as the standard + If the :mod:`policy ` defines certain headers to be unique (as the standard policies do), this method may raise a :exc:`ValueError` when an attempt is made to assign a value to such a header when one already exists. This behavior is intentional for consistency's sake, but do not depend on it @@ -378,7 +378,7 @@ message objects. deprecated. Note that existing parameter values of headers may be accessed through - the :attr:`~email.headerregistry.BaseHeader.params` attribute of the + the :attr:`~email.headerregistry.ParameterizedMIMEHeader.params` attribute of the header value (for example, ``msg['Content-Type'].params['charset']``). .. versionchanged:: 3.4 ``replace`` keyword was added. @@ -691,7 +691,7 @@ message objects. .. method:: clear_content() - Remove the payload and all of the :exc:`Content-` headers, leaving + Remove the payload and all of the :mailheader:`!Content-` headers, leaving all other headers intact and in their original order. diff --git a/Doc/library/email.parser.rst b/Doc/library/email.parser.rst index d9a61616bbbdfb..dda0466a6afa7d 100644 --- a/Doc/library/email.parser.rst +++ b/Doc/library/email.parser.rst @@ -39,9 +39,9 @@ returns the root object when you close the parser. Note that the parser can be extended in limited ways, and of course you can implement your own parser completely from scratch. All of the logic that connects the :mod:`email` package's bundled parser and the -:class:`~email.message.EmailMessage` class is embodied in the :mod:`policy` +:class:`~email.message.EmailMessage` class is embodied in the :class:`~email.policy.Policy` class, so a custom parser can create message object trees any way it finds -necessary by implementing custom versions of the appropriate :mod:`policy` +necessary by implementing custom versions of the appropriate :class:`!Policy` methods. diff --git a/Doc/library/filecmp.rst b/Doc/library/filecmp.rst index 0efb4897a1eb86..dfe4b7c59fd578 100644 --- a/Doc/library/filecmp.rst +++ b/Doc/library/filecmp.rst @@ -100,7 +100,7 @@ The :class:`dircmp` class used to get various bits of information about the directory trees being compared. - Note that via :meth:`__getattr__` hooks, all attributes are computed lazily, + Note that via :meth:`~object.__getattr__` hooks, all attributes are computed lazily, so there is no speed penalty if only those attributes which are lightweight to compute are used. diff --git a/Doc/library/fileinput.rst b/Doc/library/fileinput.rst index 4bc868759f2025..f93e9a58791eeb 100644 --- a/Doc/library/fileinput.rst +++ b/Doc/library/fileinput.rst @@ -177,7 +177,7 @@ available for subclassing as well: The keyword-only parameter *encoding* and *errors* are added. .. versionchanged:: 3.11 - The ``'rU'`` and ``'U'`` modes and the :meth:`__getitem__` method have + The ``'rU'`` and ``'U'`` modes and the :meth:`!__getitem__` method have been removed. diff --git a/Doc/library/graphlib.rst b/Doc/library/graphlib.rst index fe7932e7a61cb5..fdd8f39ef4e1c4 100644 --- a/Doc/library/graphlib.rst +++ b/Doc/library/graphlib.rst @@ -115,7 +115,7 @@ :meth:`TopologicalSorter.done` is less than the number that have been returned by :meth:`TopologicalSorter.get_ready`. - The :meth:`~TopologicalSorter.__bool__` method of this class defers to + The :meth:`~object.__bool__` method of this class defers to this function, so instead of:: if ts.is_active(): @@ -204,7 +204,7 @@ The :mod:`graphlib` module defines the following exception classes: in the working graph. If multiple cycles exist, only one undefined choice among them will be reported and included in the exception. - The detected cycle can be accessed via the second element in the :attr:`~CycleError.args` + The detected cycle can be accessed via the second element in the :attr:`~BaseException.args` attribute of the exception instance and consists in a list of nodes, such that each node is, in the graph, an immediate predecessor of the next node in the list. In the reported list, the first and the last node will be the same, to make it clear that it is cyclic. diff --git a/Doc/library/gzip.rst b/Doc/library/gzip.rst index 2f26295d057f86..c28cfdc50e3989 100644 --- a/Doc/library/gzip.rst +++ b/Doc/library/gzip.rst @@ -70,7 +70,7 @@ The module defines the following items: .. class:: GzipFile(filename=None, mode=None, compresslevel=9, fileobj=None, mtime=None) Constructor for the :class:`GzipFile` class, which simulates most of the - methods of a :term:`file object`, with the exception of the :meth:`truncate` + methods of a :term:`file object`, with the exception of the :meth:`~io.IOBase.truncate` method. At least one of *fileobj* and *filename* must be given a non-trivial value. @@ -113,7 +113,7 @@ The module defines the following items: :class:`GzipFile` supports the :class:`io.BufferedIOBase` interface, including iteration and the :keyword:`with` statement. Only the - :meth:`truncate` method isn't implemented. + :meth:`~io.IOBase.truncate` method isn't implemented. :class:`GzipFile` also provides the following method and attribute: diff --git a/Doc/library/hashlib.rst b/Doc/library/hashlib.rst index 8d7b3413d19722..c77b728dbf3056 100644 --- a/Doc/library/hashlib.rst +++ b/Doc/library/hashlib.rst @@ -244,7 +244,7 @@ by the SHAKE algorithm. .. method:: shake.digest(length) - Return the digest of the data passed to the :meth:`update` method so far. + Return the digest of the data passed to the :meth:`~hash.update` method so far. This is a bytes object of size *length* which may contain bytes in the whole range from 0 to 255. @@ -513,9 +513,9 @@ Simple hashing To calculate hash of some data, you should first construct a hash object by calling the appropriate constructor function (:func:`blake2b` or -:func:`blake2s`), then update it with the data by calling :meth:`update` on the +:func:`blake2s`), then update it with the data by calling :meth:`~hash.update` on the object, and, finally, get the digest out of the object by calling -:meth:`digest` (or :meth:`hexdigest` for hex-encoded string). +:meth:`~hash.digest` (or :meth:`~hash.hexdigest` for hex-encoded string). >>> from hashlib import blake2b >>> h = blake2b() diff --git a/Doc/library/importlib.resources.abc.rst b/Doc/library/importlib.resources.abc.rst index b91e37510fe113..8851396c16b375 100644 --- a/Doc/library/importlib.resources.abc.rst +++ b/Doc/library/importlib.resources.abc.rst @@ -139,10 +139,10 @@ An abstract base class for resource readers capable of serving the :meth:`importlib.resources.files` interface. Subclasses - :class:`importlib.resources.abc.ResourceReader` and provides - concrete implementations of the :class:`importlib.resources.abc.ResourceReader`'s + :class:`ResourceReader` and provides + concrete implementations of the :class:`!ResourceReader`'s abstract methods. Therefore, any loader supplying - :class:`importlib.abc.TraversableResources` also supplies ResourceReader. + :class:`!TraversableResources` also supplies :class:`!ResourceReader`. Loaders that wish to support resource reading are expected to implement this interface. diff --git a/Doc/library/json.rst b/Doc/library/json.rst index 35a08995487c1b..6c3059381776c9 100644 --- a/Doc/library/json.rst +++ b/Doc/library/json.rst @@ -192,7 +192,7 @@ Basic Usage dictionaries will be sorted by key. To use a custom :class:`JSONEncoder` subclass (e.g. one that overrides the - :meth:`default` method to serialize additional types), specify it with the + :meth:`~JSONEncoder.default` method to serialize additional types), specify it with the *cls* kwarg; otherwise :class:`JSONEncoder` is used. .. versionchanged:: 3.6 @@ -422,7 +422,7 @@ Encoders and Decoders Added support for int- and float-derived Enum classes. To extend this to recognize other objects, subclass and implement a - :meth:`default` method with another method that returns a serializable object + :meth:`~JSONEncoder.default` method with another method that returns a serializable object for ``o`` if possible, otherwise it should call the superclass implementation (to raise :exc:`TypeError`). @@ -483,7 +483,7 @@ Encoders and Decoders :exc:`TypeError`). For example, to support arbitrary iterators, you could implement - :meth:`default` like this:: + :meth:`~JSONEncoder.default` like this:: def default(self, o): try: diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst index 6c47b9e206c4ff..954681efb44367 100644 --- a/Doc/library/logging.rst +++ b/Doc/library/logging.rst @@ -385,21 +385,21 @@ have specific values relative to the predefined levels. If you define a level with the same numeric value, it overwrites the predefined value; the predefined name is lost. -+--------------+---------------+ -| Level | Numeric value | -+==============+===============+ -| ``CRITICAL`` | 50 | -+--------------+---------------+ -| ``ERROR`` | 40 | -+--------------+---------------+ -| ``WARNING`` | 30 | -+--------------+---------------+ -| ``INFO`` | 20 | -+--------------+---------------+ -| ``DEBUG`` | 10 | -+--------------+---------------+ -| ``NOTSET`` | 0 | -+--------------+---------------+ ++-----------------------+---------------+ +| Level | Numeric value | ++=======================+===============+ +| .. py:data:: CRITICAL | 50 | ++-----------------------+---------------+ +| .. py:data:: ERROR | 40 | ++-----------------------+---------------+ +| .. py:data:: WARNING | 30 | ++-----------------------+---------------+ +| .. py:data:: INFO | 20 | ++-----------------------+---------------+ +| .. py:data:: DEBUG | 10 | ++-----------------------+---------------+ +| .. py:data:: NOTSET | 0 | ++-----------------------+---------------+ .. _handler: diff --git a/Doc/library/lzma.rst b/Doc/library/lzma.rst index 868d4dcfb6c996..434e7ac9061186 100644 --- a/Doc/library/lzma.rst +++ b/Doc/library/lzma.rst @@ -100,7 +100,8 @@ Reading and writing compressed files *filters* arguments have the same meanings as for :class:`LZMACompressor`. :class:`LZMAFile` supports all the members specified by - :class:`io.BufferedIOBase`, except for :meth:`detach` and :meth:`truncate`. + :class:`io.BufferedIOBase`, except for :meth:`~io.BufferedIOBase.detach` + and :meth:`~io.IOBase.truncate`. Iteration and the :keyword:`with` statement are supported. The following method is also provided: diff --git a/Doc/library/msvcrt.rst b/Doc/library/msvcrt.rst index 42fffee6a0f449..32693e3d007c05 100644 --- a/Doc/library/msvcrt.rst +++ b/Doc/library/msvcrt.rst @@ -38,7 +38,7 @@ File Operations Lock part of a file based on file descriptor *fd* from the C runtime. Raises :exc:`OSError` on failure. The locked region of the file extends from the current file position for *nbytes* bytes, and may continue beyond the end of the - file. *mode* must be one of the :const:`LK_\*` constants listed below. Multiple + file. *mode* must be one of the :const:`!LK_\*` constants listed below. Multiple regions in a file may be locked at the same time, but may not overlap. Adjacent regions are not merged; they must be unlocked individually. diff --git a/Doc/library/netrc.rst b/Doc/library/netrc.rst index 88265d9b9e9e93..c36e5cfecfc6a8 100644 --- a/Doc/library/netrc.rst +++ b/Doc/library/netrc.rst @@ -51,9 +51,19 @@ the Unix :program:`ftp` program and other FTP clients. Exception raised by the :class:`~netrc.netrc` class when syntactical errors are encountered in source text. Instances of this exception provide three - interesting attributes: :attr:`msg` is a textual explanation of the error, - :attr:`filename` is the name of the source file, and :attr:`lineno` gives the - line number on which the error was found. + interesting attributes: + + .. attribute:: msg + + Textual explanation of the error. + + .. attribute:: filename + + The name of the source file. + + .. attribute:: lineno + + The line number on which the error was found. .. _netrc-objects: diff --git a/Doc/library/operator.rst b/Doc/library/operator.rst index dab4de9eb6abb7..57c67bcf3aa12e 100644 --- a/Doc/library/operator.rst +++ b/Doc/library/operator.rst @@ -59,9 +59,9 @@ truth tests, identity tests, and boolean operations: __not__(obj) Return the outcome of :keyword:`not` *obj*. (Note that there is no - :meth:`__not__` method for object instances; only the interpreter core defines - this operation. The result is affected by the :meth:`__bool__` and - :meth:`__len__` methods.) + :meth:`!__not__` method for object instances; only the interpreter core defines + this operation. The result is affected by the :meth:`~object.__bool__` and + :meth:`~object.__len__` methods.) .. function:: truth(obj) diff --git a/Doc/library/poplib.rst b/Doc/library/poplib.rst index c9ada42447e8aa..5e0371592ecb96 100644 --- a/Doc/library/poplib.rst +++ b/Doc/library/poplib.rst @@ -156,7 +156,7 @@ A :class:`POP3` instance has the following methods: .. method:: POP3.pass_(password) Send password, response includes message count and mailbox size. Note: the - mailbox on the server is locked until :meth:`~poplib.quit` is called. + mailbox on the server is locked until :meth:`~POP3.quit` is called. .. method:: POP3.apop(user, secret) diff --git a/Doc/library/pprint.rst b/Doc/library/pprint.rst index 2a3bc92e1733ad..fa5153284a2aab 100644 --- a/Doc/library/pprint.rst +++ b/Doc/library/pprint.rst @@ -45,7 +45,7 @@ The :mod:`pprint` module defines one class: several keyword parameters. *stream* (default ``sys.stdout``) is a :term:`file-like object` to - which the output will be written by calling its :meth:`write` method. + which the output will be written by calling its :meth:`!write` method. If both *stream* and ``sys.stdout`` are ``None``, then :meth:`~PrettyPrinter.pprint` silently returns. diff --git a/Doc/library/pty.rst b/Doc/library/pty.rst index 7f4da41e93802d..ad4981c97119fa 100644 --- a/Doc/library/pty.rst +++ b/Doc/library/pty.rst @@ -71,7 +71,7 @@ The :mod:`pty` module defines the following functions: Return the exit status value from :func:`os.waitpid` on the child process. - :func:`waitstatus_to_exitcode` can be used to convert the exit status into + :func:`os.waitstatus_to_exitcode` can be used to convert the exit status into an exit code. .. audit-event:: pty.spawn argv pty.spawn diff --git a/Doc/library/sched.rst b/Doc/library/sched.rst index 04215d31ba10ca..01bac5afd0b9b3 100644 --- a/Doc/library/sched.rst +++ b/Doc/library/sched.rst @@ -115,7 +115,7 @@ Scheduler Objects .. method:: scheduler.run(blocking=True) - Run all scheduled events. This method will wait (using the :func:`delayfunc` + Run all scheduled events. This method will wait (using the *delayfunc* function passed to the constructor) for the next event, then execute it and so on until there are no more scheduled events. diff --git a/Doc/library/selectors.rst b/Doc/library/selectors.rst index 0deb15cf4c5037..dd50bac37e49b8 100644 --- a/Doc/library/selectors.rst +++ b/Doc/library/selectors.rst @@ -60,9 +60,9 @@ constants below: +-----------------------+-----------------------------------------------+ | Constant | Meaning | +=======================+===============================================+ - | :const:`EVENT_READ` | Available for read | + | .. data:: EVENT_READ | Available for read | +-----------------------+-----------------------------------------------+ - | :const:`EVENT_WRITE` | Available for write | + | .. data:: EVENT_WRITE | Available for write | +-----------------------+-----------------------------------------------+ @@ -132,8 +132,8 @@ constants below: Change a registered file object's monitored events or attached data. - This is equivalent to :meth:`BaseSelector.unregister(fileobj)` followed - by :meth:`BaseSelector.register(fileobj, events, data)`, except that it + This is equivalent to ``BaseSelector.unregister(fileobj)`` followed + by ``BaseSelector.register(fileobj, events, data)``, except that it can be implemented more efficiently. This returns a new :class:`SelectorKey` instance, or raises a diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst index dcf421f162b15a..4ba2856fce2654 100644 --- a/Doc/library/shutil.rst +++ b/Doc/library/shutil.rst @@ -363,7 +363,7 @@ Directory and files operations If *copy_function* is given, it must be a callable that takes two arguments *src* and *dst*, and will be used to copy *src* to *dst* if :func:`os.rename` cannot be used. If the source is a directory, - :func:`copytree` is called, passing it the :func:`copy_function`. The + :func:`copytree` is called, passing it the *copy_function*. The default *copy_function* is :func:`copy2`. Using :func:`~shutil.copy` as the *copy_function* allows the move to succeed when it is not possible to also copy the metadata, at the expense of not copying any of the metadata. diff --git a/Doc/library/stat.rst b/Doc/library/stat.rst index 083dc5e3bcfd68..77538514598a50 100644 --- a/Doc/library/stat.rst +++ b/Doc/library/stat.rst @@ -13,8 +13,8 @@ The :mod:`stat` module defines constants and functions for interpreting the results of :func:`os.stat`, :func:`os.fstat` and :func:`os.lstat` (if they -exist). For complete details about the :c:func:`stat`, :c:func:`fstat` and -:c:func:`lstat` calls, consult the documentation for your system. +exist). For complete details about the :c:func:`stat`, :c:func:`!fstat` and +:c:func:`!lstat` calls, consult the documentation for your system. .. versionchanged:: 3.4 The stat module is backed by a C implementation. @@ -89,9 +89,9 @@ mode: .. function:: S_IFMT(mode) Return the portion of the file's mode that describes the file type (used by the - :func:`S_IS\*` functions above). + :func:`!S_IS\*` functions above). -Normally, you would use the :func:`os.path.is\*` functions for testing the type +Normally, you would use the :func:`!os.path.is\*` functions for testing the type of a file; the functions here are useful when you are doing multiple tests of the same file and wish to avoid the overhead of the :c:func:`stat` system call for each test. These are also useful when checking for information about a file diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 4437bc496ab9b9..6158911fb28a7a 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -44,7 +44,7 @@ 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:`__bool__` method that returns ``False`` or a :meth:`__len__` method that +:meth:`~object.__bool__` method that returns ``False`` or a :meth:`__len__` method that returns zero, when called with the object. [1]_ Here are most of the built-in objects considered false: diff --git a/Doc/library/sysconfig.rst b/Doc/library/sysconfig.rst index 0049b663973f4a..dfe6934e6da8cc 100644 --- a/Doc/library/sysconfig.rst +++ b/Doc/library/sysconfig.rst @@ -69,7 +69,7 @@ Python uses an installation scheme that differs depending on the platform and on the installation options. These schemes are stored in :mod:`sysconfig` under unique identifiers based on the value returned by :const:`os.name`. -Every new component that is installed using :mod:`distutils` or a +Every new component that is installed using :mod:`!distutils` or a Distutils-based system will follow the same scheme to copy its file in the right places. diff --git a/Doc/library/textwrap.rst b/Doc/library/textwrap.rst index 1a9d5f98f78a7e..a150eefbf932ef 100644 --- a/Doc/library/textwrap.rst +++ b/Doc/library/textwrap.rst @@ -60,7 +60,7 @@ functions should be good enough; otherwise, you should use an instance of First the whitespace in *text* is collapsed (all whitespace is replaced by single spaces). If the result fits in the *width*, it is returned. Otherwise, enough words are dropped from the end so that the remaining words - plus the :attr:`placeholder` fit within :attr:`width`:: + plus the :attr:`.placeholder` fit within :attr:`.width`:: >>> textwrap.shorten("Hello world!", width=12) 'Hello world!' @@ -173,7 +173,7 @@ hyphenated words; only then will long words be broken if necessary, unless .. attribute:: expand_tabs (default: ``True``) If true, then all tab characters in *text* will be - expanded to spaces using the :meth:`expandtabs` method of *text*. + expanded to spaces using the :meth:`~str.expandtabs` method of *text*. .. attribute:: tabsize diff --git a/Doc/library/urllib.error.rst b/Doc/library/urllib.error.rst index f7d47ed76aca18..8772e8600795eb 100644 --- a/Doc/library/urllib.error.rst +++ b/Doc/library/urllib.error.rst @@ -61,6 +61,8 @@ The following exceptions are raised by :mod:`urllib.error` as appropriate: This exception is raised when the :func:`~urllib.request.urlretrieve` function detects that the amount of the downloaded data is less than the expected amount (given by - the *Content-Length* header). The :attr:`content` attribute stores the - downloaded (and supposedly truncated) data. + the *Content-Length* header). + .. attribute:: content + + The downloaded (and supposedly truncated) data. diff --git a/Doc/library/winreg.rst b/Doc/library/winreg.rst index 4ab671817710dd..06bd4d87eb03c6 100644 --- a/Doc/library/winreg.rst +++ b/Doc/library/winreg.rst @@ -288,7 +288,7 @@ This module offers the following functions: table (FAT) file system, the filename may not have an extension. A call to :func:`LoadKey` fails if the calling process does not have the - :const:`SE_RESTORE_PRIVILEGE` privilege. Note that privileges are different + :c:data:`!SE_RESTORE_PRIVILEGE` privilege. Note that privileges are different from permissions -- see the `RegLoadKey documentation `__ for more details. @@ -414,7 +414,7 @@ This module offers the following functions: If *key* represents a key on a remote computer, the path described by *file_name* is relative to the remote computer. The caller of this method must - possess the :const:`SeBackupPrivilege` security privilege. Note that + possess the **SeBackupPrivilege** security privilege. Note that privileges are different than permissions -- see the `Conflicts Between User Rights and Permissions documentation `__ @@ -536,7 +536,7 @@ This module offers the following functions: Constants ------------------ -The following constants are defined for use in many :mod:`_winreg` functions. +The following constants are defined for use in many :mod:`winreg` functions. .. _hkey-constants: @@ -745,7 +745,7 @@ All registry functions in this module return one of these objects. All registry functions in this module which accept a handle object also accept an integer, however, use of the handle object is encouraged. -Handle objects provide semantics for :meth:`__bool__` -- thus :: +Handle objects provide semantics for :meth:`~object.__bool__` -- thus :: if handle: print("Yes") diff --git a/Doc/library/winsound.rst b/Doc/library/winsound.rst index 372f792a0f938e..370c5216652ba7 100644 --- a/Doc/library/winsound.rst +++ b/Doc/library/winsound.rst @@ -24,7 +24,7 @@ provided by Windows platforms. It includes functions and several constants. .. function:: PlaySound(sound, flags) - Call the underlying :c:func:`PlaySound` function from the Platform API. The + Call the underlying :c:func:`!PlaySound` function from the Platform API. The *sound* parameter may be a filename, a system sound alias, audio data as a :term:`bytes-like object`, or ``None``. Its interpretation depends on the value of *flags*, which can be a bitwise ORed @@ -35,7 +35,7 @@ provided by Windows platforms. It includes functions and several constants. .. function:: MessageBeep(type=MB_OK) - Call the underlying :c:func:`MessageBeep` function from the Platform API. This + Call the underlying :c:func:`!MessageBeep` function from the Platform API. This plays a sound as specified in the registry. The *type* argument specifies which sound to play; possible values are ``-1``, ``MB_ICONASTERISK``, ``MB_ICONEXCLAMATION``, ``MB_ICONHAND``, ``MB_ICONQUESTION``, and ``MB_OK``, all diff --git a/Doc/library/xml.rst b/Doc/library/xml.rst index cf30de67719b93..1e49b6568dfc28 100644 --- a/Doc/library/xml.rst +++ b/Doc/library/xml.rst @@ -75,10 +75,10 @@ decompression bomb Safe Safe Safe potential reliance on system-provided libraries. Check :const:`pyexpat.EXPAT_VERSION`. 2. :mod:`xml.etree.ElementTree` doesn't expand external entities and raises a - :exc:`ParserError` when an entity occurs. + :exc:`~xml.etree.ElementTree.ParseError` when an entity occurs. 3. :mod:`xml.dom.minidom` doesn't expand external entities and simply returns the unexpanded entity verbatim. -4. :mod:`xmlrpclib` doesn't expand external entities and omits them. +4. :mod:`xmlrpc.client` doesn't expand external entities and omits them. 5. Since Python 3.7.1, external general entities are no longer processed by default. @@ -119,8 +119,8 @@ all known attack vectors with examples and references. .. _defusedxml-package: -The :mod:`defusedxml` Package ------------------------------------------------------- +The :mod:`!defusedxml` Package +------------------------------ `defusedxml`_ is a pure Python package with modified subclasses of all stdlib XML parsers that prevent any potentially malicious operation. Use of this diff --git a/Doc/library/xml.sax.handler.rst b/Doc/library/xml.sax.handler.rst index 719ce5ab1bcf65..e2f28e3244cb09 100644 --- a/Doc/library/xml.sax.handler.rst +++ b/Doc/library/xml.sax.handler.rst @@ -393,7 +393,7 @@ implements this interface, then register the object with your :class:`~xml.sax.xmlreader.XMLReader`, the parser will call the methods in your object to report all warnings and errors. There are three levels of errors available: warnings, (possibly) recoverable errors, -and unrecoverable errors. All methods take a :exc:`SAXParseException` as the +and unrecoverable errors. All methods take a :exc:`~xml.sax.SAXParseException` as the only parameter. Errors and warnings may be converted to an exception by raising the passed-in exception object. diff --git a/Doc/library/xml.sax.utils.rst b/Doc/library/xml.sax.utils.rst index e46fefdf997510..8c1be43d1f2776 100644 --- a/Doc/library/xml.sax.utils.rst +++ b/Doc/library/xml.sax.utils.rst @@ -87,5 +87,5 @@ or as base classes. reading. The input source can be given as a string, a file-like object, or an :class:`~xml.sax.xmlreader.InputSource` object; parsers will use this function to implement the polymorphic *source* argument to their - :meth:`parse` method. + :meth:`~xml.sax.xmlreader.XMLReader.parse` method. diff --git a/Doc/library/xmlrpc.rst b/Doc/library/xmlrpc.rst index ae68157b0f63c1..5f0a2cf68d01f9 100644 --- a/Doc/library/xmlrpc.rst +++ b/Doc/library/xmlrpc.rst @@ -1,5 +1,5 @@ -:mod:`xmlrpc` --- XMLRPC server and client modules -================================================== +:mod:`!xmlrpc` --- XMLRPC server and client modules +=================================================== XML-RPC is a Remote Procedure Call method that uses XML passed via HTTP as a transport. With it, a client can call methods with parameters on a remote diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index d2a39c57adfc54..65d1ca1b8ddcbe 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -1581,9 +1581,9 @@ Basic customization Called to implement truth value testing and the built-in operation ``bool()``; should return ``False`` or ``True``. When this method is not - defined, :meth:`__len__` is called, if it is defined, and the object is + defined, :meth:`~object.__len__` is called, if it is defined, and the object is considered true if its result is nonzero. If a class defines neither - :meth:`__len__` nor :meth:`__bool__`, all its instances are considered + :meth:`!__len__` nor :meth:`!__bool__`, all its instances are considered true. @@ -2477,7 +2477,7 @@ through the object's keys; for sequences, it should iterate through the values. Called to implement the built-in function :func:`len`. Should return the length of the object, an integer ``>=`` 0. Also, an object that doesn't define a - :meth:`__bool__` method and whose :meth:`__len__` method returns zero is + :meth:`~object.__bool__` method and whose :meth:`!__len__` method returns zero is considered to be false in a Boolean context. .. impl-detail:: @@ -2486,7 +2486,7 @@ through the object's keys; for sequences, it should iterate through the values. If the length is larger than :data:`!sys.maxsize` some features (such as :func:`len`) may raise :exc:`OverflowError`. To prevent raising :exc:`!OverflowError` by truth value testing, an object must define a - :meth:`__bool__` method. + :meth:`~object.__bool__` method. .. method:: object.__length_hint__(self) diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index 2a16025ad62124..8320cd0ae75193 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -1707,7 +1707,7 @@ control flow statements, the following values are interpreted as false: ``False``, ``None``, numeric zero of all types, and empty strings and containers (including strings, tuples, lists, dictionaries, sets and frozensets). All other values are interpreted as true. User-defined objects can customize their -truth value by providing a :meth:`__bool__` method. +truth value by providing a :meth:`~object.__bool__` method. .. index:: pair: operator; not diff --git a/Doc/reference/lexical_analysis.rst b/Doc/reference/lexical_analysis.rst index 44726de3e0373d..8d07e071f71be8 100644 --- a/Doc/reference/lexical_analysis.rst +++ b/Doc/reference/lexical_analysis.rst @@ -757,7 +757,7 @@ is converted before formatting. Conversion ``'!s'`` calls :func:`str` on the result, ``'!r'`` calls :func:`repr`, and ``'!a'`` calls :func:`ascii`. The result is then formatted using the :func:`format` protocol. The -format specifier is passed to the :meth:`__format__` method of the +format specifier is passed to the :meth:`~object.__format__` method of the expression or conversion result. An empty string is passed when the format specifier is omitted. The formatted result is then included in the final value of the whole string. diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 22eb47856f5714..df7dcd607489c8 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -2,7 +2,6 @@ # as tested on the CI via check-warnings.py in reusable-docs.yml. # Keep lines sorted lexicographically to help avoid merge conflicts. -Doc/c-api/bool.rst Doc/c-api/buffer.rst Doc/c-api/datetime.rst Doc/c-api/descriptor.rst @@ -27,26 +26,22 @@ Doc/c-api/unicode.rst Doc/extending/extending.rst Doc/extending/newtypes.rst Doc/faq/design.rst -Doc/faq/extending.rst Doc/faq/gui.rst Doc/faq/library.rst Doc/faq/programming.rst Doc/glossary.rst -Doc/howto/curses.rst Doc/howto/descriptor.rst Doc/howto/enum.rst Doc/howto/isolating-extensions.rst Doc/howto/logging-cookbook.rst Doc/howto/logging.rst Doc/howto/urllib2.rst -Doc/install/index.rst Doc/library/__future__.rst Doc/library/abc.rst Doc/library/ast.rst Doc/library/asyncio-dev.rst Doc/library/asyncio-eventloop.rst Doc/library/asyncio-extending.rst -Doc/library/asyncio-future.rst Doc/library/asyncio-policy.rst Doc/library/asyncio-stream.rst Doc/library/asyncio-subprocess.rst @@ -56,12 +51,10 @@ Doc/library/bisect.rst Doc/library/bz2.rst Doc/library/calendar.rst Doc/library/cmd.rst -Doc/library/code.rst Doc/library/codecs.rst Doc/library/collections.abc.rst Doc/library/collections.rst Doc/library/concurrent.futures.rst -Doc/library/concurrent.rst Doc/library/configparser.rst Doc/library/contextlib.rst Doc/library/copy.rst @@ -76,11 +69,8 @@ Doc/library/dis.rst Doc/library/doctest.rst Doc/library/email.charset.rst Doc/library/email.compat32-message.rst -Doc/library/email.encoders.rst Doc/library/email.errors.rst -Doc/library/email.generator.rst Doc/library/email.headerregistry.rst -Doc/library/email.message.rst Doc/library/email.mime.rst Doc/library/email.parser.rst Doc/library/email.policy.rst @@ -88,26 +78,20 @@ Doc/library/enum.rst Doc/library/exceptions.rst Doc/library/faulthandler.rst Doc/library/fcntl.rst -Doc/library/filecmp.rst -Doc/library/fileinput.rst Doc/library/ftplib.rst Doc/library/functions.rst Doc/library/functools.rst Doc/library/getpass.rst Doc/library/gettext.rst -Doc/library/graphlib.rst Doc/library/gzip.rst -Doc/library/hashlib.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.abc.rst Doc/library/importlib.resources.rst Doc/library/importlib.rst Doc/library/inspect.rst Doc/library/io.rst -Doc/library/json.rst Doc/library/locale.rst Doc/library/logging.config.rst Doc/library/logging.handlers.rst @@ -115,12 +99,9 @@ Doc/library/logging.rst Doc/library/lzma.rst Doc/library/mailbox.rst Doc/library/mmap.rst -Doc/library/msvcrt.rst Doc/library/multiprocessing.rst Doc/library/multiprocessing.shared_memory.rst -Doc/library/netrc.rst Doc/library/numbers.rst -Doc/library/operator.rst Doc/library/optparse.rst Doc/library/os.path.rst Doc/library/os.rst @@ -128,10 +109,7 @@ Doc/library/pickle.rst Doc/library/pickletools.rst Doc/library/platform.rst Doc/library/plistlib.rst -Doc/library/poplib.rst -Doc/library/pprint.rst Doc/library/profile.rst -Doc/library/pty.rst Doc/library/pyclbr.rst Doc/library/pydoc.rst Doc/library/pyexpat.rst @@ -140,30 +118,25 @@ Doc/library/readline.rst Doc/library/reprlib.rst Doc/library/resource.rst Doc/library/rlcompleter.rst -Doc/library/sched.rst Doc/library/select.rst Doc/library/selectors.rst Doc/library/shelve.rst -Doc/library/shutil.rst Doc/library/signal.rst Doc/library/site.rst Doc/library/smtplib.rst Doc/library/socket.rst Doc/library/socketserver.rst Doc/library/ssl.rst -Doc/library/stat.rst Doc/library/stdtypes.rst Doc/library/string.rst Doc/library/subprocess.rst Doc/library/sys.rst Doc/library/sys_path_init.rst -Doc/library/sysconfig.rst Doc/library/syslog.rst Doc/library/tarfile.rst Doc/library/tempfile.rst Doc/library/termios.rst Doc/library/test.rst -Doc/library/textwrap.rst Doc/library/threading.rst Doc/library/time.rst Doc/library/tkinter.rst @@ -175,13 +148,10 @@ Doc/library/turtle.rst Doc/library/unittest.mock-examples.rst Doc/library/unittest.mock.rst Doc/library/unittest.rst -Doc/library/urllib.error.rst Doc/library/urllib.parse.rst Doc/library/urllib.request.rst Doc/library/uuid.rst Doc/library/weakref.rst -Doc/library/winreg.rst -Doc/library/winsound.rst Doc/library/wsgiref.rst Doc/library/xml.dom.minidom.rst Doc/library/xml.dom.pulldom.rst @@ -191,9 +161,7 @@ Doc/library/xml.rst Doc/library/xml.sax.handler.rst Doc/library/xml.sax.reader.rst Doc/library/xml.sax.rst -Doc/library/xml.sax.utils.rst Doc/library/xmlrpc.client.rst -Doc/library/xmlrpc.rst Doc/library/xmlrpc.server.rst Doc/library/zlib.rst Doc/license.rst @@ -201,18 +169,14 @@ Doc/reference/compound_stmts.rst Doc/reference/datamodel.rst Doc/reference/expressions.rst Doc/reference/import.rst -Doc/reference/lexical_analysis.rst Doc/reference/simple_stmts.rst Doc/tutorial/appendix.rst Doc/tutorial/classes.rst Doc/tutorial/controlflow.rst Doc/tutorial/datastructures.rst -Doc/tutorial/errors.rst Doc/tutorial/inputoutput.rst -Doc/tutorial/interactive.rst Doc/tutorial/introduction.rst Doc/tutorial/modules.rst -Doc/tutorial/stdlib2.rst Doc/using/cmdline.rst Doc/using/configure.rst Doc/using/windows.rst diff --git a/Doc/tutorial/errors.rst b/Doc/tutorial/errors.rst index 8a207c385c6ab7..1ec59767e9ce12 100644 --- a/Doc/tutorial/errors.rst +++ b/Doc/tutorial/errors.rst @@ -154,7 +154,7 @@ exception type. The *except clause* may specify a variable after the exception name. The variable is bound to the exception instance which typically has an ``args`` attribute that stores the arguments. For convenience, builtin exception -types define :meth:`__str__` to print all the arguments without explicitly +types define :meth:`~object.__str__` to print all the arguments without explicitly accessing ``.args``. :: >>> try: @@ -174,7 +174,7 @@ accessing ``.args``. :: x = spam y = eggs -The exception's :meth:`__str__` output is printed as the last part ('detail') +The exception's :meth:`~object.__str__` output is printed as the last part ('detail') of the message for unhandled exceptions. :exc:`BaseException` is the common base class of all exceptions. One of its diff --git a/Doc/tutorial/interactive.rst b/Doc/tutorial/interactive.rst index c0eb1feec4eb4d..0d3896a4832b59 100644 --- a/Doc/tutorial/interactive.rst +++ b/Doc/tutorial/interactive.rst @@ -23,7 +23,7 @@ Python statement names, the current local variables, and the available module names. For dotted expressions such as ``string.a``, it will evaluate the expression up to the final ``'.'`` and then suggest completions from the attributes of the resulting object. Note that this may execute -application-defined code if an object with a :meth:`__getattr__` method +application-defined code if an object with a :meth:`~object.__getattr__` method is part of the expression. The default configuration also saves your history into a file named :file:`.python_history` in your user directory. The history will be available again during the next interactive interpreter diff --git a/Doc/whatsnew/3.0.rst b/Doc/whatsnew/3.0.rst index 63b24748d8aab6..6f2a155abe10b6 100644 --- a/Doc/whatsnew/3.0.rst +++ b/Doc/whatsnew/3.0.rst @@ -789,7 +789,7 @@ Operators And Special Methods :attr:`__doc__`, :attr:`__globals__`, :attr:`~definition.__name__`, respectively. -* :meth:`__nonzero__` is now :meth:`__bool__`. +* :meth:`!__nonzero__` is now :meth:`~object.__bool__`. Builtins -------- diff --git a/Misc/NEWS.d/3.11.0a6.rst b/Misc/NEWS.d/3.11.0a6.rst index 8621edcfb04bb3..fcec71c6f59da2 100644 --- a/Misc/NEWS.d/3.11.0a6.rst +++ b/Misc/NEWS.d/3.11.0a6.rst @@ -352,7 +352,7 @@ rather than ``JUMP_FORWARD`` with an argument of ``(2**32)+offset``. .. nonce: 3Z_qxd .. section: Core and Builtins -Correct the docstring for the :meth:`__bool__` method. Patch by Jelle +Correct the docstring for the :meth:`~object.__bool__` method. Patch by Jelle Zijlstra. .. From 357708eee0a665240905f77a4a6832b642be6d52 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Sat, 29 Jul 2023 18:21:45 +0100 Subject: [PATCH 092/632] [3.11] gh-107422: Remove outdated `TypedDict` example from typing docs (#107436) (#107438) gh-107422: Remove outdated `TypedDict` example from typing docs (#107436) Co-authored-by: Rakesh Sabale <102187286+ghubrakesh@users.noreply.github.com> --- Doc/library/typing.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index d03f0af1bc6b61..a0766690bad287 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -2073,9 +2073,6 @@ types. class XZ(X, Z): pass # raises TypeError - T = TypeVar('T') - class XT(X, Generic[T]): pass # raises TypeError - A ``TypedDict`` can be generic: .. testcode:: From ddccdbfc403b3e45693cb307fc05ea15b0c642cb Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 29 Jul 2023 11:07:49 -0700 Subject: [PATCH 093/632] [3.11] gh-106634: Corrected minor asyncio doc issues (GH-106671) (#106711) gh-106634: Corrected minor asyncio doc issues (GH-106671) (cherry picked from commit 4b4a5b70aa8d47b1e2a0582b741c31b786da762a) Co-authored-by: Chris Brett --- Lib/asyncio/base_events.py | 2 +- Lib/asyncio/events.py | 2 +- Misc/ACKS | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index c5c7bd46ff8b14..d4c8238ba5217f 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -716,7 +716,7 @@ def call_later(self, delay, callback, *args, context=None): always relative to the current time. Each callback will be called exactly once. If two callbacks - are scheduled for exactly the same time, it undefined which + are scheduled for exactly the same time, it is undefined which will be called first. Any positional arguments after the callback will be passed to diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py index b1799320eaa08d..5fe4074faf3a09 100644 --- a/Lib/asyncio/events.py +++ b/Lib/asyncio/events.py @@ -613,7 +613,7 @@ class AbstractEventLoopPolicy: def get_event_loop(self): """Get the event loop for the current context. - Returns an event loop object implementing the BaseEventLoop interface, + Returns an event loop object implementing the AbstractEventLoop interface, or raises an exception in case no event loop has been set for the current context and the current policy does not specify to create one. diff --git a/Misc/ACKS b/Misc/ACKS index eba0ffba9a633f..5fe93d638082dc 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -224,6 +224,7 @@ Erik Bray Brian Brazil Demian Brecht Dave Brennan +Christopher Richard James Brett Tom Bridgman Anthony Briggs Keith Briggs From ff5dd9d9db0d036dbc65b7c5de49c6f3117a2355 Mon Sep 17 00:00:00 2001 From: da-woods Date: Sun, 30 Jul 2023 09:48:01 +0100 Subject: [PATCH 094/632] [3.11] Fix the documentation for PyCode_New add qualname parameter (GH-107186) (#107454) [3.11] Fix the documentation for PyCode_New add `qualname` parameter (GH-107186). (cherry picked from commit f2abeb590dae5918388f91b60b31f040d44218f0) --- Doc/c-api/code.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/c-api/code.rst b/Doc/c-api/code.rst index ee39f2aea85b09..33682d30dff337 100644 --- a/Doc/c-api/code.rst +++ b/Doc/c-api/code.rst @@ -33,7 +33,7 @@ bound into a function. Return the number of free variables in *co*. -.. c:function:: PyCodeObject* PyCode_New(int argcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, int firstlineno, PyObject *linetable, PyObject *exceptiontable) +.. c:function:: PyCodeObject* PyCode_New(int argcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, PyObject *qualname, int firstlineno, PyObject *linetable, PyObject *exceptiontable) Return a new code object. If you need a dummy code object to create a frame, use :c:func:`PyCode_NewEmpty` instead. Calling :c:func:`PyCode_New` directly @@ -43,9 +43,9 @@ bound into a function. execution or VM crashes. Use this function only with extreme care. .. versionchanged:: 3.11 - Added ``exceptiontable`` parameter. + Added ``qualname`` and ``exceptiontable`` parameters. -.. c:function:: PyCodeObject* PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, int firstlineno, PyObject *linetable, PyObject *exceptiontable) +.. c:function:: PyCodeObject* PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, PyObject *qualname, int firstlineno, PyObject *linetable, PyObject *exceptiontable) Similar to :c:func:`PyCode_New`, but with an extra "posonlyargcount" for positional-only arguments. The same caveats that apply to ``PyCode_New`` also apply to this function. @@ -53,7 +53,7 @@ bound into a function. .. versionadded:: 3.8 .. versionchanged:: 3.11 - Added ``exceptiontable`` parameter. + Added ``qualname`` and ``exceptiontable`` parameters. .. c:function:: PyCodeObject* PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) From 9690efc5552153cb7276b7d481827f26a4f3ae2b Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Mon, 31 Jul 2023 02:15:27 -0400 Subject: [PATCH 095/632] [3.11] Update macOS installer screens for 3.11.5. (GH-107477) --- Mac/BuildScript/resources/ReadMe.rtf | 4 ++-- Mac/BuildScript/resources/Welcome.rtf | 23 ----------------------- 2 files changed, 2 insertions(+), 25 deletions(-) diff --git a/Mac/BuildScript/resources/ReadMe.rtf b/Mac/BuildScript/resources/ReadMe.rtf index 2d6ca8f261898e..94e61ac5a4df71 100644 --- a/Mac/BuildScript/resources/ReadMe.rtf +++ b/Mac/BuildScript/resources/ReadMe.rtf @@ -1,4 +1,4 @@ -{\rtf1\ansi\ansicpg1252\cocoartf2639 +{\rtf1\ansi\ansicpg1252\cocoartf2709 \cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fswiss\fcharset0 Helvetica-Bold;\f2\fswiss\fcharset0 Helvetica-Oblique; \f3\fmodern\fcharset0 CourierNewPSMT;\f4\fmodern\fcharset0 Courier;} {\colortbl;\red255\green255\blue255;} @@ -13,7 +13,7 @@ \f1\b \cf0 \ul \ulc0 Certificate verification and OpenSSL\ \f0\b0 \ulnone \ -This package includes its own private copy of OpenSSL 1.1.1. The trust certificates in system and user keychains managed by the +This package includes its own private copy of OpenSSL 3.0. The trust certificates in system and user keychains managed by the \f2\i Keychain Access \f0\i0 application and the \f2\i security diff --git a/Mac/BuildScript/resources/Welcome.rtf b/Mac/BuildScript/resources/Welcome.rtf index 93500bc0e1e2eb..86f4dfdbe9c0b9 100644 --- a/Mac/BuildScript/resources/Welcome.rtf +++ b/Mac/BuildScript/resources/Welcome.rtf @@ -25,27 +25,4 @@ At the end of this install, click on \f2 Install Certificates \f0 to install a set of current SSL root certificates.\ \ - -\f1\b [UPDATE: fixed in macOS 13.4] macOS 13 Ventura users -\f0\b0 : Due to an issue with the macOS -\f1\b Installer -\f0\b0 app in macOS 13 Ventura updates prior to macOS 13.4, installation of some third-party packages including this Python package may fail with a vague -\f1\b "The installer encountered an error" -\f0\b0 message if the -\f1\b Installer -\f0\b0 app does not have permission to access the folder containing the downloaded installer file, typically in the -\f1\b Downloads -\f0\b0 folder. Go to -\f1\b System Settings -\f0\b0 -> -\f1\b Privacy & Security -\f0\b0 -> -\f1\b Files and Folders -\f0\b0 , then click the mark in front of -\f1\b Installer -\f0\b0 to expand, and enable -\f1\b Downloads Folder -\f0\b0 by moving the toggle to the right. See {\field{\*\fldinst{HYPERLINK "https://github.com/python/cpython/issues/103207"}}{\fldrslt https://github.com/python/cpython/issues/103207}} for up-to-date information on this issue. This problem has been resolved in macOS 13.4.\ -\ -\ } \ No newline at end of file From b8beb0762024b70fd176310fd5d21fd14b5df387 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Mon, 31 Jul 2023 02:16:39 -0400 Subject: [PATCH 096/632] [3.11] gh-99079: Update macOS installer to use OpenSSL 3.0.9. (GH-107476) --- Doc/license.rst | 308 ++++++++++-------- Mac/BuildScript/build-installer.py | 6 +- ...3-07-30-23-42-20.gh-issue-99079.JAtoh1.rst | 1 + 3 files changed, 184 insertions(+), 131 deletions(-) create mode 100644 Misc/NEWS.d/next/macOS/2023-07-30-23-42-20.gh-issue-99079.JAtoh1.rst diff --git a/Doc/license.rst b/Doc/license.rst index 37a3d4fa9e5f2c..4e8a25ed5ecc18 100644 --- a/Doc/license.rst +++ b/Doc/license.rst @@ -657,134 +657,186 @@ The modules :mod:`hashlib`, :mod:`posix`, :mod:`ssl`, :mod:`crypt` use the OpenSSL library for added performance if made available by the operating system. Additionally, the Windows and macOS installers for Python may include a copy of the OpenSSL libraries, so we include a copy -of the OpenSSL license here:: - - - LICENSE ISSUES - ============== - - The OpenSSL toolkit stays under a dual license, i.e. both the conditions of - the OpenSSL License and the original SSLeay license apply to the toolkit. - See below for the actual license texts. Actually both licenses are BSD-style - Open Source licenses. In case of any license issues related to OpenSSL - please contact openssl-core@openssl.org. - - OpenSSL License - --------------- - - /* ==================================================================== - * Copyright (c) 1998-2008 The OpenSSL Project. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. All advertising materials mentioning features or use of this - * software must display the following acknowledgment: - * "This product includes software developed by the OpenSSL Project - * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" - * - * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - * endorse or promote products derived from this software without - * prior written permission. For written permission, please contact - * openssl-core@openssl.org. - * - * 5. Products derived from this software may not be called "OpenSSL" - * nor may "OpenSSL" appear in their names without prior written - * permission of the OpenSSL Project. - * - * 6. Redistributions of any form whatsoever must retain the following - * acknowledgment: - * "This product includes software developed by the OpenSSL Project - * for use in the OpenSSL Toolkit (http://www.openssl.org/)" - * - * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY - * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - * ==================================================================== - * - * This product includes cryptographic software written by Eric Young - * (eay@cryptsoft.com). This product includes software written by Tim - * Hudson (tjh@cryptsoft.com). - * - */ - - Original SSLeay License - ----------------------- - - /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) - * All rights reserved. - * - * This package is an SSL implementation written - * by Eric Young (eay@cryptsoft.com). - * The implementation was written so as to conform with Netscapes SSL. - * - * This library is free for commercial and non-commercial use as long as - * the following conditions are aheared to. The following conditions - * apply to all code found in this distribution, be it the RC4, RSA, - * lhash, DES, etc., code; not just the SSL code. The SSL documentation - * included with this distribution is covered by the same copyright terms - * except that the holder is Tim Hudson (tjh@cryptsoft.com). - * - * Copyright remains Eric Young's, and as such any Copyright notices in - * the code are not to be removed. - * If this package is used in a product, Eric Young should be given attribution - * as the author of the parts of the library used. - * This can be in the form of a textual message at program startup or - * in documentation (online or textual) provided with the package. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * "This product includes cryptographic software written by - * Eric Young (eay@cryptsoft.com)" - * The word 'cryptographic' can be left out if the rouines from the library - * being used are not cryptographic related :-). - * 4. If you include any Windows specific code (or a derivative thereof) from - * the apps directory (application code) you must include an acknowledgement: - * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" - * - * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * The licence and distribution terms for any publically available version or - * derivative of this code cannot be changed. i.e. this code cannot simply be - * copied and put under another distribution licence - * [including the GNU Public Licence.] - */ +of the OpenSSL license here. For the OpenSSL 3.0 release, +and later releases derived from that, the Apache License v2 applies:: + + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS expat diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index 436276aee38bc0..71fbe3c0777e1b 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -246,9 +246,9 @@ def library_recipes(): result.extend([ dict( - name="OpenSSL 1.1.1u", - url="https://www.openssl.org/source/openssl-1.1.1u.tar.gz", - checksum='e2f8d84b523eecd06c7be7626830370300fbcc15386bf5142d72758f6963ebc6', + name="OpenSSL 3.0.9", + url="https://www.openssl.org/source/openssl-3.0.9.tar.gz", + checksum='eb1ab04781474360f77c318ab89d8c5a03abc38e63d65a603cabbf1b00a1dc90', buildrecipe=build_universal_openssl, configure=None, install=None, diff --git a/Misc/NEWS.d/next/macOS/2023-07-30-23-42-20.gh-issue-99079.JAtoh1.rst b/Misc/NEWS.d/next/macOS/2023-07-30-23-42-20.gh-issue-99079.JAtoh1.rst new file mode 100644 index 00000000000000..d0eef4ec1003ce --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2023-07-30-23-42-20.gh-issue-99079.JAtoh1.rst @@ -0,0 +1 @@ +Update macOS installer to use OpenSSL 3.0.9. From ed5be956ee20b6a7993bf8477e11d35498794887 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Mon, 31 Jul 2023 03:25:29 -0400 Subject: [PATCH 097/632] [3.11] gh-99079: add What's New item (GH-107482) --- Doc/whatsnew/3.11.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 91f54f38428a76..f102aa599069c2 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -2712,4 +2712,13 @@ tarfile In Python 3.14, the default will switch to ``'data'``. (Contributed by Petr Viktorin in :pep:`706`.) + +Notable Changes in 3.11.5 +========================= + +OpenSSL +------- + +* Windows builds and macOS installers from python.org now use OpenSSL 3.0. + .. _libb2: https://www.blake2.net/ From aa5f2b1f3cdb5f71f5210d4578d5c68b6dd5e255 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Mon, 31 Jul 2023 08:28:45 +0000 Subject: [PATCH 098/632] [3.11] gh-105751, test_ctypes: Remove disabled tests (GH-105826) (#107484) * The following tests were disabled since the initial ctypes commit in 2006, commit babddfca758abe34ff12023f63b18d745fae7ca9: * Callbacks.test_char_p() * DeletePointerTestCase.test_X() * NumberTestCase.test_perf() * StructureTestCase.test_subclass_creation() * Tests.test_X() of test_byteswap * NumberTestCase.test_bool_from_address() was disabled in 2007 by commit 5dc4fe09b7648f9801558e766b21a3d3b2dcad3b. * Remove check_perf() and run_test() of test_numbers. (cherry picked from commit 8f10140e74d141a0a894702044e213e6f0690d9c) Co-authored-by: Victor Stinner --- Lib/ctypes/test/test_byteswap.py | 8 ---- Lib/ctypes/test/test_callbacks.py | 8 ---- Lib/ctypes/test/test_keeprefs.py | 31 ------------ Lib/ctypes/test/test_numbers.py | 77 ------------------------------ Lib/ctypes/test/test_structures.py | 9 ---- 5 files changed, 133 deletions(-) diff --git a/Lib/ctypes/test/test_byteswap.py b/Lib/ctypes/test/test_byteswap.py index 7e98559dfbccb6..caefb774cc5375 100644 --- a/Lib/ctypes/test/test_byteswap.py +++ b/Lib/ctypes/test/test_byteswap.py @@ -14,14 +14,6 @@ def bin(s): # For Structures and Unions, these types are created on demand. class Test(unittest.TestCase): - @unittest.skip('test disabled') - def test_X(self): - print(sys.byteorder, file=sys.stderr) - for i in range(32): - bits = BITS() - setattr(bits, "i%s" % i, 1) - dump(bits) - def test_slots(self): class BigPoint(BigEndianStructure): __slots__ = () diff --git a/Lib/ctypes/test/test_callbacks.py b/Lib/ctypes/test/test_callbacks.py index 8f95a244439cdd..8e27359e4a8bf8 100644 --- a/Lib/ctypes/test/test_callbacks.py +++ b/Lib/ctypes/test/test_callbacks.py @@ -93,14 +93,6 @@ def test_char(self): self.check_type(c_char, b"x") self.check_type(c_char, b"a") - # disabled: would now (correctly) raise a RuntimeWarning about - # a memory leak. A callback function cannot return a non-integral - # C type without causing a memory leak. - @unittest.skip('test disabled') - def test_char_p(self): - self.check_type(c_char_p, "abc") - self.check_type(c_char_p, "def") - def test_pyobject(self): o = () from sys import getrefcount as grc diff --git a/Lib/ctypes/test/test_keeprefs.py b/Lib/ctypes/test/test_keeprefs.py index 94c02573fa19d8..e20adc7696f501 100644 --- a/Lib/ctypes/test/test_keeprefs.py +++ b/Lib/ctypes/test/test_keeprefs.py @@ -93,37 +93,6 @@ def test_p_cint(self): x = pointer(i) self.assertEqual(x._objects, {'1': i}) -class DeletePointerTestCase(unittest.TestCase): - @unittest.skip('test disabled') - def test_X(self): - class X(Structure): - _fields_ = [("p", POINTER(c_char_p))] - x = X() - i = c_char_p("abc def") - from sys import getrefcount as grc - print("2?", grc(i)) - x.p = pointer(i) - print("3?", grc(i)) - for i in range(320): - c_int(99) - x.p[0] - print(x.p[0]) -## del x -## print "2?", grc(i) -## del i - import gc - gc.collect() - for i in range(320): - c_int(99) - x.p[0] - print(x.p[0]) - print(x.p.contents) -## print x._objects - - x.p[0] = "spam spam" -## print x.p[0] - print("+" * 42) - print(x._objects) class PointerToStructure(unittest.TestCase): def test(self): diff --git a/Lib/ctypes/test/test_numbers.py b/Lib/ctypes/test/test_numbers.py index db500e812beb15..dc5d4a713c5b00 100644 --- a/Lib/ctypes/test/test_numbers.py +++ b/Lib/ctypes/test/test_numbers.py @@ -82,14 +82,6 @@ def test_typeerror(self): self.assertRaises(TypeError, t, "") self.assertRaises(TypeError, t, None) - @unittest.skip('test disabled') - def test_valid_ranges(self): - # invalid values of the correct type - # raise ValueError (not OverflowError) - for t, (l, h) in zip(unsigned_types, unsigned_ranges): - self.assertRaises(ValueError, t, l-1) - self.assertRaises(ValueError, t, h+1) - def test_from_param(self): # the from_param class method attribute always # returns PyCArgObject instances @@ -205,19 +197,6 @@ def test_char_from_address(self): a[0] = ord('?') self.assertEqual(v.value, b'?') - # array does not support c_bool / 't' - @unittest.skip('test disabled') - def test_bool_from_address(self): - from ctypes import c_bool - from array import array - a = array(c_bool._type_, [True]) - v = t.from_address(a.buffer_info()[0]) - self.assertEqual(v.value, a[0]) - self.assertEqual(type(v) is t) - a[0] = False - self.assertEqual(v.value, a[0]) - self.assertEqual(type(v) is t) - def test_init(self): # c_int() can be initialized from Python's int, and c_int. # Not from c_long or so, which seems strange, abc should @@ -234,62 +213,6 @@ def test_float_overflow(self): if (hasattr(t, "__ctype_le__")): self.assertRaises(OverflowError, t.__ctype_le__, big_int) - @unittest.skip('test disabled') - def test_perf(self): - check_perf() - -from ctypes import _SimpleCData -class c_int_S(_SimpleCData): - _type_ = "i" - __slots__ = [] - -def run_test(rep, msg, func, arg=None): -## items = [None] * rep - items = range(rep) - from time import perf_counter as clock - if arg is not None: - start = clock() - for i in items: - func(arg); func(arg); func(arg); func(arg); func(arg) - stop = clock() - else: - start = clock() - for i in items: - func(); func(); func(); func(); func() - stop = clock() - print("%15s: %.2f us" % (msg, ((stop-start)*1e6/5/rep))) - -def check_perf(): - # Construct 5 objects - from ctypes import c_int - - REP = 200000 - - run_test(REP, "int()", int) - run_test(REP, "int(999)", int) - run_test(REP, "c_int()", c_int) - run_test(REP, "c_int(999)", c_int) - run_test(REP, "c_int_S()", c_int_S) - run_test(REP, "c_int_S(999)", c_int_S) - -# Python 2.3 -OO, win2k, P4 700 MHz: -# -# int(): 0.87 us -# int(999): 0.87 us -# c_int(): 3.35 us -# c_int(999): 3.34 us -# c_int_S(): 3.23 us -# c_int_S(999): 3.24 us - -# Python 2.2 -OO, win2k, P4 700 MHz: -# -# int(): 0.89 us -# int(999): 0.89 us -# c_int(): 9.99 us -# c_int(999): 10.02 us -# c_int_S(): 9.87 us -# c_int_S(999): 9.85 us if __name__ == '__main__': -## check_perf() unittest.main() diff --git a/Lib/ctypes/test/test_structures.py b/Lib/ctypes/test/test_structures.py index f95d5a99a3a153..2168aa7df76688 100644 --- a/Lib/ctypes/test/test_structures.py +++ b/Lib/ctypes/test/test_structures.py @@ -359,15 +359,6 @@ def get_except(self, func, *args): except Exception as detail: return detail.__class__, str(detail) - @unittest.skip('test disabled') - def test_subclass_creation(self): - meta = type(Structure) - # same as 'class X(Structure): pass' - # fails, since we need either a _fields_ or a _abstract_ attribute - cls, msg = self.get_except(meta, "X", (Structure,), {}) - self.assertEqual((cls, msg), - (AttributeError, "class must define a '_fields_' attribute")) - def test_abstract_class(self): class X(Structure): _abstract_ = "something" From 57f27e444175a8a5ffcd86971e06de61c1c38628 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Mon, 31 Jul 2023 09:16:59 +0000 Subject: [PATCH 099/632] [3.11] gh-46376: Return existing pointer when possible in ctypes (GH-107131) (#107488) (cherry picked from commit 08447b5deb47e2a0df87fa0a0576d300e5c909b4) Co-authored-by: Konstantin --- Lib/ctypes/test/test_keeprefs.py | 27 +++++++++++++++++ ...3-07-24-01-21-16.gh-issue-46376.w-xuDL.rst | 1 + Modules/_ctypes/_ctypes.c | 29 +++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2023-07-24-01-21-16.gh-issue-46376.w-xuDL.rst diff --git a/Lib/ctypes/test/test_keeprefs.py b/Lib/ctypes/test/test_keeprefs.py index e20adc7696f501..61650ad1704753 100644 --- a/Lib/ctypes/test/test_keeprefs.py +++ b/Lib/ctypes/test/test_keeprefs.py @@ -93,6 +93,33 @@ def test_p_cint(self): x = pointer(i) self.assertEqual(x._objects, {'1': i}) + def test_pp_ownership(self): + d = c_int(123) + n = c_int(456) + + p = pointer(d) + pp = pointer(p) + + self.assertIs(pp._objects['1'], p) + self.assertIs(pp._objects['0']['1'], d) + + pp.contents.contents = n + + self.assertIs(pp._objects['1'], p) + self.assertIs(pp._objects['0']['1'], n) + + self.assertIs(p._objects['1'], n) + self.assertEqual(len(p._objects), 1) + + del d + del p + + self.assertIs(pp._objects['0']['1'], n) + self.assertEqual(len(pp._objects), 2) + + del n + + self.assertEqual(len(pp._objects), 2) class PointerToStructure(unittest.TestCase): def test(self): diff --git a/Misc/NEWS.d/next/Library/2023-07-24-01-21-16.gh-issue-46376.w-xuDL.rst b/Misc/NEWS.d/next/Library/2023-07-24-01-21-16.gh-issue-46376.w-xuDL.rst new file mode 100644 index 00000000000000..8e8f0245b4539b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-07-24-01-21-16.gh-issue-46376.w-xuDL.rst @@ -0,0 +1 @@ +Prevent memory leak and use-after-free when using pointers to pointers with ctypes diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index fc73264ff5a32c..9cc518132f92f5 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -5158,6 +5158,8 @@ static PyObject * Pointer_get_contents(CDataObject *self, void *closure) { StgDictObject *stgdict; + PyObject *keep, *ptr_probe; + CDataObject *ptr2ptr; if (*(void **)self->b_ptr == NULL) { PyErr_SetString(PyExc_ValueError, @@ -5167,6 +5169,33 @@ Pointer_get_contents(CDataObject *self, void *closure) stgdict = PyObject_stgdict((PyObject *)self); assert(stgdict); /* Cannot be NULL for pointer instances */ + + keep = GetKeepedObjects(self); + if (keep != NULL) { + // check if it's a pointer to a pointer: + // pointers will have '0' key in the _objects + ptr_probe = PyDict_GetItemString(keep, "0"); + + if (ptr_probe != NULL) { + ptr2ptr = (CDataObject*) PyDict_GetItemString(keep, "1"); + if (ptr2ptr == NULL) { + PyErr_SetString(PyExc_ValueError, + "Unexpected NULL pointer in _objects"); + return NULL; + } + // don't construct a new object, + // return existing one instead to preserve refcount + assert( + *(void**) self->b_ptr == ptr2ptr->b_ptr || + *(void**) self->b_value.c == ptr2ptr->b_ptr || + *(void**) self->b_ptr == ptr2ptr->b_value.c || + *(void**) self->b_value.c == ptr2ptr->b_value.c + ); // double-check that we are returning the same thing + Py_INCREF(ptr2ptr); + return (PyObject *) ptr2ptr; + } + } + return PyCData_FromBaseObj(stgdict->proto, (PyObject *)self, 0, *(void **)self->b_ptr); From 3abcdc71b6820e3f46f29cdcc7c91ccaf3145bdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Mon, 31 Jul 2023 09:40:20 +0000 Subject: [PATCH 100/632] [3.11] gh-104280: Add test cases for DTrace probes (GH-107125) (#107492) (cherry picked from commit a1c737b73d3658be0e1d072a340d42e3d96373c6) Co-authored-by: Furkan Onder --- Lib/test/test_dtrace.py | 70 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/Lib/test/test_dtrace.py b/Lib/test/test_dtrace.py index 4b971deacc1a5c..092a050fd1d976 100644 --- a/Lib/test/test_dtrace.py +++ b/Lib/test/test_dtrace.py @@ -3,6 +3,7 @@ import re import subprocess import sys +import sysconfig import types import unittest @@ -173,6 +174,75 @@ class SystemTapOptimizedTests(TraceTests, unittest.TestCase): backend = SystemTapBackend() optimize_python = 2 +class CheckDtraceProbes(unittest.TestCase): + @classmethod + def setUpClass(cls): + if sysconfig.get_config_var('WITH_DTRACE'): + readelf_major_version, readelf_minor_version = cls.get_readelf_version() + if support.verbose: + print(f"readelf version: {readelf_major_version}.{readelf_minor_version}") + else: + raise unittest.SkipTest("CPython must be configured with the --with-dtrace option.") + + + @staticmethod + def get_readelf_version(): + try: + cmd = ["readelf", "--version"] + proc = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + ) + with proc: + version, stderr = proc.communicate() + + if proc.returncode: + raise Exception( + f"Command {' '.join(cmd)!r} failed " + f"with exit code {proc.returncode}: " + f"stdout={version!r} stderr={stderr!r}" + ) + except OSError: + raise unittest.SkipTest("Couldn't find readelf on the path") + + # Regex to parse: + # 'GNU readelf (GNU Binutils) 2.40.0\n' -> 2.40 + match = re.search(r"^(?:GNU) readelf.*?\b(\d+)\.(\d+)", version) + if match is None: + raise unittest.SkipTest(f"Unable to parse readelf version: {version}") + + return int(match.group(1)), int(match.group(2)) + + def get_readelf_output(self): + command = ["readelf", "-n", sys.executable] + stdout, _ = subprocess.Popen( + command, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True, + ).communicate() + return stdout + + def test_check_probes(self): + readelf_output = self.get_readelf_output() + + available_probe_names = [ + "Name: import__find__load__done", + "Name: import__find__load__start", + "Name: audit", + "Name: gc__start", + "Name: gc__done", + "Name: function__entry", + "Name: function__return", + "Name: line", + ] + + for probe_name in available_probe_names: + with self.subTest(probe_name=probe_name): + self.assertIn(probe_name, readelf_output) + if __name__ == '__main__': unittest.main() From 81d0c7ca5aa6b2f86eb9ae5b66946c54dd483548 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 31 Jul 2023 05:32:18 -0700 Subject: [PATCH 101/632] [3.11] gh-106368: Add test for Argument Clinic misbehaving custom converter_init() (GH-107496) (#107500) (cherry picked from commit 2c5d206b33e4cdcafaaaf1eeaa189c10de332dc5) Co-authored-by: Erlend E. Aasland --- Lib/test/test_clinic.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index 1c01fb0ed36888..bc0eee1552e46c 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -314,6 +314,26 @@ def test_unknown_destination_command(self): msg = "unknown destination command 'nosuchcommand'" self.assertIn(msg, out) + def test_no_access_to_members_in_converter_init(self): + out = self.expect_failure(""" + /*[python input] + class Custom_converter(CConverter): + converter = "some_c_function" + def converter_init(self): + self.function.noaccess + [python start generated code]*/ + /*[clinic input] + module test + test.fn + a: Custom + [clinic start generated code]*/ + """) + msg = ( + "Stepped on a land mine, trying to access attribute 'noaccess':\n" + "Don't access members of self.function inside converter_init!" + ) + self.assertIn(msg, out) + class ClinicGroupPermuterTest(TestCase): def _test(self, l, m, r, output): From a15d06c2305844fe0ac3723336f3815edbef98bc Mon Sep 17 00:00:00 2001 From: Charlie Zhao Date: Mon, 31 Jul 2023 21:52:23 +0800 Subject: [PATCH 102/632] =?UTF-8?q?[3.11]=20gh-106263:=20Fix=20segfault=20?= =?UTF-8?q?in=20`signaldict=5Frepr`=20=20in=20`=5Fdecimal`=20module=20(#?= =?UTF-8?q?=E2=80=A6=20(#107490)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: sunmy2019 <59365878+sunmy2019@users.noreply.github.com> (cherry picked from commit 3979150a0d406707f6d253d7c15fb32c1e005a77) --- Lib/test/test_decimal.py | 30 +++++++++++++++++++ ...-06-30-16-42-44.gh-issue-106263.tk-t93.rst | 2 ++ Modules/_decimal/_decimal.c | 30 +++++++++++++++++-- Tools/c-analyzer/cpython/ignored.tsv | 1 + 4 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-06-30-16-42-44.gh-issue-106263.tk-t93.rst diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index 749496e3e9455e..d0ba34803c21e6 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -5682,6 +5682,36 @@ def test_maxcontext_exact_arith(self): self.assertEqual(Decimal(400) ** -1, Decimal('0.0025')) + def test_c_signaldict_segfault(self): + # See gh-106263 for details. + SignalDict = type(C.Context().flags) + sd = SignalDict() + err_msg = "invalid signal dict" + + with self.assertRaisesRegex(ValueError, err_msg): + len(sd) + + with self.assertRaisesRegex(ValueError, err_msg): + iter(sd) + + with self.assertRaisesRegex(ValueError, err_msg): + repr(sd) + + with self.assertRaisesRegex(ValueError, err_msg): + sd[C.InvalidOperation] = True + + with self.assertRaisesRegex(ValueError, err_msg): + sd[C.InvalidOperation] + + with self.assertRaisesRegex(ValueError, err_msg): + sd == C.Context().flags + + with self.assertRaisesRegex(ValueError, err_msg): + C.Context().flags == sd + + with self.assertRaisesRegex(ValueError, err_msg): + sd.copy() + @requires_docstrings @requires_cdecimal class SignatureTest(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2023-06-30-16-42-44.gh-issue-106263.tk-t93.rst b/Misc/NEWS.d/next/Library/2023-06-30-16-42-44.gh-issue-106263.tk-t93.rst new file mode 100644 index 00000000000000..d86a6bfdabbfb0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-06-30-16-42-44.gh-issue-106263.tk-t93.rst @@ -0,0 +1,2 @@ +Fix crash when calling ``repr`` with a manually constructed SignalDict object. +Patch by Charlie Zhao. diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index 5cacdca3f44baa..a97490d48c90ac 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -247,14 +247,12 @@ value_error_int(const char *mesg) return -1; } -#ifdef CONFIG_32 static PyObject * value_error_ptr(const char *mesg) { PyErr_SetString(PyExc_ValueError, mesg); return NULL; } -#endif static int type_error_int(const char *mesg) @@ -541,6 +539,8 @@ getround(PyObject *v) initialized to new SignalDicts. Once a SignalDict is tied to a context, it cannot be deleted. */ +static const char *INVALID_SIGNALDICT_ERROR_MSG = "invalid signal dict"; + static int signaldict_init(PyObject *self, PyObject *args UNUSED, PyObject *kwds UNUSED) { @@ -549,8 +549,11 @@ signaldict_init(PyObject *self, PyObject *args UNUSED, PyObject *kwds UNUSED) } static Py_ssize_t -signaldict_len(PyObject *self UNUSED) +signaldict_len(PyObject *self) { + if (SdFlagAddr(self) == NULL) { + return value_error_int(INVALID_SIGNALDICT_ERROR_MSG); + } return SIGNAL_MAP_LEN; } @@ -558,6 +561,9 @@ static PyObject *SignalTuple; static PyObject * signaldict_iter(PyObject *self UNUSED) { + if (SdFlagAddr(self) == NULL) { + return value_error_ptr(INVALID_SIGNALDICT_ERROR_MSG); + } return PyTuple_Type.tp_iter(SignalTuple); } @@ -565,6 +571,9 @@ static PyObject * signaldict_getitem(PyObject *self, PyObject *key) { uint32_t flag; + if (SdFlagAddr(self) == NULL) { + return value_error_ptr(INVALID_SIGNALDICT_ERROR_MSG); + } flag = exception_as_flag(key); if (flag & DEC_ERRORS) { @@ -580,6 +589,10 @@ signaldict_setitem(PyObject *self, PyObject *key, PyObject *value) uint32_t flag; int x; + if (SdFlagAddr(self) == NULL) { + return value_error_int(INVALID_SIGNALDICT_ERROR_MSG); + } + if (value == NULL) { return value_error_int("signal keys cannot be deleted"); } @@ -612,6 +625,10 @@ signaldict_repr(PyObject *self) const char *b[SIGNAL_MAP_LEN]; /* bool */ int i; + if (SdFlagAddr(self) == NULL) { + return value_error_ptr(INVALID_SIGNALDICT_ERROR_MSG); + } + assert(SIGNAL_MAP_LEN == 9); for (cm=signal_map, i=0; cm->name != NULL; cm++, i++) { @@ -634,6 +651,10 @@ signaldict_richcompare(PyObject *v, PyObject *w, int op) assert(PyDecSignalDict_Check(v)); + if ((SdFlagAddr(v) == NULL) || (SdFlagAddr(w) == NULL)) { + return value_error_ptr(INVALID_SIGNALDICT_ERROR_MSG); + } + if (op == Py_EQ || op == Py_NE) { if (PyDecSignalDict_Check(w)) { res = (SdFlags(v)==SdFlags(w)) ^ (op==Py_NE) ? Py_True : Py_False; @@ -662,6 +683,9 @@ signaldict_richcompare(PyObject *v, PyObject *w, int op) static PyObject * signaldict_copy(PyObject *self, PyObject *args UNUSED) { + if (SdFlagAddr(self) == NULL) { + return value_error_ptr(INVALID_SIGNALDICT_ERROR_MSG); + } return flags_as_dict(SdFlags(self)); } diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv index 63d0695bfb4a6e..6e4f1c4046398c 100644 --- a/Tools/c-analyzer/cpython/ignored.tsv +++ b/Tools/c-analyzer/cpython/ignored.tsv @@ -1432,6 +1432,7 @@ Modules/_decimal/_decimal.c - invalid_rounding_err - Modules/_decimal/_decimal.c - invalid_signals_err - Modules/_decimal/_decimal.c - signal_map - Modules/_decimal/_decimal.c - ssize_constants - +Modules/_decimal/_decimal.c - INVALID_SIGNALDICT_ERROR_MSG - Modules/_elementtree.c - ExpatMemoryHandler - Modules/_io/_iomodule.c - static_types - Modules/_io/textio.c - encodefuncs - From 1b40431189b2fe5da8020d28b60d6d2854b780ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Mon, 31 Jul 2023 14:40:47 +0000 Subject: [PATCH 103/632] [3.11] gh-105751: Remove obsolete `object` base class in some ctypes tests (GH-107460) (#107502) (cherry picked from commit 520efecfc3aed34d3a44545c7cd872d1aea8c7dc) Co-authored-by: Tomas R --- Lib/ctypes/test/test_as_parameter.py | 6 +++--- Lib/ctypes/test/test_callbacks.py | 2 +- Lib/ctypes/test/test_numbers.py | 8 ++++---- Lib/ctypes/test/test_parameters.py | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Lib/ctypes/test/test_as_parameter.py b/Lib/ctypes/test/test_as_parameter.py index 9c39179d2a446a..aaaf6e2ceb0e83 100644 --- a/Lib/ctypes/test/test_as_parameter.py +++ b/Lib/ctypes/test/test_as_parameter.py @@ -194,7 +194,7 @@ class S8I(Structure): def test_recursive_as_param(self): from ctypes import c_int - class A(object): + class A: pass a = A() @@ -205,7 +205,7 @@ class A(object): #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class AsParamWrapper(object): +class AsParamWrapper: def __init__(self, param): self._as_parameter_ = param @@ -214,7 +214,7 @@ class AsParamWrapperTestCase(BasicWrapTestCase): #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class AsParamPropertyWrapper(object): +class AsParamPropertyWrapper: def __init__(self, param): self._param = param diff --git a/Lib/ctypes/test/test_callbacks.py b/Lib/ctypes/test/test_callbacks.py index 8e27359e4a8bf8..5c514db5114e93 100644 --- a/Lib/ctypes/test/test_callbacks.py +++ b/Lib/ctypes/test/test_callbacks.py @@ -122,7 +122,7 @@ def test_unsupported_restype_2(self): def test_issue_7959(self): proto = self.functype.__func__(None) - class X(object): + class X: def func(self): pass def __init__(self): self.v = proto(self.func) diff --git a/Lib/ctypes/test/test_numbers.py b/Lib/ctypes/test/test_numbers.py index dc5d4a713c5b00..a7696376a5ab05 100644 --- a/Lib/ctypes/test/test_numbers.py +++ b/Lib/ctypes/test/test_numbers.py @@ -98,7 +98,7 @@ def test_byref(self): def test_floats(self): # c_float and c_double can be created from # Python int and float - class FloatLike(object): + class FloatLike: def __float__(self): return 2.0 f = FloatLike() @@ -109,15 +109,15 @@ def __float__(self): self.assertEqual(t(f).value, 2.0) def test_integers(self): - class FloatLike(object): + class FloatLike: def __float__(self): return 2.0 f = FloatLike() - class IntLike(object): + class IntLike: def __int__(self): return 2 d = IntLike() - class IndexLike(object): + class IndexLike: def __index__(self): return 2 i = IndexLike() diff --git a/Lib/ctypes/test/test_parameters.py b/Lib/ctypes/test/test_parameters.py index 3fdc994e9078ce..59c94e3cc23cb5 100644 --- a/Lib/ctypes/test/test_parameters.py +++ b/Lib/ctypes/test/test_parameters.py @@ -145,7 +145,7 @@ def test_noctypes_argtype(self): # TypeError: has no from_param method self.assertRaises(TypeError, setattr, func, "argtypes", (object,)) - class Adapter(object): + class Adapter: def from_param(cls, obj): return None @@ -153,7 +153,7 @@ def from_param(cls, obj): self.assertEqual(func(None), None) self.assertEqual(func(object()), None) - class Adapter(object): + class Adapter: def from_param(cls, obj): return obj @@ -162,7 +162,7 @@ def from_param(cls, obj): self.assertRaises(ArgumentError, func, object()) self.assertEqual(func(c_void_p(42)), 42) - class Adapter(object): + class Adapter: def from_param(cls, obj): raise ValueError(obj) From 3de42bbf8cf372b2e6a59bb505b1c2c1544018db Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 31 Jul 2023 08:33:07 -0700 Subject: [PATCH 104/632] [3.11] gh-105578: Add more usage examples to `typing.AnyStr` docs (GH-107045) (#107504) gh-105578: Add more usage examples to `typing.AnyStr` docs (GH-107045) ``typing.AnyStr`` has different semantics to ``str | bytes``, which often leads to user confusion (cherry picked from commit f877b32b879f2076bb1c52826af0c28ebf1aaeed) Co-authored-by: Michael The --- Doc/library/typing.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index a0766690bad287..b6bb50b65db620 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -801,6 +801,21 @@ using ``[]``. concat(b"foo", b"bar") # OK, output has type 'bytes' concat("foo", b"bar") # Error, cannot mix str and bytes + Note that, despite its name, ``AnyStr`` has nothing to do with the + :class:`Any` type, nor does it mean "any string". In particular, ``AnyStr`` + and ``str | bytes`` are different from each other and have different use + cases:: + + # Invalid use of AnyStr: + # The type variable is used only once in the function signature, + # so cannot be "solved" by the type checker + def greet_bad(cond: bool) -> AnyStr: + return "hi there!" if cond else b"greetings!" + + # The better way of annotating this function: + def greet_proper(cond: bool) -> str | bytes: + return "hi there!" if cond else b"greetings!" + .. data:: LiteralString Special type that includes only literal strings. From 3be07c98b3e22601a5837ce25c04803ba63a1aa6 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 31 Jul 2023 14:43:16 -0700 Subject: [PATCH 105/632] [3.11] gh-107507: Replace 'The goals of Argument Clinic' with a summary (GH-107508) (#107517) Summarise the goals of Argument Clinic in a single sentence. Mention that Argument Clinic was introduced with PEP-436. (cherry picked from commit abb71c6a8f73482c910ffdf050a86089a48e0e60) Co-authored-by: Erlend E. Aasland --- Doc/howto/clinic.rst | 58 +++----------------------------------------- 1 file changed, 4 insertions(+), 54 deletions(-) diff --git a/Doc/howto/clinic.rst b/Doc/howto/clinic.rst index 1ec8984391bbef..913a3df7e3c48a 100644 --- a/Doc/howto/clinic.rst +++ b/Doc/howto/clinic.rst @@ -13,8 +13,10 @@ Argument Clinic How-To .. topic:: Abstract Argument Clinic is a preprocessor for CPython C files. - Its purpose is to automate all the boilerplate involved - with writing argument parsing code for "builtins", + It was introduced in Python 3.4 with :pep:`436`, + in order to provide introspection signatures, + and to generate performant and tailor-made boilerplate code + for argument parsing in CPython builtins, module level functions, and class methods. This document is divided in four major sections: @@ -44,58 +46,6 @@ Argument Clinic How-To Background ========== - -The goals of Argument Clinic ----------------------------- - -Argument Clinic's primary goal -is to take over responsibility for all argument parsing code -inside CPython. This means that, when you convert a function -to work with Argument Clinic, that function should no longer -do any of its own argument parsing—the code generated by -Argument Clinic should be a "black box" to you, where CPython -calls in at the top, and your code gets called at the bottom, -with ``PyObject *args`` (and maybe ``PyObject *kwargs``) -magically converted into the C variables and types you need. - -In order for Argument Clinic to accomplish its primary goal, -it must be easy to use. Currently, working with CPython's -argument parsing library is a chore, requiring maintaining -redundant information in a surprising number of places. -When you use Argument Clinic, you don't have to repeat yourself. - -Obviously, no one would want to use Argument Clinic unless -it's solving their problem—and without creating new problems of -its own. -So it's paramount that Argument Clinic generate correct code. -It'd be nice if the code was faster, too, but at the very least -it should not introduce a major speed regression. (Eventually Argument -Clinic *should* make a major speedup possible—we could -rewrite its code generator to produce tailor-made argument -parsing code, rather than calling the general-purpose CPython -argument parsing library. That would make for the fastest -argument parsing possible!) - -Additionally, Argument Clinic must be flexible enough to -work with any approach to argument parsing. Python has -some functions with some very strange parsing behaviors; -Argument Clinic's goal is to support all of them. - -Finally, the original motivation for Argument Clinic was -to provide introspection "signatures" for CPython builtins. -It used to be, the introspection query functions would throw -an exception if you passed in a builtin. With Argument -Clinic, that's a thing of the past! - -One idea you should keep in mind, as you work with -Argument Clinic: the more information you give it, the -better job it'll be able to do. -Argument Clinic is admittedly relatively simple right -now. But as it evolves it will get more sophisticated, -and it should be able to do many interesting and smart -things with all the information you give it. - - Basic concepts -------------- From 46cae02085311481dc8b1ea9a5110969d9325bc7 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 1 Aug 2023 03:21:08 -0700 Subject: [PATCH 106/632] [3.11] gh-106092: Fix use-after-free crash in frame_dealloc (GH-106875) (#107533) --- .../2023-07-18-16-13-51.gh-issue-106092.bObgRM.rst | 2 ++ Objects/frameobject.c | 13 +++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-07-18-16-13-51.gh-issue-106092.bObgRM.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-07-18-16-13-51.gh-issue-106092.bObgRM.rst b/Misc/NEWS.d/next/Core and Builtins/2023-07-18-16-13-51.gh-issue-106092.bObgRM.rst new file mode 100644 index 00000000000000..7fb5b45c763e45 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-07-18-16-13-51.gh-issue-106092.bObgRM.rst @@ -0,0 +1,2 @@ +Fix a segmentation fault caused by a use-after-free bug in ``frame_dealloc`` +when the trashcan delays the deallocation of a ``PyFrameObject``. diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 1e6bdcfb592df5..425749d14cf014 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -851,9 +851,6 @@ frame_dealloc(PyFrameObject *f) /* It is the responsibility of the owning generator/coroutine * to have cleared the generator pointer */ - assert(f->f_frame->owner != FRAME_OWNED_BY_GENERATOR || - _PyFrame_GetGenerator(f->f_frame)->gi_frame_state == FRAME_CLEARED); - if (_PyObject_GC_IS_TRACKED(f)) { _PyObject_GC_UNTRACK(f); } @@ -861,10 +858,14 @@ frame_dealloc(PyFrameObject *f) Py_TRASHCAN_BEGIN(f, frame_dealloc); PyCodeObject *co = NULL; + /* GH-106092: If f->f_frame was on the stack and we reached the maximum + * nesting depth for deallocations, the trashcan may have delayed this + * deallocation until after f->f_frame is freed. Avoid dereferencing + * f->f_frame unless we know it still points to valid memory. */ + _PyInterpreterFrame *frame = (_PyInterpreterFrame *)f->_f_frame_data; + /* Kill all local variables including specials, if we own them */ - if (f->f_frame->owner == FRAME_OWNED_BY_FRAME_OBJECT) { - assert(f->f_frame == (_PyInterpreterFrame *)f->_f_frame_data); - _PyInterpreterFrame *frame = (_PyInterpreterFrame *)f->_f_frame_data; + if (f->f_frame == frame && frame->owner == FRAME_OWNED_BY_FRAME_OBJECT) { /* Don't clear code object until the end */ co = frame->f_code; frame->f_code = NULL; From 623b0d9c598585dd8917b794ae99ce89c6711c5e Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 1 Aug 2023 13:31:22 -0700 Subject: [PATCH 107/632] [3.11] Clarify `Self` interaction with subclasses (GH-107511) (#107549) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Alexandru Mărășteanu --- Doc/library/typing.rst | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index b6bb50b65db620..0419f6799c6646 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -905,13 +905,17 @@ using ``[]``. For example:: - from typing import Self + from typing import Self, reveal_type class Foo: def return_self(self) -> Self: ... return self + class SubclassOfFoo(Foo): pass + + reveal_type(Foo().return_self()) # Revealed type is "Foo" + reveal_type(SubclassOfFoo().return_self()) # Revealed type is "SubclassOfFoo" This annotation is semantically equivalent to the following, albeit in a more succinct fashion:: @@ -925,15 +929,11 @@ using ``[]``. ... return self - In general if something currently follows the pattern of:: - - class Foo: - def return_self(self) -> "Foo": - ... - return self - - You should use :data:`Self` as calls to ``SubclassOfFoo.return_self`` would have - ``Foo`` as the return type and not ``SubclassOfFoo``. + In general, if something returns ``self``, as in the above examples, you + should use ``Self`` as the return annotation. If ``Foo.return_self`` was + annotated as returning ``"Foo"``, then the type checker would infer the + object returned from ``SubclassOfFoo.return_self`` as being of type ``Foo`` + rather than ``SubclassOfFoo``. Other common use cases include: @@ -941,6 +941,17 @@ using ``[]``. of the ``cls`` parameter. - Annotating an :meth:`~object.__enter__` method which returns self. + You should not use ``Self`` as the return annotation if the method is not + guaranteed to return an instance of a subclass when the class is + subclassed:: + + class Eggs: + # Self would be an incorrect return annotation here, + # as the object returned is always an instance of Eggs, + # even in subclasses + def returns_eggs(self) -> "Eggs": + return Eggs() + See :pep:`673` for more details. .. versionadded:: 3.11 From 4f65f03f2d597881e1a805e91579d10889299a3d Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 3 Aug 2023 07:09:27 -0700 Subject: [PATCH 108/632] [3.11] gh-107077: Raise SSLCertVerificationError even if the error is set via SSL_ERROR_SYSCALL (GH-107586) (#107588) Co-authored-by: Pablo Galindo Salgado Co-authored-by: T. Wouters --- .../Library/2023-08-03-12-52-19.gh-issue-107077.-pzHD6.rst | 6 ++++++ Modules/_ssl.c | 4 ++++ 2 files changed, 10 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2023-08-03-12-52-19.gh-issue-107077.-pzHD6.rst diff --git a/Misc/NEWS.d/next/Library/2023-08-03-12-52-19.gh-issue-107077.-pzHD6.rst b/Misc/NEWS.d/next/Library/2023-08-03-12-52-19.gh-issue-107077.-pzHD6.rst new file mode 100644 index 00000000000000..ecaf437a48e0ae --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-08-03-12-52-19.gh-issue-107077.-pzHD6.rst @@ -0,0 +1,6 @@ +Seems that in some conditions, OpenSSL will return ``SSL_ERROR_SYSCALL`` +instead of ``SSL_ERROR_SSL`` when a certification verification has failed, +but the error parameters will still contain ``ERR_LIB_SSL`` and +``SSL_R_CERTIFICATE_VERIFY_FAILED``. We are now detecting this situation and +raising the appropiate ``ssl.SSLCertVerificationError``. Patch by Pablo +Galindo diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 09257223924463..4ccd1240bac3e6 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -650,6 +650,10 @@ PySSL_SetError(PySSLSocket *sslsock, int ret, const char *filename, int lineno) errstr = "Some I/O error occurred"; } } else { + if (ERR_GET_LIB(e) == ERR_LIB_SSL && + ERR_GET_REASON(e) == SSL_R_CERTIFICATE_VERIFY_FAILED) { + type = state->PySSLCertVerificationErrorObject; + } p = PY_SSL_ERROR_SYSCALL; } break; From 6a2f981418e781e4bdbb4ed257845a14a5e82874 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 4 Aug 2023 12:11:42 -0700 Subject: [PATCH 109/632] [3.11] Docs: Only include Plausible for html, not for epub etc (GH-107637) (#107643) Co-authored-by: Hugo van Kemenade --- Doc/tools/templates/layout.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Doc/tools/templates/layout.html b/Doc/tools/templates/layout.html index 9832feba141675..80103158ea01e6 100644 --- a/Doc/tools/templates/layout.html +++ b/Doc/tools/templates/layout.html @@ -26,7 +26,9 @@ {% endblock %} {% block extrahead %} - + {% if builder == "html" %} + + {% endif %} {% if builder != "htmlhelp" %} {% if pagename == 'whatsnew/changelog' and not embedded %} From f978a79130133de1cf26436c460ff169cf5c8dc5 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sat, 5 Aug 2023 00:14:21 +0300 Subject: [PATCH 110/632] [3.11] Docs: upgrade to python-docs-theme 2023.7 (GH-107617) (#107634) (cherry picked from commit 19f32b24b2e1680ff9929bb64d681397b259c6fb) --- Doc/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/requirements.txt b/Doc/requirements.txt index ddecdb4a5f7c2e..2b8d4df214730f 100644 --- a/Doc/requirements.txt +++ b/Doc/requirements.txt @@ -15,6 +15,6 @@ sphinxext-opengraph>=0.7.1 # The theme used by the documentation is stored separately, so we need # to install that as well. -python-docs-theme>=2022.1 +python-docs-theme>=2023.7 -c constraints.txt From 7e834c45541dbc000a8fcff7d70228eb948dea74 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 5 Aug 2023 04:40:44 -0700 Subject: [PATCH 111/632] [3.11] gh-107432 Fix incorrect indentation in annotations HOWTO (GH-107445) (#107655) gh-107432 Fix incorrect indentation in annotations HOWTO (GH-107445) gh-107432 Fix incorrect indentation in annotations document Body text in https://docs.python.org/3/howto/annotations.html was indented throughout, and was being rendered in blockquote elements. (cherry picked from commit 5e2746d6e2fb0da29225ead7135f078c5f087b57) Co-authored-by: Daniele Procida --- Doc/howto/annotations.rst | 326 +++++++++++++++++++------------------- 1 file changed, 163 insertions(+), 163 deletions(-) diff --git a/Doc/howto/annotations.rst b/Doc/howto/annotations.rst index 472069032d6509..1134686c947d66 100644 --- a/Doc/howto/annotations.rst +++ b/Doc/howto/annotations.rst @@ -32,201 +32,201 @@ Annotations Best Practices Accessing The Annotations Dict Of An Object In Python 3.10 And Newer ==================================================================== - Python 3.10 adds a new function to the standard library: - :func:`inspect.get_annotations`. In Python versions 3.10 - and newer, calling this function is the best practice for - accessing the annotations dict of any object that supports - annotations. This function can also "un-stringize" - stringized annotations for you. - - If for some reason :func:`inspect.get_annotations` isn't - viable for your use case, you may access the - ``__annotations__`` data member manually. Best practice - for this changed in Python 3.10 as well: as of Python 3.10, - ``o.__annotations__`` is guaranteed to *always* work - on Python functions, classes, and modules. If you're - certain the object you're examining is one of these three - *specific* objects, you may simply use ``o.__annotations__`` - to get at the object's annotations dict. - - However, other types of callables--for example, - callables created by :func:`functools.partial`--may - not have an ``__annotations__`` attribute defined. When - accessing the ``__annotations__`` of a possibly unknown - object, best practice in Python versions 3.10 and - newer is to call :func:`getattr` with three arguments, - for example ``getattr(o, '__annotations__', None)``. - - Before Python 3.10, accessing ``__annotations__`` on a class that - defines no annotations but that has a parent class with - annotations would return the parent's ``__annotations__``. - In Python 3.10 and newer, the child class's annotations - will be an empty dict instead. +Python 3.10 adds a new function to the standard library: +:func:`inspect.get_annotations`. In Python versions 3.10 +and newer, calling this function is the best practice for +accessing the annotations dict of any object that supports +annotations. This function can also "un-stringize" +stringized annotations for you. + +If for some reason :func:`inspect.get_annotations` isn't +viable for your use case, you may access the +``__annotations__`` data member manually. Best practice +for this changed in Python 3.10 as well: as of Python 3.10, +``o.__annotations__`` is guaranteed to *always* work +on Python functions, classes, and modules. If you're +certain the object you're examining is one of these three +*specific* objects, you may simply use ``o.__annotations__`` +to get at the object's annotations dict. + +However, other types of callables--for example, +callables created by :func:`functools.partial`--may +not have an ``__annotations__`` attribute defined. When +accessing the ``__annotations__`` of a possibly unknown +object, best practice in Python versions 3.10 and +newer is to call :func:`getattr` with three arguments, +for example ``getattr(o, '__annotations__', None)``. + +Before Python 3.10, accessing ``__annotations__`` on a class that +defines no annotations but that has a parent class with +annotations would return the parent's ``__annotations__``. +In Python 3.10 and newer, the child class's annotations +will be an empty dict instead. Accessing The Annotations Dict Of An Object In Python 3.9 And Older =================================================================== - In Python 3.9 and older, accessing the annotations dict - of an object is much more complicated than in newer versions. - The problem is a design flaw in these older versions of Python, - specifically to do with class annotations. +In Python 3.9 and older, accessing the annotations dict +of an object is much more complicated than in newer versions. +The problem is a design flaw in these older versions of Python, +specifically to do with class annotations. - Best practice for accessing the annotations dict of other - objects--functions, other callables, and modules--is the same - as best practice for 3.10, assuming you aren't calling - :func:`inspect.get_annotations`: you should use three-argument - :func:`getattr` to access the object's ``__annotations__`` - attribute. +Best practice for accessing the annotations dict of other +objects--functions, other callables, and modules--is the same +as best practice for 3.10, assuming you aren't calling +:func:`inspect.get_annotations`: you should use three-argument +:func:`getattr` to access the object's ``__annotations__`` +attribute. - Unfortunately, this isn't best practice for classes. The problem - is that, since ``__annotations__`` is optional on classes, and - because classes can inherit attributes from their base classes, - accessing the ``__annotations__`` attribute of a class may - inadvertently return the annotations dict of a *base class.* - As an example:: +Unfortunately, this isn't best practice for classes. The problem +is that, since ``__annotations__`` is optional on classes, and +because classes can inherit attributes from their base classes, +accessing the ``__annotations__`` attribute of a class may +inadvertently return the annotations dict of a *base class.* +As an example:: - class Base: - a: int = 3 - b: str = 'abc' + class Base: + a: int = 3 + b: str = 'abc' - class Derived(Base): - pass + class Derived(Base): + pass - print(Derived.__annotations__) + print(Derived.__annotations__) - This will print the annotations dict from ``Base``, not - ``Derived``. +This will print the annotations dict from ``Base``, not +``Derived``. - Your code will have to have a separate code path if the object - you're examining is a class (``isinstance(o, type)``). - In that case, best practice relies on an implementation detail - of Python 3.9 and before: if a class has annotations defined, - they are stored in the class's ``__dict__`` dictionary. Since - the class may or may not have annotations defined, best practice - is to call the ``get`` method on the class dict. +Your code will have to have a separate code path if the object +you're examining is a class (``isinstance(o, type)``). +In that case, best practice relies on an implementation detail +of Python 3.9 and before: if a class has annotations defined, +they are stored in the class's ``__dict__`` dictionary. Since +the class may or may not have annotations defined, best practice +is to call the ``get`` method on the class dict. - To put it all together, here is some sample code that safely - accesses the ``__annotations__`` attribute on an arbitrary - object in Python 3.9 and before:: +To put it all together, here is some sample code that safely +accesses the ``__annotations__`` attribute on an arbitrary +object in Python 3.9 and before:: - if isinstance(o, type): - ann = o.__dict__.get('__annotations__', None) - else: - ann = getattr(o, '__annotations__', None) + if isinstance(o, type): + ann = o.__dict__.get('__annotations__', None) + else: + ann = getattr(o, '__annotations__', None) - After running this code, ``ann`` should be either a - dictionary or ``None``. You're encouraged to double-check - the type of ``ann`` using :func:`isinstance` before further - examination. +After running this code, ``ann`` should be either a +dictionary or ``None``. You're encouraged to double-check +the type of ``ann`` using :func:`isinstance` before further +examination. - Note that some exotic or malformed type objects may not have - a ``__dict__`` attribute, so for extra safety you may also wish - to use :func:`getattr` to access ``__dict__``. +Note that some exotic or malformed type objects may not have +a ``__dict__`` attribute, so for extra safety you may also wish +to use :func:`getattr` to access ``__dict__``. Manually Un-Stringizing Stringized Annotations ============================================== - In situations where some annotations may be "stringized", - and you wish to evaluate those strings to produce the - Python values they represent, it really is best to - call :func:`inspect.get_annotations` to do this work - for you. - - If you're using Python 3.9 or older, or if for some reason - you can't use :func:`inspect.get_annotations`, you'll need - to duplicate its logic. You're encouraged to examine the - implementation of :func:`inspect.get_annotations` in the - current Python version and follow a similar approach. - - In a nutshell, if you wish to evaluate a stringized annotation - on an arbitrary object ``o``: - - * If ``o`` is a module, use ``o.__dict__`` as the - ``globals`` when calling :func:`eval`. - * If ``o`` is a class, use ``sys.modules[o.__module__].__dict__`` - as the ``globals``, and ``dict(vars(o))`` as the ``locals``, - when calling :func:`eval`. - * If ``o`` is a wrapped callable using :func:`functools.update_wrapper`, - :func:`functools.wraps`, or :func:`functools.partial`, iteratively - unwrap it by accessing either ``o.__wrapped__`` or ``o.func`` as - appropriate, until you have found the root unwrapped function. - * If ``o`` is a callable (but not a class), use - ``o.__globals__`` as the globals when calling :func:`eval`. - - However, not all string values used as annotations can - be successfully turned into Python values by :func:`eval`. - String values could theoretically contain any valid string, - and in practice there are valid use cases for type hints that - require annotating with string values that specifically - *can't* be evaluated. For example: - - * :pep:`604` union types using ``|``, before support for this - was added to Python 3.10. - * Definitions that aren't needed at runtime, only imported - when :const:`typing.TYPE_CHECKING` is true. - - If :func:`eval` attempts to evaluate such values, it will - fail and raise an exception. So, when designing a library - API that works with annotations, it's recommended to only - attempt to evaluate string values when explicitly requested - to by the caller. +In situations where some annotations may be "stringized", +and you wish to evaluate those strings to produce the +Python values they represent, it really is best to +call :func:`inspect.get_annotations` to do this work +for you. + +If you're using Python 3.9 or older, or if for some reason +you can't use :func:`inspect.get_annotations`, you'll need +to duplicate its logic. You're encouraged to examine the +implementation of :func:`inspect.get_annotations` in the +current Python version and follow a similar approach. + +In a nutshell, if you wish to evaluate a stringized annotation +on an arbitrary object ``o``: + +* If ``o`` is a module, use ``o.__dict__`` as the + ``globals`` when calling :func:`eval`. +* If ``o`` is a class, use ``sys.modules[o.__module__].__dict__`` + as the ``globals``, and ``dict(vars(o))`` as the ``locals``, + when calling :func:`eval`. +* If ``o`` is a wrapped callable using :func:`functools.update_wrapper`, + :func:`functools.wraps`, or :func:`functools.partial`, iteratively + unwrap it by accessing either ``o.__wrapped__`` or ``o.func`` as + appropriate, until you have found the root unwrapped function. +* If ``o`` is a callable (but not a class), use + ``o.__globals__`` as the globals when calling :func:`eval`. + +However, not all string values used as annotations can +be successfully turned into Python values by :func:`eval`. +String values could theoretically contain any valid string, +and in practice there are valid use cases for type hints that +require annotating with string values that specifically +*can't* be evaluated. For example: + +* :pep:`604` union types using ``|``, before support for this + was added to Python 3.10. +* Definitions that aren't needed at runtime, only imported + when :const:`typing.TYPE_CHECKING` is true. + +If :func:`eval` attempts to evaluate such values, it will +fail and raise an exception. So, when designing a library +API that works with annotations, it's recommended to only +attempt to evaluate string values when explicitly requested +to by the caller. Best Practices For ``__annotations__`` In Any Python Version ============================================================ - * You should avoid assigning to the ``__annotations__`` member - of objects directly. Let Python manage setting ``__annotations__``. +* You should avoid assigning to the ``__annotations__`` member + of objects directly. Let Python manage setting ``__annotations__``. - * If you do assign directly to the ``__annotations__`` member - of an object, you should always set it to a ``dict`` object. +* If you do assign directly to the ``__annotations__`` member + of an object, you should always set it to a ``dict`` object. - * If you directly access the ``__annotations__`` member - of an object, you should ensure that it's a - dictionary before attempting to examine its contents. +* If you directly access the ``__annotations__`` member + of an object, you should ensure that it's a + dictionary before attempting to examine its contents. - * You should avoid modifying ``__annotations__`` dicts. +* You should avoid modifying ``__annotations__`` dicts. - * You should avoid deleting the ``__annotations__`` attribute - of an object. +* You should avoid deleting the ``__annotations__`` attribute + of an object. ``__annotations__`` Quirks ========================== - In all versions of Python 3, function - objects lazy-create an annotations dict if no annotations - are defined on that object. You can delete the ``__annotations__`` - attribute using ``del fn.__annotations__``, but if you then - access ``fn.__annotations__`` the object will create a new empty dict - that it will store and return as its annotations. Deleting the - annotations on a function before it has lazily created its annotations - dict will throw an ``AttributeError``; using ``del fn.__annotations__`` - twice in a row is guaranteed to always throw an ``AttributeError``. - - Everything in the above paragraph also applies to class and module - objects in Python 3.10 and newer. - - In all versions of Python 3, you can set ``__annotations__`` - on a function object to ``None``. However, subsequently - accessing the annotations on that object using ``fn.__annotations__`` - will lazy-create an empty dictionary as per the first paragraph of - this section. This is *not* true of modules and classes, in any Python - version; those objects permit setting ``__annotations__`` to any - Python value, and will retain whatever value is set. - - If Python stringizes your annotations for you - (using ``from __future__ import annotations``), and you - specify a string as an annotation, the string will - itself be quoted. In effect the annotation is quoted - *twice.* For example:: - - from __future__ import annotations - def foo(a: "str"): pass - - print(foo.__annotations__) - - This prints ``{'a': "'str'"}``. This shouldn't really be considered - a "quirk"; it's mentioned here simply because it might be surprising. +In all versions of Python 3, function +objects lazy-create an annotations dict if no annotations +are defined on that object. You can delete the ``__annotations__`` +attribute using ``del fn.__annotations__``, but if you then +access ``fn.__annotations__`` the object will create a new empty dict +that it will store and return as its annotations. Deleting the +annotations on a function before it has lazily created its annotations +dict will throw an ``AttributeError``; using ``del fn.__annotations__`` +twice in a row is guaranteed to always throw an ``AttributeError``. + +Everything in the above paragraph also applies to class and module +objects in Python 3.10 and newer. + +In all versions of Python 3, you can set ``__annotations__`` +on a function object to ``None``. However, subsequently +accessing the annotations on that object using ``fn.__annotations__`` +will lazy-create an empty dictionary as per the first paragraph of +this section. This is *not* true of modules and classes, in any Python +version; those objects permit setting ``__annotations__`` to any +Python value, and will retain whatever value is set. + +If Python stringizes your annotations for you +(using ``from __future__ import annotations``), and you +specify a string as an annotation, the string will +itself be quoted. In effect the annotation is quoted +*twice.* For example:: + + from __future__ import annotations + def foo(a: "str"): pass + + print(foo.__annotations__) + +This prints ``{'a': "'str'"}``. This shouldn't really be considered +a "quirk"; it's mentioned here simply because it might be surprising. From b89feac7592c1dc2f8632f12fb1ff82c5602819f Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 5 Aug 2023 10:25:21 -0700 Subject: [PATCH 112/632] [3.11] gh-107662: Switch 'any' and 'anext' in functions.rst (GH-107663) (#107665) gh-107662: Switch 'any' and 'anext' in functions.rst (GH-107663) Order was reversed in index at top, not in body. (cherry picked from commit 9ebc6ecbc336d7b17cd158d1a4522f832df3e6e2) Co-authored-by: Terry Jan Reedy --- Doc/library/functions.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index e00d079ce43875..2468e53cd596a1 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -14,8 +14,8 @@ are always available. They are listed here in alphabetical order. | | :func:`abs` | | :func:`enumerate` | | :func:`len` | | |func-range|_ | | | :func:`aiter` | | :func:`eval` | | |func-list|_ | | :func:`repr` | | | :func:`all` | | :func:`exec` | | :func:`locals` | | :func:`reversed` | -| | :func:`any` | | | | | | :func:`round` | -| | :func:`anext` | | **F** | | **M** | | | +| | :func:`anext` | | | | | | :func:`round` | +| | :func:`any` | | **F** | | **M** | | | | | :func:`ascii` | | :func:`filter` | | :func:`map` | | **S** | | | | | :func:`float` | | :func:`max` | | |func-set|_ | | | **B** | | :func:`format` | | |func-memoryview|_ | | :func:`setattr` | From e4b5ec71fe44e4a4a393acf98075ab76be2bbb6a Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 5 Aug 2023 13:29:31 -0700 Subject: [PATCH 113/632] [3.11] Docs: Argument Clinic: Improve 'How to write a custom converter' (GH-107328) (#107670) - Omit unneccesary wording and sentences - Don't mention implementation details (no digression, explanation) (cherry picked from commit 4a5b4221e381c541f3f73537b7b87580d100158b) Co-authored-by: Erlend E. Aasland Co-authored-by: Ezio Melotti --- Doc/howto/clinic.rst | 50 +++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/Doc/howto/clinic.rst b/Doc/howto/clinic.rst index 913a3df7e3c48a..48c8f3c4e01c0f 100644 --- a/Doc/howto/clinic.rst +++ b/Doc/howto/clinic.rst @@ -1338,35 +1338,37 @@ state. Example from the ``setattro`` slot method in See also :pep:`573`. +.. _clinic-howto-custom-converter: + How to write a custom converter ------------------------------- -As we hinted at in the previous section... you can write your own converters! -A converter is simply a Python class that inherits from :py:class:`!CConverter`. -The main purpose of a custom converter is if you have a parameter using -the ``O&`` format unit—parsing this parameter means calling +A converter is a Python class that inherits from :py:class:`!CConverter`. +The main purpose of a custom converter, is for parameters parsed with +the ``O&`` format unit --- parsing such a parameter means calling a :c:func:`PyArg_ParseTuple` "converter function". -Your converter class should be named ``*something*_converter``. -If the name follows this convention, then your converter class -will be automatically registered with Argument Clinic; its name -will be the name of your class with the ``_converter`` suffix -stripped off. (This is accomplished with a metaclass.) - -You shouldn't subclass :py:meth:`!CConverter.__init__`. Instead, you should -write a :py:meth:`!converter_init` function. :py:meth:`!converter_init` -always accepts a *self* parameter; after that, all additional -parameters *must* be keyword-only. Any arguments passed in to -the converter in Argument Clinic will be passed along to your -:py:meth:`!converter_init`. +Your converter class should be named :samp:`{ConverterName}_converter`. +By following this convention, your converter class will be automatically +registered with Argument Clinic, with its *converter name* being the name of +your converter class with the ``_converter`` suffix stripped off. -There are some additional members of :py:class:`!CConverter` you may wish -to specify in your subclass. Here's the current list: +Instead of subclassing :py:meth:`!CConverter.__init__`, +write a :py:meth:`!converter_init` method. +Apart for the *self* parameter, all additional :py:meth:`!converter_init` +parameters **must** be keyword-only. +Any arguments passed to the converter in Argument Clinic +will be passed along to your :py:meth:`!converter_init` method. +See :py:class:`!CConverter` for a list of members you may wish to specify in +your subclass. .. module:: clinic .. class:: CConverter + The base class for all converters. + See :ref:`clinic-howto-custom-converter` for how to subclass this class. + .. attribute:: type The C type to use for this variable. @@ -1431,16 +1433,16 @@ Here's the simplest example of a custom converter, from :source:`Modules/zlibmod [python start generated code]*/ /*[python end generated code: output=da39a3ee5e6b4b0d input=35521e4e733823c7]*/ -This block adds a converter to Argument Clinic named ``ssize_t``. Parameters -declared as ``ssize_t`` will be declared as type :c:type:`Py_ssize_t`, and will -be parsed by the ``'O&'`` format unit, which will call the -``ssize_t_converter`` converter function. ``ssize_t`` variables -automatically support default values. +This block adds a converter named ``ssize_t`` to Argument Clinic. +Parameters declared as ``ssize_t`` will be declared with type :c:type:`Py_ssize_t`, +and will be parsed by the ``'O&'`` format unit, +which will call the :c:func:`!ssize_t_converter` converter C function. +``ssize_t`` variables automatically support default values. More sophisticated custom converters can insert custom C code to handle initialization and cleanup. You can see more examples of custom converters in the CPython -source tree; grep the C files for the string :py:class:`!CConverter`. +source tree; grep the C files for the string ``CConverter``. How to write a custom return converter From 58b31612e67eb9ef064acfee2b204095144a1408 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 6 Aug 2023 04:53:25 -0700 Subject: [PATCH 114/632] [3.11] Docs: skip python-docs-theme 2023.7 to fix mobile menu (GH-107666) (#107691) Docs: skip python-docs-theme 2023.7 to fix mobile menu (GH-107666) (cherry picked from commit 9641c4d8e2bdf9b00dd9f373d4a74dfad000afd1) Co-authored-by: Hugo van Kemenade --- Doc/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/requirements.txt b/Doc/requirements.txt index 2b8d4df214730f..3c0e0a7733673e 100644 --- a/Doc/requirements.txt +++ b/Doc/requirements.txt @@ -15,6 +15,6 @@ sphinxext-opengraph>=0.7.1 # The theme used by the documentation is stored separately, so we need # to install that as well. -python-docs-theme>=2023.7 +python-docs-theme>=2023.3.1,!=2023.7 -c constraints.txt From 2345a8fb0c8e660e2e3d63697e16433a312fce58 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 6 Aug 2023 07:06:16 -0700 Subject: [PATCH 115/632] [3.11] Do not use deprecated ``logger.warn()`` in pyspecific (GH-107694) (#107696) Do not use deprecated ``logger.warn()`` in pyspecific (GH-107694) (cherry picked from commit 9564e31cbc95a723f2414537231bc4611b56644f) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/tools/extensions/pyspecific.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/tools/extensions/pyspecific.py b/Doc/tools/extensions/pyspecific.py index 9104be01b722e1..f59331419e5083 100644 --- a/Doc/tools/extensions/pyspecific.py +++ b/Doc/tools/extensions/pyspecific.py @@ -185,7 +185,7 @@ def parse_platforms(self): if unknown: cls = type(self) logger = logging.getLogger(cls.__qualname__) - logger.warn( + logger.warning( f"Unknown platform(s) or syntax '{' '.join(sorted(unknown))}' " f"in '.. availability:: {self.arguments[0]}', see " f"{__file__}:{cls.__qualname__}.known_platforms for a set " @@ -272,7 +272,7 @@ def run(self): info = env.all_audit_events.setdefault(name, new_info) if info is not new_info: if not self._do_args_match(info['args'], new_info['args']): - self.logger.warn( + self.logger.warning( "Mismatched arguments for audit-event {}: {!r} != {!r}" .format(name, info['args'], new_info['args']) ) @@ -549,7 +549,7 @@ def write(self, *ignored): 'building topics... ', length=len(pydoc_topic_labels)): if label not in self.env.domaindata['std']['labels']: - self.env.logger.warn('label %r not in documentation' % label) + self.env.logger.warning(f'label {label!r} not in documentation') continue docname, labelid, sectname = self.env.domaindata['std']['labels'][label] doctree = self.env.get_and_resolve_doctree(docname, self) From 1878419ed86fdfa32f5bcc0d79139e46d91f9545 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 6 Aug 2023 14:20:24 -0700 Subject: [PATCH 116/632] [3.11] Improve cross-references in `runpy` docs (GH-107673) (#107699) Improve cross-references in `runpy` docs (GH-107673) - Add links to `__main__` and `sys.path` where appropriate - Ensure each paragraph never has more than one link to the same thing, to avoid visual clutter from too many links (cherry picked from commit 4e242d1ffb2d165443fe2680f7d1fef9fecbcfc0) Co-authored-by: Kamil Turek --- Doc/library/runpy.rst | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/Doc/library/runpy.rst b/Doc/library/runpy.rst index e161458040f896..4b37e40bfd6b84 100644 --- a/Doc/library/runpy.rst +++ b/Doc/library/runpy.rst @@ -39,7 +39,7 @@ The :mod:`runpy` module provides two functions: The *mod_name* argument should be an absolute module name. If the module name refers to a package rather than a normal - module, then that package is imported and the ``__main__`` submodule within + module, then that package is imported and the :mod:`__main__` submodule within that package is then executed and the resulting module globals dictionary returned. @@ -74,7 +74,7 @@ The :mod:`runpy` module provides two functions: Note that this manipulation of :mod:`sys` is not thread-safe. Other threads may see the partially initialised module, as well as the altered list of - arguments. It is recommended that the :mod:`sys` module be left alone when + arguments. It is recommended that the ``sys`` module be left alone when invoking this function from threaded code. .. seealso:: @@ -82,7 +82,7 @@ The :mod:`runpy` module provides two functions: command line. .. versionchanged:: 3.1 - Added ability to execute packages by looking for a ``__main__`` submodule. + Added ability to execute packages by looking for a :mod:`__main__` submodule. .. versionchanged:: 3.2 Added ``__cached__`` global variable (see :pep:`3147`). @@ -101,15 +101,16 @@ The :mod:`runpy` module provides two functions: Execute the code at the named filesystem location and return the resulting module globals dictionary. As with a script name supplied to the CPython command line, the supplied path may refer to a Python source file, a - compiled bytecode file or a valid sys.path entry containing a ``__main__`` - module (e.g. a zipfile containing a top-level ``__main__.py`` file). + compiled bytecode file or a valid :data:`sys.path` entry containing a + :mod:`__main__` module + (e.g. a zipfile containing a top-level ``__main__.py`` file). For a simple script, the specified code is simply executed in a fresh - module namespace. For a valid sys.path entry (typically a zipfile or + module namespace. For a valid :data:`sys.path` entry (typically a zipfile or directory), the entry is first added to the beginning of ``sys.path``. The function then looks for and executes a :mod:`__main__` module using the updated path. Note that there is no special protection against invoking - an existing :mod:`__main__` entry located elsewhere on ``sys.path`` if + an existing ``__main__`` entry located elsewhere on ``sys.path`` if there is no such module at the specified location. The optional dictionary argument *init_globals* may be used to pre-populate @@ -132,14 +133,14 @@ The :mod:`runpy` module provides two functions: supplied path, and ``__spec__``, ``__cached__``, ``__loader__`` and ``__package__`` will all be set to :const:`None`. - If the supplied path is a reference to a valid sys.path entry, then - ``__spec__`` will be set appropriately for the imported ``__main__`` + If the supplied path is a reference to a valid :data:`sys.path` entry, then + ``__spec__`` will be set appropriately for the imported :mod:`__main__` module (that is, ``__spec__.name`` will always be ``__main__``). ``__file__``, ``__cached__``, ``__loader__`` and ``__package__`` will be :ref:`set as normal ` based on the module spec. A number of alterations are also made to the :mod:`sys` module. Firstly, - ``sys.path`` may be altered as described above. ``sys.argv[0]`` is updated + :data:`sys.path` may be altered as described above. ``sys.argv[0]`` is updated with the value of ``path_name`` and ``sys.modules[__name__]`` is updated with a temporary module object for the module being executed. All modifications to items in :mod:`sys` are reverted before the function @@ -147,7 +148,7 @@ The :mod:`runpy` module provides two functions: Note that, unlike :func:`run_module`, the alterations made to :mod:`sys` are not optional in this function as these adjustments are essential to - allowing the execution of sys.path entries. As the thread-safety + allowing the execution of :data:`sys.path` entries. As the thread-safety limitations still apply, use of this function in threaded code should be either serialised with the import lock or delegated to a separate process. @@ -160,7 +161,7 @@ The :mod:`runpy` module provides two functions: .. versionchanged:: 3.4 Updated to take advantage of the module spec feature added by :pep:`451`. This allows ``__cached__`` to be set correctly in the - case where ``__main__`` is imported from a valid sys.path entry rather + case where ``__main__`` is imported from a valid :data:`sys.path` entry rather than being executed directly. .. seealso:: From d58c74c335bbd5904656b3fffd88fa3cdc67d92b Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 6 Aug 2023 17:17:41 -0700 Subject: [PATCH 117/632] [3.11] Docs: Argument Clinic: Move the CConverter class to the reference (GH-107671) (#107702) (cherry picked from commit a6675b1a597c67be972598ac8562883fabe48099) Co-authored-by: Erlend E. Aasland --- Doc/howto/clinic.rst | 133 ++++++++++++++++++++++--------------------- 1 file changed, 69 insertions(+), 64 deletions(-) diff --git a/Doc/howto/clinic.rst b/Doc/howto/clinic.rst index 48c8f3c4e01c0f..d75ba8a19cff2e 100644 --- a/Doc/howto/clinic.rst +++ b/Doc/howto/clinic.rst @@ -193,6 +193,71 @@ The CLI supports the following options: The list of files to process. +.. _clinic-classes: + +Classes for extending Argument Clinic +------------------------------------- + +.. module:: clinic + +.. class:: CConverter + + The base class for all converters. + See :ref:`clinic-howto-custom-converter` for how to subclass this class. + + .. attribute:: type + + The C type to use for this variable. + :attr:`!type` should be a Python string specifying the type, + e.g. ``'int'``. + If this is a pointer type, the type string should end with ``' *'``. + + .. attribute:: default + + The Python default value for this parameter, as a Python value. + Or the magic value ``unspecified`` if there is no default. + + .. attribute:: py_default + + :attr:`!default` as it should appear in Python code, + as a string. + Or ``None`` if there is no default. + + .. attribute:: c_default + + :attr:`!default` as it should appear in C code, + as a string. + Or ``None`` if there is no default. + + .. attribute:: c_ignored_default + + The default value used to initialize the C variable when + there is no default, but not specifying a default may + result in an "uninitialized variable" warning. This can + easily happen when using option groups—although + properly written code will never actually use this value, + the variable does get passed in to the impl, and the + C compiler will complain about the "use" of the + uninitialized value. This value should always be a + non-empty string. + + .. attribute:: converter + + The name of the C converter function, as a string. + + .. attribute:: impl_by_reference + + A boolean value. If true, + Argument Clinic will add a ``&`` in front of the name of + the variable when passing it into the impl function. + + .. attribute:: parse_by_reference + + A boolean value. If true, + Argument Clinic will add a ``&`` in front of the name of + the variable when passing it into :c:func:`PyArg_ParseTuple`. + + .. _clinic-tutorial: Tutorial @@ -1343,7 +1408,7 @@ See also :pep:`573`. How to write a custom converter ------------------------------- -A converter is a Python class that inherits from :py:class:`!CConverter`. +A converter is a Python class that inherits from :py:class:`CConverter`. The main purpose of a custom converter, is for parameters parsed with the ``O&`` format unit --- parsing such a parameter means calling a :c:func:`PyArg_ParseTuple` "converter function". @@ -1355,73 +1420,13 @@ your converter class with the ``_converter`` suffix stripped off. Instead of subclassing :py:meth:`!CConverter.__init__`, write a :py:meth:`!converter_init` method. -Apart for the *self* parameter, all additional :py:meth:`!converter_init` -parameters **must** be keyword-only. +:py:meth:`!converter_init` always accepts a *self* parameter. +After *self*, all additional parameters **must** be keyword-only. Any arguments passed to the converter in Argument Clinic will be passed along to your :py:meth:`!converter_init` method. -See :py:class:`!CConverter` for a list of members you may wish to specify in +See :py:class:`CConverter` for a list of members you may wish to specify in your subclass. -.. module:: clinic - -.. class:: CConverter - - The base class for all converters. - See :ref:`clinic-howto-custom-converter` for how to subclass this class. - - .. attribute:: type - - The C type to use for this variable. - :attr:`!type` should be a Python string specifying the type, - e.g. ``'int'``. - If this is a pointer type, the type string should end with ``' *'``. - - .. attribute:: default - - The Python default value for this parameter, as a Python value. - Or the magic value ``unspecified`` if there is no default. - - .. attribute:: py_default - - :attr:`!default` as it should appear in Python code, - as a string. - Or ``None`` if there is no default. - - .. attribute:: c_default - - :attr:`!default` as it should appear in C code, - as a string. - Or ``None`` if there is no default. - - .. attribute:: c_ignored_default - - The default value used to initialize the C variable when - there is no default, but not specifying a default may - result in an "uninitialized variable" warning. This can - easily happen when using option groups—although - properly written code will never actually use this value, - the variable does get passed in to the impl, and the - C compiler will complain about the "use" of the - uninitialized value. This value should always be a - non-empty string. - - .. attribute:: converter - - The name of the C converter function, as a string. - - .. attribute:: impl_by_reference - - A boolean value. If true, - Argument Clinic will add a ``&`` in front of the name of - the variable when passing it into the impl function. - - .. attribute:: parse_by_reference - - A boolean value. If true, - Argument Clinic will add a ``&`` in front of the name of - the variable when passing it into :c:func:`PyArg_ParseTuple`. - - Here's the simplest example of a custom converter, from :source:`Modules/zlibmodule.c`:: /*[python input] From 880670a34f664b1c93b7da79520a58040b68cabf Mon Sep 17 00:00:00 2001 From: Tomas R Date: Mon, 7 Aug 2023 16:42:19 +0200 Subject: [PATCH 118/632] [3.11] gh-107442: Document all valid types for ctypes `_as_parameter_` (GH-107443) (#107718) (cherry picked from commit 6925c578a0e3cbb00858e64da813a7ffe79623c4) Co-authored-by: Tomas R --- Doc/library/ctypes.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index b55ec32ef5af57..debc1c0adde841 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -399,9 +399,10 @@ Calling functions with your own custom data types ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ You can also customize :mod:`ctypes` argument conversion to allow instances of -your own classes be used as function arguments. :mod:`ctypes` looks for an -:attr:`_as_parameter_` attribute and uses this as the function argument. Of -course, it must be one of integer, string, or bytes:: +your own classes be used as function arguments. :mod:`ctypes` looks for an +:attr:`!_as_parameter_` attribute and uses this as the function argument. The +attribute must be an integer, string, bytes, a :mod:`ctypes` instance, or an +object with an :attr:`!_as_parameter_` attribute:: >>> class Bottles: ... def __init__(self, number): From 81c8f7d61982cf34506d6b21466f35f0184b472e Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 7 Aug 2023 17:48:43 +0300 Subject: [PATCH 119/632] [3.11] gh-104496: Use correct Tcl or Tk version in Tkinter tests (GH-107688) (GH-107719) In future Tcl and Tk versions can be desynchronized. (cherry picked from commit 3c8e8f3ceeae08fc43d885f5a4c65a3ee4b1a2c8) --- Lib/test/test_tcl.py | 13 ++----------- Lib/tkinter/test/support.py | 18 +++++++++--------- Lib/tkinter/test/test_tkinter/test_images.py | 6 +++--- Lib/tkinter/test/test_tkinter/test_widgets.py | 16 ++++++++-------- Lib/tkinter/test/test_ttk/test_style.py | 2 +- Lib/tkinter/test/test_ttk/test_widgets.py | 8 ++++---- Lib/tkinter/test/widget_tests.py | 6 +++--- 7 files changed, 30 insertions(+), 39 deletions(-) diff --git a/Lib/test/test_tcl.py b/Lib/test/test_tcl.py index cb3ab1d04435c6..8537bd56961b12 100644 --- a/Lib/test/test_tcl.py +++ b/Lib/test/test_tcl.py @@ -23,14 +23,6 @@ tcl_version = tuple(map(int, _tkinter.TCL_VERSION.split('.'))) -_tk_patchlevel = None -def get_tk_patchlevel(): - global _tk_patchlevel - if _tk_patchlevel is None: - tcl = Tcl() - _tk_patchlevel = tcl.info_patchlevel() - return _tk_patchlevel - class TkinterTest(unittest.TestCase): @@ -574,7 +566,6 @@ def test_splitlist(self): (1, '2', (3.4,)) if self.wantobjects else ('1', '2', '3.4')), ] - tk_patchlevel = get_tk_patchlevel() if not self.wantobjects: expected = ('12', '\u20ac', '\xe2\x82\xac', '3.4') else: @@ -583,8 +574,8 @@ def test_splitlist(self): (call('dict', 'create', 12, '\u20ac', b'\xe2\x82\xac', (3.4,)), expected), ] - dbg_info = ('want objects? %s, Tcl version: %s, Tk patchlevel: %s' - % (self.wantobjects, tcl_version, tk_patchlevel)) + dbg_info = ('want objects? %s, Tcl version: %s, Tcl patchlevel: %s' + % (self.wantobjects, tcl_version, self.interp.info_patchlevel())) for arg, res in testcases: self.assertEqual(splitlist(arg), res, 'arg=%a, %s' % (arg, dbg_info)) diff --git a/Lib/tkinter/test/support.py b/Lib/tkinter/test/support.py index 9e26d04536f227..7f8e1e7078b077 100644 --- a/Lib/tkinter/test/support.py +++ b/Lib/tkinter/test/support.py @@ -80,28 +80,28 @@ def simulate_mouse_click(widget, x, y): import _tkinter tcl_version = tuple(map(int, _tkinter.TCL_VERSION.split('.'))) +tk_version = tuple(map(int, _tkinter.TK_VERSION.split('.'))) -def requires_tcl(*version): - if len(version) <= 2: - return unittest.skipUnless(tcl_version >= version, - 'requires Tcl version >= ' + '.'.join(map(str, version))) +def requires_tk(*version): + if len(version) <= 2 and tk_version >= version: + return lambda test: test def deco(test): @functools.wraps(test) def newtest(self): - if get_tk_patchlevel() < version: - self.skipTest('requires Tcl version >= ' + + root = getattr(self, 'root', None) + if get_tk_patchlevel(root) < version: + self.skipTest('requires Tk version >= ' + '.'.join(map(str, version))) test(self) return newtest return deco _tk_patchlevel = None -def get_tk_patchlevel(): +def get_tk_patchlevel(root): global _tk_patchlevel if _tk_patchlevel is None: - tcl = tkinter.Tcl() - _tk_patchlevel = tcl.info_patchlevel() + _tk_patchlevel = tkinter._parse_version(root.tk.globalgetvar('tk_patchLevel')) return _tk_patchlevel units = { diff --git a/Lib/tkinter/test/test_tkinter/test_images.py b/Lib/tkinter/test/test_tkinter/test_images.py index cc69ccac62d742..8b473cce5f9947 100644 --- a/Lib/tkinter/test/test_tkinter/test_images.py +++ b/Lib/tkinter/test/test_tkinter/test_images.py @@ -2,7 +2,7 @@ import tkinter from test import support from test.support import os_helper -from tkinter.test.support import AbstractTkTest, AbstractDefaultRootTest, requires_tcl +from tkinter.test.support import AbstractTkTest, AbstractDefaultRootTest, requires_tk support.requires('gui') @@ -213,11 +213,11 @@ def test_create_from_gif_file(self): def test_create_from_gif_data(self): self.check_create_from_data('gif') - @requires_tcl(8, 6) + @requires_tk(8, 6) def test_create_from_png_file(self): self.check_create_from_file('png') - @requires_tcl(8, 6) + @requires_tk(8, 6) def test_create_from_png_data(self): self.check_create_from_data('png') diff --git a/Lib/tkinter/test/test_tkinter/test_widgets.py b/Lib/tkinter/test/test_tkinter/test_widgets.py index 2a5913521fbe13..24d00b714ad3da 100644 --- a/Lib/tkinter/test/test_tkinter/test_widgets.py +++ b/Lib/tkinter/test/test_tkinter/test_widgets.py @@ -4,7 +4,7 @@ import os from test.support import requires -from tkinter.test.support import (requires_tcl, +from tkinter.test.support import (requires_tk, get_tk_patchlevel, widget_eq, AbstractDefaultRootTest) from tkinter.test.widget_tests import ( @@ -614,7 +614,7 @@ def test_configure_inactiveselectbackground(self): widget = self.create() self.checkColorParam(widget, 'inactiveselectbackground') - @requires_tcl(8, 6) + @requires_tk(8, 6) def test_configure_insertunfocussed(self): widget = self.create() self.checkEnumParam(widget, 'insertunfocussed', @@ -919,7 +919,7 @@ def test_coords(self): for i in range(4): self.assertIsInstance(coords[i], float) - @requires_tcl(8, 6) + @requires_tk(8, 6) def test_moveto(self): widget = self.create() i1 = widget.create_rectangle(1, 1, 20, 20, tags='group') @@ -964,7 +964,7 @@ def test_configure_activestyle(self): self.checkEnumParam(widget, 'activestyle', 'dotbox', 'none', 'underline') - test_configure_justify = requires_tcl(8, 6, 5)(StandardOptionsTests.test_configure_justify) + test_configure_justify = requires_tk(8, 6, 5)(StandardOptionsTests.test_configure_justify) def test_configure_listvariable(self): widget = self.create() @@ -1103,7 +1103,7 @@ def test_configure_digits(self): def test_configure_from(self): widget = self.create() - conv = float if get_tk_patchlevel() >= (8, 6, 10) else float_round + conv = float if get_tk_patchlevel(self.root) >= (8, 6, 10) else float_round self.checkFloatParam(widget, 'from', 100, 14.9, 15.1, conv=conv) def test_configure_label(self): @@ -1230,19 +1230,19 @@ def test_configure_opaqueresize(self): widget = self.create() self.checkBooleanParam(widget, 'opaqueresize') - @requires_tcl(8, 6, 5) + @requires_tk(8, 6, 5) def test_configure_proxybackground(self): widget = self.create() self.checkColorParam(widget, 'proxybackground') - @requires_tcl(8, 6, 5) + @requires_tk(8, 6, 5) def test_configure_proxyborderwidth(self): widget = self.create() self.checkPixelsParam(widget, 'proxyborderwidth', 0, 1.3, 2.9, 6, -2, '10p', conv=False) - @requires_tcl(8, 6, 5) + @requires_tk(8, 6, 5) def test_configure_proxyrelief(self): widget = self.create() self.checkReliefParam(widget, 'proxyrelief') diff --git a/Lib/tkinter/test/test_ttk/test_style.py b/Lib/tkinter/test/test_ttk/test_style.py index 54ad3437168fe1..f94adc41f4df8b 100644 --- a/Lib/tkinter/test/test_ttk/test_style.py +++ b/Lib/tkinter/test/test_ttk/test_style.py @@ -170,7 +170,7 @@ def test_map_custom_copy(self): newname = f'C.{name}' self.assertEqual(style.map(newname), {}) style.map(newname, **default) - if theme == 'alt' and name == '.' and get_tk_patchlevel() < (8, 6, 1): + if theme == 'alt' and name == '.' and get_tk_patchlevel(self.root) < (8, 6, 1): default['embossed'] = [('disabled', '1')] self.assertEqual(style.map(newname), default) for key, value in default.items(): diff --git a/Lib/tkinter/test/test_ttk/test_widgets.py b/Lib/tkinter/test/test_ttk/test_widgets.py index 96d2afcf90ea81..1b9499e018f78b 100644 --- a/Lib/tkinter/test/test_ttk/test_widgets.py +++ b/Lib/tkinter/test/test_ttk/test_widgets.py @@ -5,7 +5,7 @@ import sys from test.test_ttk_textonly import MockTclObj -from tkinter.test.support import (AbstractTkTest, tcl_version, get_tk_patchlevel, +from tkinter.test.support import (AbstractTkTest, tk_version, get_tk_patchlevel, simulate_mouse_click, AbstractDefaultRootTest) from tkinter.test.widget_tests import (add_standard_options, AbstractWidgetTest, StandardOptionsTests, IntegerSizeTests, PixelSizeTests, @@ -20,7 +20,7 @@ def test_configure_class(self): widget = self.create() self.assertEqual(widget['class'], '') errmsg='attempt to change read-only option' - if get_tk_patchlevel() < (8, 6, 0, 'beta', 3): + if get_tk_patchlevel(self.root) < (8, 6, 0, 'beta', 3): errmsg='Attempt to change read-only option' self.checkInvalidParam(widget, 'class', 'Foo', errmsg=errmsg) widget2 = self.create(class_='Foo') @@ -562,7 +562,7 @@ def test_configure_orient(self): widget = self.create() self.assertEqual(str(widget['orient']), 'vertical') errmsg='attempt to change read-only option' - if get_tk_patchlevel() < (8, 6, 0, 'beta', 3): + if get_tk_patchlevel(self.root) < (8, 6, 0, 'beta', 3): errmsg='Attempt to change read-only option' self.checkInvalidParam(widget, 'orient', 'horizontal', errmsg=errmsg) @@ -1528,7 +1528,7 @@ def test_heading(self): def test_heading_callback(self): def simulate_heading_click(x, y): - if tcl_version >= (8, 6): + if tk_version >= (8, 6): self.assertEqual(self.tv.identify_column(x), '#0') self.assertEqual(self.tv.identify_region(x, y), 'heading') simulate_mouse_click(self.tv, x, y) diff --git a/Lib/tkinter/test/widget_tests.py b/Lib/tkinter/test/widget_tests.py index a450544c3ee6b8..1beb446cf27d0b 100644 --- a/Lib/tkinter/test/widget_tests.py +++ b/Lib/tkinter/test/widget_tests.py @@ -2,7 +2,7 @@ import unittest import tkinter -from tkinter.test.support import (AbstractTkTest, tcl_version, +from tkinter.test.support import (AbstractTkTest, tk_version, pixels_conv, tcl_obj_eq) import test.support @@ -23,7 +23,7 @@ def scaling(self): return self._scaling def _str(self, value): - if not self._stringify and self.wantobjects and tcl_version >= (8, 6): + if not self._stringify and self.wantobjects and tk_version >= (8, 6): return value if isinstance(value, tuple): return ' '.join(map(self._str, value)) @@ -157,7 +157,7 @@ def checkReliefParam(self, widget, name): 'flat', 'groove', 'raised', 'ridge', 'solid', 'sunken') errmsg='bad relief "spam": must be '\ 'flat, groove, raised, ridge, solid, or sunken' - if tcl_version < (8, 6): + if tk_version < (8, 6): errmsg = None self.checkInvalidParam(widget, name, 'spam', errmsg=errmsg) From 22b39d13eccb965515e3a4b3fd358755f8db9d7f Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 7 Aug 2023 23:51:00 +0300 Subject: [PATCH 120/632] [3.11] gh-107735: Add C API tests for PySys_GetObject() and PySys_SetObject() (GH-107736) (GH-107741) (cherry picked from commit bea5f93196d213d6fbf4ba8984caf4c3cd1da882) --- Lib/test/test_capi/test_misc.py | 43 +++++++++++++++++++++++++++++++++ Modules/_testcapimodule.c | 41 +++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 3e36fbde8c66d1..341b3b79091ae4 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -3,6 +3,7 @@ from collections import OrderedDict import _thread +import contextlib import importlib.machinery import importlib.util import os @@ -40,6 +41,8 @@ Py_DEBUG = hasattr(sys, 'gettotalrefcount') +NULL = None + def decode_stderr(err): return err.decode('utf-8', 'replace').replace('\r', '') @@ -910,6 +913,46 @@ def some(): with self.assertRaises(SystemError): _testcapi.function_get_module(None) # not a function + def test_sys_getobject(self): + getobject = _testcapi.sys_getobject + + self.assertIs(getobject(b'stdout'), sys.stdout) + with support.swap_attr(sys, '\U0001f40d', 42): + self.assertEqual(getobject('\U0001f40d'.encode()), 42) + + self.assertIs(getobject(b'nonexisting'), AttributeError) + self.assertIs(getobject(b'\xff'), AttributeError) + # CRASHES getobject(NULL) + + def test_sys_setobject(self): + setobject = _testcapi.sys_setobject + + value = ['value'] + value2 = ['value2'] + try: + self.assertEqual(setobject(b'newattr', value), 0) + self.assertIs(sys.newattr, value) + self.assertEqual(setobject(b'newattr', value2), 0) + self.assertIs(sys.newattr, value2) + self.assertEqual(setobject(b'newattr', NULL), 0) + self.assertFalse(hasattr(sys, 'newattr')) + self.assertEqual(setobject(b'newattr', NULL), 0) + finally: + with contextlib.suppress(AttributeError): + del sys.newattr + try: + self.assertEqual(setobject('\U0001f40d'.encode(), value), 0) + self.assertIs(getattr(sys, '\U0001f40d'), value) + self.assertEqual(setobject('\U0001f40d'.encode(), NULL), 0) + self.assertFalse(hasattr(sys, '\U0001f40d')) + finally: + with contextlib.suppress(AttributeError): + delattr(sys, '\U0001f40d') + + with self.assertRaises(UnicodeDecodeError): + setobject(b'\xff', value) + # CRASHES setobject(NULL, value) + class TestPendingCalls(unittest.TestCase): diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index b29a919a8325b6..5c00b48001a919 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -44,6 +44,16 @@ # error "The public headers should not include , see bpo-46748" #endif +#define NULLABLE(x) do { if (x == Py_None) x = NULL; } while (0); + +#define RETURN_INT(value) do { \ + int _ret = (value); \ + if (_ret == -1) { \ + return NULL; \ + } \ + return PyLong_FromLong(_ret); \ + } while (0) + // Forward declarations static struct PyModuleDef _testcapimodule; static PyType_Spec HeapTypeNameType_Spec; @@ -6449,6 +6459,35 @@ static PyObject *getargs_s_hash_int(PyObject *, PyObject *, PyObject*); static PyObject *getargs_s_hash_int2(PyObject *, PyObject *, PyObject*); static PyObject *gh_99240_clear_args(PyObject *, PyObject *); +static PyObject * +sys_getobject(PyObject *Py_UNUSED(module), PyObject *arg) +{ + const char *name; + Py_ssize_t size; + if (!PyArg_Parse(arg, "z#", &name, &size)) { + return NULL; + } + PyObject *result = PySys_GetObject(name); + if (result == NULL) { + result = PyExc_AttributeError; + } + return Py_NewRef(result); +} + +static PyObject * +sys_setobject(PyObject *Py_UNUSED(module), PyObject *args) +{ + const char *name; + Py_ssize_t size; + PyObject *value; + if (!PyArg_ParseTuple(args, "z#O", &name, &size, &value)) { + return NULL; + } + NULLABLE(value); + RETURN_INT(PySys_SetObject(name, value)); +} + + static PyMethodDef TestMethods[] = { {"exc_set_object", exc_set_object, METH_VARARGS}, {"raise_exception", raise_exception, METH_VARARGS}, @@ -6761,6 +6800,8 @@ static PyMethodDef TestMethods[] = { {"function_get_code", function_get_code, METH_O, NULL}, {"function_get_globals", function_get_globals, METH_O, NULL}, {"function_get_module", function_get_module, METH_O, NULL}, + {"sys_getobject", sys_getobject, METH_O}, + {"sys_setobject", sys_setobject, METH_VARARGS}, {NULL, NULL} /* sentinel */ }; From 951320e4d0f498857d0f78b3dbd0ee353bc4b93c Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 7 Aug 2023 16:17:12 -0600 Subject: [PATCH 121/632] [3.11] gh-98154: Clarify Usage of "Reference Count" In the Docs (gh-107753) PEP 683 (immortal objects) revealed some ways in which the Python documentation has been unnecessarily coupled to the implementation details of reference counts. In the end users should focus on reference ownership, including taking references and releasing them, rather than on how many reference counts an object has. This change updates the documentation to reflect that perspective. --- Doc/c-api/allocation.rst | 13 +++++---- Doc/c-api/arg.rst | 17 +++++++---- Doc/c-api/buffer.rst | 7 +++-- Doc/c-api/bytes.rst | 4 +-- Doc/c-api/exceptions.rst | 3 +- Doc/c-api/intro.rst | 59 ++++++++++++++++++++++----------------- Doc/c-api/module.rst | 2 +- Doc/c-api/object.rst | 13 +++++---- Doc/c-api/refcounting.rst | 47 ++++++++++++++++++++----------- Doc/c-api/sys.rst | 5 ++-- Doc/c-api/typeobj.rst | 10 ++++--- Doc/c-api/unicode.rst | 12 ++++---- Doc/glossary.rst | 11 +++++--- Doc/library/sys.rst | 3 ++ 14 files changed, 123 insertions(+), 83 deletions(-) diff --git a/Doc/c-api/allocation.rst b/Doc/c-api/allocation.rst index 44747e29643661..b3609c233156b6 100644 --- a/Doc/c-api/allocation.rst +++ b/Doc/c-api/allocation.rst @@ -29,12 +29,13 @@ Allocating Objects on the Heap .. c:macro:: PyObject_New(TYPE, typeobj) - Allocate a new Python object using the C structure type *TYPE* and the - Python type object *typeobj* (``PyTypeObject*``). - Fields not defined by the Python object header - are not initialized; the object's reference count will be one. The size of - the memory allocation is determined from the :c:member:`~PyTypeObject.tp_basicsize` field of - the type object. + Allocate a new Python object using the C structure type *TYPE* + and the Python type object *typeobj* (``PyTypeObject*``). + Fields not defined by the Python object header are not initialized. + The caller will own the only reference to the object + (i.e. its reference count will be one). + The size of the memory allocation is determined from the + :c:member:`~PyTypeObject.tp_basicsize` field of the type object. .. c:macro:: PyObject_NewVar(TYPE, typeobj, size) diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst index dfbec82c457bc3..08d6cf788e299e 100644 --- a/Doc/c-api/arg.rst +++ b/Doc/c-api/arg.rst @@ -330,8 +330,10 @@ Other objects ``O`` (object) [PyObject \*] Store a Python object (without any conversion) in a C object pointer. The C - program thus receives the actual object that was passed. The object's reference - count is not increased. The pointer stored is not ``NULL``. + program thus receives the actual object that was passed. A new + :term:`strong reference` to the object is not created + (i.e. its reference count is not increased). + The pointer stored is not ``NULL``. ``O!`` (object) [*typeobject*, PyObject \*] Store a Python object in a C object pointer. This is similar to ``O``, but @@ -415,7 +417,8 @@ inside nested parentheses. They are: mutually exclude each other. Note that any Python object references which are provided to the caller are -*borrowed* references; do not decrement their reference count! +*borrowed* references; do not release them +(i.e. do not decrement their reference count)! Additional arguments passed to these functions must be addresses of variables whose type is determined by the format string; these are used to store values @@ -650,8 +653,10 @@ Building values Convert a C :c:type:`Py_complex` structure to a Python complex number. ``O`` (object) [PyObject \*] - Pass a Python object untouched (except for its reference count, which is - incremented by one). If the object passed in is a ``NULL`` pointer, it is assumed + Pass a Python object untouched but create a new + :term:`strong reference` to it + (i.e. its reference count is incremented by one). + If the object passed in is a ``NULL`` pointer, it is assumed that this was caused because the call producing the argument found an error and set an exception. Therefore, :c:func:`Py_BuildValue` will return ``NULL`` but won't raise an exception. If no exception has been raised yet, :exc:`SystemError` is @@ -661,7 +666,7 @@ Building values Same as ``O``. ``N`` (object) [PyObject \*] - Same as ``O``, except it doesn't increment the reference count on the object. + Same as ``O``, except it doesn't create a new :term:`strong reference`. Useful when the object is created by a call to an object constructor in the argument list. diff --git a/Doc/c-api/buffer.rst b/Doc/c-api/buffer.rst index 02b53ec149c733..8ca1c190dab9a9 100644 --- a/Doc/c-api/buffer.rst +++ b/Doc/c-api/buffer.rst @@ -102,7 +102,9 @@ a buffer, see :c:func:`PyObject_GetBuffer`. .. c:member:: PyObject *obj A new reference to the exporting object. The reference is owned by - the consumer and automatically decremented and set to ``NULL`` by + the consumer and automatically released + (i.e. reference count decremented) + and set to ``NULL`` by :c:func:`PyBuffer_Release`. The field is the equivalent of the return value of any standard C-API function. @@ -454,7 +456,8 @@ Buffer-related functions .. c:function:: void PyBuffer_Release(Py_buffer *view) - Release the buffer *view* and decrement the reference count for + Release the buffer *view* and release the :term:`strong reference` + (i.e. decrement the reference count) to the view's supporting object, ``view->obj``. This function MUST be called when the buffer is no longer being used, otherwise reference leaks may occur. diff --git a/Doc/c-api/bytes.rst b/Doc/c-api/bytes.rst index 110a98e19d8237..33b7d23ff85a3d 100644 --- a/Doc/c-api/bytes.rst +++ b/Doc/c-api/bytes.rst @@ -187,8 +187,8 @@ called with a non-bytes parameter. .. c:function:: void PyBytes_ConcatAndDel(PyObject **bytes, PyObject *newpart) Create a new bytes object in *\*bytes* containing the contents of *newpart* - appended to *bytes*. This version decrements the reference count of - *newpart*. + appended to *bytes*. This version releases the :term:`strong reference` + to *newpart* (i.e. decrements its reference count). .. c:function:: int _PyBytes_Resize(PyObject **bytes, Py_ssize_t newsize) diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst index 8d8b0ac7dd9a93..ae45aaef238fec 100644 --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -99,7 +99,8 @@ For convenience, some of these functions will always return a This is the most common way to set the error indicator. The first argument specifies the exception type; it is normally one of the standard exceptions, - e.g. :c:data:`PyExc_RuntimeError`. You need not increment its reference count. + e.g. :c:data:`PyExc_RuntimeError`. You need not create a new + :term:`strong reference` to it (e.g. with :c:func:`Py_INCREF`). The second argument is an error message; it is decoded from ``'utf-8'``. diff --git a/Doc/c-api/intro.rst b/Doc/c-api/intro.rst index a3ccfa08baf706..ec2d8bc0ef02ef 100644 --- a/Doc/c-api/intro.rst +++ b/Doc/c-api/intro.rst @@ -287,52 +287,58 @@ true if (and only if) the object pointed to by *a* is a Python list. Reference Counts ---------------- -The reference count is important because today's computers have a finite (and -often severely limited) memory size; it counts how many different places there -are that have a reference to an object. Such a place could be another object, -or a global (or static) C variable, or a local variable in some C function. -When an object's reference count becomes zero, the object is deallocated. If -it contains references to other objects, their reference count is decremented. -Those other objects may be deallocated in turn, if this decrement makes their -reference count become zero, and so on. (There's an obvious problem with -objects that reference each other here; for now, the solution is "don't do -that.") +The reference count is important because today's computers have a finite +(and often severely limited) memory size; it counts how many different +places there are that have a :term:`strong reference` to an object. +Such a place could be another object, or a global (or static) C variable, +or a local variable in some C function. +When the last :term:`strong reference` to an object is released +(i.e. its reference count becomes zero), the object is deallocated. +If it contains references to other objects, those references are released. +Those other objects may be deallocated in turn, if there are no more +references to them, and so on. (There's an obvious problem with +objects that reference each other here; for now, the solution +is "don't do that.") .. index:: single: Py_INCREF() single: Py_DECREF() -Reference counts are always manipulated explicitly. The normal way is to use -the macro :c:func:`Py_INCREF` to increment an object's reference count by one, -and :c:func:`Py_DECREF` to decrement it by one. The :c:func:`Py_DECREF` macro +Reference counts are always manipulated explicitly. The normal way is +to use the macro :c:func:`Py_INCREF` to take a new reference to an +object (i.e. increment its reference count by one), +and :c:func:`Py_DECREF` to release that reference (i.e. decrement the +reference count by one). The :c:func:`Py_DECREF` macro is considerably more complex than the incref one, since it must check whether the reference count becomes zero and then cause the object's deallocator to be -called. The deallocator is a function pointer contained in the object's type -structure. The type-specific deallocator takes care of decrementing the -reference counts for other objects contained in the object if this is a compound +called. The deallocator is a function pointer contained in the object's type +structure. The type-specific deallocator takes care of releasing references +for other objects contained in the object if this is a compound object type, such as a list, as well as performing any additional finalization that's needed. There's no chance that the reference count can overflow; at least as many bits are used to hold the reference count as there are distinct memory locations in virtual memory (assuming ``sizeof(Py_ssize_t) >= sizeof(void*)``). Thus, the reference count increment is a simple operation. -It is not necessary to increment an object's reference count for every local -variable that contains a pointer to an object. In theory, the object's +It is not necessary to hold a :term:`strong reference` (i.e. increment +the reference count) for every local variable that contains a pointer +to an object. In theory, the object's reference count goes up by one when the variable is made to point to it and it goes down by one when the variable goes out of scope. However, these two cancel each other out, so at the end the reference count hasn't changed. The only real reason to use the reference count is to prevent the object from being deallocated as long as our variable is pointing to it. If we know that there is at least one other reference to the object that lives at least as long as -our variable, there is no need to increment the reference count temporarily. +our variable, there is no need to take a new :term:`strong reference` +(i.e. increment the reference count) temporarily. An important situation where this arises is in objects that are passed as arguments to C functions in an extension module that are called from Python; the call mechanism guarantees to hold a reference to every argument for the duration of the call. However, a common pitfall is to extract an object from a list and hold on to it -for a while without incrementing its reference count. Some other operation might -conceivably remove the object from the list, decrementing its reference count +for a while without taking a new reference. Some other operation might +conceivably remove the object from the list, releasing that reference, and possibly deallocating it. The real danger is that innocent-looking operations may invoke arbitrary Python code which could do this; there is a code path which allows control to flow back to the user from a :c:func:`Py_DECREF`, so @@ -340,7 +346,8 @@ almost any operation is potentially dangerous. A safe approach is to always use the generic operations (functions whose name begins with ``PyObject_``, ``PyNumber_``, ``PySequence_`` or ``PyMapping_``). -These operations always increment the reference count of the object they return. +These operations always create a new :term:`strong reference` +(i.e. increment the reference count) of the object they return. This leaves the caller with the responsibility to call :c:func:`Py_DECREF` when they are done with the result; this soon becomes second nature. @@ -356,7 +363,7 @@ to objects (objects are not owned: they are always shared). "Owning a reference" means being responsible for calling Py_DECREF on it when the reference is no longer needed. Ownership can also be transferred, meaning that the code that receives ownership of the reference then becomes responsible for -eventually decref'ing it by calling :c:func:`Py_DECREF` or :c:func:`Py_XDECREF` +eventually releasing it by calling :c:func:`Py_DECREF` or :c:func:`Py_XDECREF` when it's no longer needed---or passing on this responsibility (usually to its caller). When a function passes ownership of a reference on to its caller, the caller is said to receive a *new* reference. When no ownership is transferred, @@ -414,9 +421,9 @@ For example, the above two blocks of code could be replaced by the following It is much more common to use :c:func:`PyObject_SetItem` and friends with items whose references you are only borrowing, like arguments that were passed in to -the function you are writing. In that case, their behaviour regarding reference -counts is much saner, since you don't have to increment a reference count so you -can give a reference away ("have it be stolen"). For example, this function +the function you are writing. In that case, their behaviour regarding references +is much saner, since you don't have to take a new reference just so you +can give that reference away ("have it be stolen"). For example, this function sets all items of a list (actually, any mutable sequence) to a given item:: int diff --git a/Doc/c-api/module.rst b/Doc/c-api/module.rst index 8ca48c852d4e6f..6ef4eea6a07f73 100644 --- a/Doc/c-api/module.rst +++ b/Doc/c-api/module.rst @@ -498,7 +498,7 @@ state: .. note:: Unlike other functions that steal references, ``PyModule_AddObject()`` - only decrements the reference count of *value* **on success**. + only releases the reference to *value* **on success**. This means that its return value must be checked, and calling code must :c:func:`Py_DECREF` *value* manually on error. diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index e7d80dbf8c5c13..bc67fb60dc8bec 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -15,8 +15,8 @@ Object Protocol .. c:macro:: Py_RETURN_NOTIMPLEMENTED Properly handle returning :c:data:`Py_NotImplemented` from within a C - function (that is, increment the reference count of NotImplemented and - return it). + function (that is, create a new :term:`strong reference` + to NotImplemented and return it). .. c:function:: int PyObject_Print(PyObject *o, FILE *fp, int flags) @@ -320,11 +320,12 @@ Object Protocol When *o* is non-``NULL``, returns a type object corresponding to the object type of object *o*. On failure, raises :exc:`SystemError` and returns ``NULL``. This - is equivalent to the Python expression ``type(o)``. This function increments the - reference count of the return value. There's really no reason to use this + is equivalent to the Python expression ``type(o)``. + This function creates a new :term:`strong reference` to the return value. + There's really no reason to use this function instead of the :c:func:`Py_TYPE()` function, which returns a - pointer of type :c:expr:`PyTypeObject*`, except when the incremented reference - count is needed. + pointer of type :c:expr:`PyTypeObject*`, except when a new + :term:`strong reference` is needed. .. c:function:: int PyObject_TypeCheck(PyObject *o, PyTypeObject *type) diff --git a/Doc/c-api/refcounting.rst b/Doc/c-api/refcounting.rst index af25e8bb913685..e1782ab27f4f66 100644 --- a/Doc/c-api/refcounting.rst +++ b/Doc/c-api/refcounting.rst @@ -13,31 +13,36 @@ objects. .. c:function:: void Py_INCREF(PyObject *o) - Increment the reference count for object *o*. + Indicate taking a new :term:`strong reference` to object *o*, + indicating it is in use and should not be destroyed. This function is usually used to convert a :term:`borrowed reference` to a :term:`strong reference` in-place. The :c:func:`Py_NewRef` function can be used to create a new :term:`strong reference`. + When done using the object, release it by calling :c:func:`Py_DECREF`. + The object must not be ``NULL``; if you aren't sure that it isn't ``NULL``, use :c:func:`Py_XINCREF`. + Do not expect this function to actually modify *o* in any way. + .. c:function:: void Py_XINCREF(PyObject *o) - Increment the reference count for object *o*. The object may be ``NULL``, in - which case the macro has no effect. + Similar to :c:func:`Py_INCREF`, but the object *o* can be ``NULL``, + in which case this has no effect. See also :c:func:`Py_XNewRef`. .. c:function:: PyObject* Py_NewRef(PyObject *o) - Create a new :term:`strong reference` to an object: increment the reference - count of the object *o* and return the object *o*. + Create a new :term:`strong reference` to an object: + call :c:func:`Py_INCREF` on *o* and return the object *o*. When the :term:`strong reference` is no longer needed, :c:func:`Py_DECREF` - should be called on it to decrement the object reference count. + should be called on it to release the reference. The object *o* must not be ``NULL``; use :c:func:`Py_XNewRef` if *o* can be ``NULL``. @@ -67,9 +72,12 @@ objects. .. c:function:: void Py_DECREF(PyObject *o) - Decrement the reference count for object *o*. + Release a :term:`strong reference` to object *o*, indicating the + reference is no longer used. - If the reference count reaches zero, the object's type's deallocation + Once the last :term:`strong reference` is released + (i.e. the object's reference count reaches 0), + the object's type's deallocation function (which must not be ``NULL``) is invoked. This function is usually used to delete a :term:`strong reference` before @@ -78,6 +86,8 @@ objects. The object must not be ``NULL``; if you aren't sure that it isn't ``NULL``, use :c:func:`Py_XDECREF`. + Do not expect this function to actually modify *o* in any way. + .. warning:: The deallocation function can cause arbitrary Python code to be invoked (e.g. @@ -92,32 +102,35 @@ objects. .. c:function:: void Py_XDECREF(PyObject *o) - Decrement the reference count for object *o*. The object may be ``NULL``, in - which case the macro has no effect; otherwise the effect is the same as for - :c:func:`Py_DECREF`, and the same warning applies. + Similar to :c:func:`Py_DECREF`, but the object *o* can be ``NULL``, + in which case this has no effect. + The same warning from :c:func:`Py_DECREF` applies here as well. .. c:function:: void Py_CLEAR(PyObject *o) - Decrement the reference count for object *o*. The object may be ``NULL``, in + Release a :term:`strong reference` for object *o*. + The object may be ``NULL``, in which case the macro has no effect; otherwise the effect is the same as for :c:func:`Py_DECREF`, except that the argument is also set to ``NULL``. The warning for :c:func:`Py_DECREF` does not apply with respect to the object passed because the macro carefully uses a temporary variable and sets the argument to ``NULL`` - before decrementing its reference count. + before releasing the reference. - It is a good idea to use this macro whenever decrementing the reference - count of an object that might be traversed during garbage collection. + It is a good idea to use this macro whenever releasing a reference + to an object that might be traversed during garbage collection. .. c:function:: void Py_IncRef(PyObject *o) - Increment the reference count for object *o*. A function version of :c:func:`Py_XINCREF`. + Indicate taking a new :term:`strong reference` to object *o*. + A function version of :c:func:`Py_XINCREF`. It can be used for runtime dynamic embedding of Python. .. c:function:: void Py_DecRef(PyObject *o) - Decrement the reference count for object *o*. A function version of :c:func:`Py_XDECREF`. + Release a :term:`strong reference` to object *o*. + A function version of :c:func:`Py_XDECREF`. It can be used for runtime dynamic embedding of Python. diff --git a/Doc/c-api/sys.rst b/Doc/c-api/sys.rst index 34028d6c87e97b..a811eb17b328ea 100644 --- a/Doc/c-api/sys.rst +++ b/Doc/c-api/sys.rst @@ -8,8 +8,9 @@ Operating System Utilities .. c:function:: PyObject* PyOS_FSPath(PyObject *path) Return the file system representation for *path*. If the object is a - :class:`str` or :class:`bytes` object, then its reference count is - incremented. If the object implements the :class:`os.PathLike` interface, + :class:`str` or :class:`bytes` object, then a new + :term:`strong reference` is returned. + If the object implements the :class:`os.PathLike` interface, then :meth:`~os.PathLike.__fspath__` is returned as long as it is a :class:`str` or :class:`bytes` object. Otherwise :exc:`TypeError` is raised and ``NULL`` is returned. diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 11f6034b9e330f..40cbcba63d69a6 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -688,7 +688,8 @@ and :c:data:`PyType_Type` effectively act as defaults.) } Finally, if the type is heap allocated (:c:macro:`Py_TPFLAGS_HEAPTYPE`), the - deallocator should decrement the reference count for its type object after + deallocator should release the owned reference to its type object + (via :c:func:`Py_DECREF`) after calling the type deallocator. In order to avoid dangling pointers, the recommended way to achieve this is: @@ -1397,9 +1398,10 @@ and :c:data:`PyType_Type` effectively act as defaults.) } The :c:func:`Py_CLEAR` macro should be used, because clearing references is - delicate: the reference to the contained object must not be decremented until + delicate: the reference to the contained object must not be released + (via :c:func:`Py_DECREF`) until after the pointer to the contained object is set to ``NULL``. This is because - decrementing the reference count may cause the contained object to become trash, + releasing the reference may cause the contained object to become trash, triggering a chain of reclamation activity that may include invoking arbitrary Python code (due to finalizers, or weakref callbacks, associated with the contained object). If it's possible for such code to reference *self* again, @@ -1477,7 +1479,7 @@ and :c:data:`PyType_Type` effectively act as defaults.) they may be C ints or floats). The third argument specifies the requested operation, as for :c:func:`PyObject_RichCompare`. - The return value's reference count is properly incremented. + The returned value is a new :term:`strong reference`. On error, sets an exception and returns ``NULL`` from the function. diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst index 8b4252f08dd4cd..3d6b4c49fa9c83 100644 --- a/Doc/c-api/unicode.rst +++ b/Doc/c-api/unicode.rst @@ -576,7 +576,7 @@ APIs: Copy an instance of a Unicode subtype to a new true Unicode object if necessary. If *obj* is already a true Unicode object (not a subtype), - return the reference with incremented refcount. + return a new :term:`strong reference` to the object. Objects other than Unicode or its subtypes will cause a :exc:`TypeError`. @@ -1537,11 +1537,11 @@ They all return ``NULL`` or ``-1`` if an exception occurs. Intern the argument *\*string* 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 - it (decrementing the reference count of the old string object and incrementing - the reference count of the interned string object), otherwise it leaves - *\*string* alone and interns it (incrementing its reference count). - (Clarification: even though there is a lot of talk about reference counts, think - of this function as reference-count-neutral; you own the object after the call + 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`). + (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.) diff --git a/Doc/glossary.rst b/Doc/glossary.rst index 5c0f0f15217a07..81599477fc9534 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -168,8 +168,9 @@ Glossary :class:`str` objects. borrowed reference - In Python's C API, a borrowed reference is a reference to an object. - It does not modify the object reference count. It becomes a dangling + In Python's C API, a borrowed reference is a reference to an object, + where the code using the object does not own the reference. + It becomes a dangling pointer if the object is destroyed. For example, a garbage collection can remove the last :term:`strong reference` to the object and so destroy it. @@ -1131,8 +1132,10 @@ Glossary strong reference In Python's C API, a strong reference is a reference to an object - which increments the object's reference count when it is created and - decrements the object's reference count when it is deleted. + which is owned by the code holding the reference. The strong + reference is taken by calling :c:func:`Py_INCREF` when the + reference is created and released with :c:func:`Py_DECREF` + when the reference is deleted. The :c:func:`Py_NewRef` function can be used to create a strong reference to an object. Usually, the :c:func:`Py_DECREF` function must be called on diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index b03860603c2862..641be05a16fb3c 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -759,6 +759,9 @@ always available. higher than you might expect, because it includes the (temporary) reference as an argument to :func:`getrefcount`. + Note that the returned value may not actually reflect how many + references to the object are actually held. Consequently, do not rely + on the returned value to be accurate, other than a value of 0 or 1. .. function:: getrecursionlimit() From 648d42643eeca18206825f0f4c58c58f71e0ae1a Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 8 Aug 2023 00:51:43 -0700 Subject: [PATCH 122/632] README: remove unmaintained sections (GH-107703) (cherry picked from commit 7a250fdc16bb6f1fe0a6b0df8bb502870405b5d6) --- README.rst | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/README.rst b/README.rst index e70800554784b2..5e492ff2d2f3e6 100644 --- a/README.rst +++ b/README.rst @@ -211,30 +211,6 @@ primary version, you would execute ``make install`` in your 3.11 build directory and ``make altinstall`` in the others. -Issue Tracker and Mailing List ------------------------------- - -Bug reports are welcome! You can use Github to `report bugs -`_, and/or `submit pull requests -`_. - -You can also follow development discussion on the `python-dev mailing list -`_. - - -Proposals for enhancement -------------------------- - -If you have a proposal to change Python, you may want to send an email to the -`comp.lang.python`_ or `python-ideas`_ mailing lists for initial feedback. A -Python Enhancement Proposal (PEP) may be submitted if your idea gains ground. -All current PEPs, as well as guidelines for submitting a new PEP, are listed at -`peps.python.org `_. - -.. _python-ideas: https://mail.python.org/mailman/listinfo/python-ideas/ -.. _comp.lang.python: https://mail.python.org/mailman/listinfo/python-list - - Release Schedule ---------------- From 0aa3b9d76c036fd306c0fd1c270d5d88d02962a1 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 8 Aug 2023 03:17:28 -0700 Subject: [PATCH 123/632] [3.11] GH-84435: Make pyspecific directives translatable (GH-19470) (#107682) GH-84435: Make pyspecific directives translatable (GH-19470) (cherry picked from commit ecb05e0b9842ba03b42b4dec8767b1c18a4e28b3) Co-authored-by: cocoatomo Co-authored-by: Jelle Zijlstra Co-authored-by: Adam Turner <9087854+aa-turner@users.noreply.github.com> --- Doc/tools/extensions/pyspecific.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/Doc/tools/extensions/pyspecific.py b/Doc/tools/extensions/pyspecific.py index f59331419e5083..d13a9dfd4c8eb8 100644 --- a/Doc/tools/extensions/pyspecific.py +++ b/Doc/tools/extensions/pyspecific.py @@ -103,14 +103,13 @@ class ImplementationDetail(Directive): final_argument_whitespace = True # This text is copied to templates/dummy.html - label_text = 'CPython implementation detail:' + label_text = sphinx_gettext('CPython implementation detail:') def run(self): self.assert_has_content() pnode = nodes.compound(classes=['impl-detail']) - label = sphinx_gettext(self.label_text) content = self.content - add_text = nodes.strong(label, label) + add_text = nodes.strong(self.label_text, self.label_text) self.state.nested_parse(content, self.content_offset, pnode) content = nodes.inline(pnode[0].rawsource, translatable=True) content.source = pnode[0].source @@ -239,9 +238,9 @@ class AuditEvent(Directive): final_argument_whitespace = True _label = [ - "Raises an :ref:`auditing event ` {name} with no arguments.", - "Raises an :ref:`auditing event ` {name} with argument {args}.", - "Raises an :ref:`auditing event ` {name} with arguments {args}.", + sphinx_gettext("Raises an :ref:`auditing event ` {name} with no arguments."), + sphinx_gettext("Raises an :ref:`auditing event ` {name} with argument {args}."), + sphinx_gettext("Raises an :ref:`auditing event ` {name} with arguments {args}."), ] @property @@ -257,7 +256,7 @@ def run(self): else: args = [] - label = sphinx_gettext(self._label[min(2, len(args))]) + label = self._label[min(2, len(args))] text = label.format(name="``{}``".format(name), args=", ".join("``{}``".format(a) for a in args if a)) @@ -419,8 +418,8 @@ class DeprecatedRemoved(Directive): final_argument_whitespace = True option_spec = {} - _deprecated_label = 'Deprecated since version {deprecated}, will be removed in version {removed}' - _removed_label = 'Deprecated since version {deprecated}, removed in version {removed}' + _deprecated_label = sphinx_gettext('Deprecated since version {deprecated}, will be removed in version {removed}') + _removed_label = sphinx_gettext('Deprecated since version {deprecated}, removed in version {removed}') def run(self): node = addnodes.versionmodified() @@ -436,7 +435,6 @@ def run(self): else: label = self._removed_label - label = sphinx_gettext(label) text = label.format(deprecated=self.arguments[0], removed=self.arguments[1]) if len(self.arguments) == 3: inodes, messages = self.state.inline_text(self.arguments[2], From edaa0db93ee23b1d936631dedde3adf5a1a0fb13 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 9 Aug 2023 09:12:02 +0300 Subject: [PATCH 124/632] [3.11] gh-86457: Fix signature for code.replace() (GH-23199) (GH-107746) Also add support of @text_signature in Argument Clinic. (cherry picked from commit 0e6e32fb84b2f7cb668e0b9927637587081e38cd) --- ...3-08-07-16-30-48.gh-issue-95065.-im4R5.rst | 2 + Objects/clinic/codeobject.c.h | 34 +-- Objects/codeobject.c | 58 ++-- Tools/clinic/clinic.py | 255 +++++++++--------- 4 files changed, 177 insertions(+), 172 deletions(-) create mode 100644 Misc/NEWS.d/next/Tools-Demos/2023-08-07-16-30-48.gh-issue-95065.-im4R5.rst diff --git a/Misc/NEWS.d/next/Tools-Demos/2023-08-07-16-30-48.gh-issue-95065.-im4R5.rst b/Misc/NEWS.d/next/Tools-Demos/2023-08-07-16-30-48.gh-issue-95065.-im4R5.rst new file mode 100644 index 00000000000000..4768e6767574d8 --- /dev/null +++ b/Misc/NEWS.d/next/Tools-Demos/2023-08-07-16-30-48.gh-issue-95065.-im4R5.rst @@ -0,0 +1,2 @@ +Argument Clinic now supports overriding automatically generated signature by +using directive ``@text_signature``. diff --git a/Objects/clinic/codeobject.c.h b/Objects/clinic/codeobject.c.h index df82524a86afe5..9bf9e14b17e51d 100644 --- a/Objects/clinic/codeobject.c.h +++ b/Objects/clinic/codeobject.c.h @@ -157,12 +157,7 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) } PyDoc_STRVAR(code_replace__doc__, -"replace($self, /, *, co_argcount=-1, co_posonlyargcount=-1,\n" -" co_kwonlyargcount=-1, co_nlocals=-1, co_stacksize=-1,\n" -" co_flags=-1, co_firstlineno=-1, co_code=None, co_consts=None,\n" -" co_names=None, co_varnames=None, co_freevars=None,\n" -" co_cellvars=None, co_filename=None, co_name=None,\n" -" co_qualname=None, co_linetable=None, co_exceptiontable=None)\n" +"replace($self, /, **changes)\n" "--\n" "\n" "Return a copy of the code object with new values for the specified fields."); @@ -174,13 +169,12 @@ static PyObject * code_replace_impl(PyCodeObject *self, int co_argcount, int co_posonlyargcount, int co_kwonlyargcount, int co_nlocals, int co_stacksize, int co_flags, - int co_firstlineno, PyBytesObject *co_code, - PyObject *co_consts, PyObject *co_names, - PyObject *co_varnames, PyObject *co_freevars, - PyObject *co_cellvars, PyObject *co_filename, - PyObject *co_name, PyObject *co_qualname, - PyBytesObject *co_linetable, - PyBytesObject *co_exceptiontable); + int co_firstlineno, PyObject *co_code, PyObject *co_consts, + PyObject *co_names, PyObject *co_varnames, + PyObject *co_freevars, PyObject *co_cellvars, + PyObject *co_filename, PyObject *co_name, + PyObject *co_qualname, PyObject *co_linetable, + PyObject *co_exceptiontable); static PyObject * code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -197,7 +191,7 @@ code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObje int co_stacksize = self->co_stacksize; int co_flags = self->co_flags; int co_firstlineno = self->co_firstlineno; - PyBytesObject *co_code = NULL; + PyObject *co_code = NULL; PyObject *co_consts = self->co_consts; PyObject *co_names = self->co_names; PyObject *co_varnames = NULL; @@ -206,8 +200,8 @@ code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObje PyObject *co_filename = self->co_filename; PyObject *co_name = self->co_name; PyObject *co_qualname = self->co_qualname; - PyBytesObject *co_linetable = (PyBytesObject *)self->co_linetable; - PyBytesObject *co_exceptiontable = (PyBytesObject *)self->co_exceptiontable; + PyObject *co_linetable = self->co_linetable; + PyObject *co_exceptiontable = self->co_exceptiontable; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); if (!args) { @@ -284,7 +278,7 @@ code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObje _PyArg_BadArgument("replace", "argument 'co_code'", "bytes", args[7]); goto exit; } - co_code = (PyBytesObject *)args[7]; + co_code = args[7]; if (!--noptargs) { goto skip_optional_kwonly; } @@ -383,7 +377,7 @@ code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObje _PyArg_BadArgument("replace", "argument 'co_linetable'", "bytes", args[16]); goto exit; } - co_linetable = (PyBytesObject *)args[16]; + co_linetable = args[16]; if (!--noptargs) { goto skip_optional_kwonly; } @@ -392,7 +386,7 @@ code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObje _PyArg_BadArgument("replace", "argument 'co_exceptiontable'", "bytes", args[17]); goto exit; } - co_exceptiontable = (PyBytesObject *)args[17]; + co_exceptiontable = args[17]; skip_optional_kwonly: return_value = code_replace_impl(self, co_argcount, co_posonlyargcount, co_kwonlyargcount, co_nlocals, co_stacksize, co_flags, co_firstlineno, co_code, co_consts, co_names, co_varnames, co_freevars, co_cellvars, co_filename, co_name, co_qualname, co_linetable, co_exceptiontable); @@ -436,4 +430,4 @@ code__varname_from_oparg(PyCodeObject *self, PyObject *const *args, Py_ssize_t n exit: return return_value; } -/*[clinic end generated code: output=9c521b6c79f90ff7 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=d1bbf51b746ca2d0 input=a9049054013a1b77]*/ diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 77ea4abf35dca9..c4a0d9ad1cf985 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -1875,27 +1875,28 @@ code_linesiterator(PyCodeObject *code, PyObject *Py_UNUSED(args)) } /*[clinic input] +@text_signature "($self, /, **changes)" code.replace * - co_argcount: int(c_default="self->co_argcount") = -1 - co_posonlyargcount: int(c_default="self->co_posonlyargcount") = -1 - co_kwonlyargcount: int(c_default="self->co_kwonlyargcount") = -1 - co_nlocals: int(c_default="self->co_nlocals") = -1 - co_stacksize: int(c_default="self->co_stacksize") = -1 - co_flags: int(c_default="self->co_flags") = -1 - co_firstlineno: int(c_default="self->co_firstlineno") = -1 - co_code: PyBytesObject(c_default="NULL") = None - co_consts: object(subclass_of="&PyTuple_Type", c_default="self->co_consts") = None - co_names: object(subclass_of="&PyTuple_Type", c_default="self->co_names") = None - co_varnames: object(subclass_of="&PyTuple_Type", c_default="NULL") = None - co_freevars: object(subclass_of="&PyTuple_Type", c_default="NULL") = None - co_cellvars: object(subclass_of="&PyTuple_Type", c_default="NULL") = None - co_filename: unicode(c_default="self->co_filename") = None - co_name: unicode(c_default="self->co_name") = None - co_qualname: unicode(c_default="self->co_qualname") = None - co_linetable: PyBytesObject(c_default="(PyBytesObject *)self->co_linetable") = None - co_exceptiontable: PyBytesObject(c_default="(PyBytesObject *)self->co_exceptiontable") = None + co_argcount: int(c_default="self->co_argcount") = unchanged + co_posonlyargcount: int(c_default="self->co_posonlyargcount") = unchanged + co_kwonlyargcount: int(c_default="self->co_kwonlyargcount") = unchanged + co_nlocals: int(c_default="self->co_nlocals") = unchanged + co_stacksize: int(c_default="self->co_stacksize") = unchanged + co_flags: int(c_default="self->co_flags") = unchanged + co_firstlineno: int(c_default="self->co_firstlineno") = unchanged + co_code: object(subclass_of="&PyBytes_Type", c_default="NULL") = unchanged + co_consts: object(subclass_of="&PyTuple_Type", c_default="self->co_consts") = unchanged + co_names: object(subclass_of="&PyTuple_Type", c_default="self->co_names") = unchanged + co_varnames: object(subclass_of="&PyTuple_Type", c_default="NULL") = unchanged + co_freevars: object(subclass_of="&PyTuple_Type", c_default="NULL") = unchanged + co_cellvars: object(subclass_of="&PyTuple_Type", c_default="NULL") = unchanged + co_filename: unicode(c_default="self->co_filename") = unchanged + co_name: unicode(c_default="self->co_name") = unchanged + co_qualname: unicode(c_default="self->co_qualname") = unchanged + co_linetable: object(subclass_of="&PyBytes_Type", c_default="self->co_linetable") = unchanged + co_exceptiontable: object(subclass_of="&PyBytes_Type", c_default="self->co_exceptiontable") = unchanged Return a copy of the code object with new values for the specified fields. [clinic start generated code]*/ @@ -1904,14 +1905,13 @@ static PyObject * code_replace_impl(PyCodeObject *self, int co_argcount, int co_posonlyargcount, int co_kwonlyargcount, int co_nlocals, int co_stacksize, int co_flags, - int co_firstlineno, PyBytesObject *co_code, - PyObject *co_consts, PyObject *co_names, - PyObject *co_varnames, PyObject *co_freevars, - PyObject *co_cellvars, PyObject *co_filename, - PyObject *co_name, PyObject *co_qualname, - PyBytesObject *co_linetable, - PyBytesObject *co_exceptiontable) -/*[clinic end generated code: output=b6cd9988391d5711 input=f6f68e03571f8d7c]*/ + int co_firstlineno, PyObject *co_code, PyObject *co_consts, + PyObject *co_names, PyObject *co_varnames, + PyObject *co_freevars, PyObject *co_cellvars, + PyObject *co_filename, PyObject *co_name, + PyObject *co_qualname, PyObject *co_linetable, + PyObject *co_exceptiontable) +/*[clinic end generated code: output=e75c48a15def18b9 input=18e280e07846c122]*/ { #define CHECK_INT_ARG(ARG) \ if (ARG < 0) { \ @@ -1936,7 +1936,7 @@ code_replace_impl(PyCodeObject *self, int co_argcount, if (code == NULL) { return NULL; } - co_code = (PyBytesObject *)code; + co_code = code; } if (PySys_Audit("code.__new__", "OOOiiiiii", @@ -1975,10 +1975,10 @@ code_replace_impl(PyCodeObject *self, int co_argcount, co = PyCode_NewWithPosOnlyArgs( co_argcount, co_posonlyargcount, co_kwonlyargcount, co_nlocals, - co_stacksize, co_flags, (PyObject*)co_code, co_consts, co_names, + co_stacksize, co_flags, co_code, co_consts, co_names, co_varnames, co_freevars, co_cellvars, co_filename, co_name, co_qualname, co_firstlineno, - (PyObject*)co_linetable, (PyObject*)co_exceptiontable); + co_linetable, co_exceptiontable); error: Py_XDECREF(code); diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 4d30e66b4822a7..84636b92e38a32 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -4072,6 +4072,7 @@ def reset(self): self.indent = IndentStack() self.kind = CALLABLE self.coexist = False + self.forced_text_signature: str | None = None self.parameter_continuation = '' self.preserve_output = False @@ -4201,6 +4202,11 @@ def at_coexist(self): fail("Called @coexist twice!") self.coexist = True + def at_text_signature(self, text_signature): + if self.forced_text_signature: + fail("Called @text_signature twice!") + self.forced_text_signature = text_signature + def parse(self, block): self.reset() self.block = block @@ -4903,142 +4909,145 @@ def format_docstring(self): add(f.cls.name) else: add(f.name) - add('(') + if self.forced_text_signature: + add(self.forced_text_signature) + else: + add('(') + + # populate "right_bracket_count" field for every parameter + assert parameters, "We should always have a self parameter. " + repr(f) + assert isinstance(parameters[0].converter, self_converter) + # self is always positional-only. + assert parameters[0].is_positional_only() + parameters[0].right_bracket_count = 0 + positional_only = True + for p in parameters[1:]: + if not p.is_positional_only(): + positional_only = False + else: + assert positional_only + if positional_only: + p.right_bracket_count = abs(p.group) + else: + # don't put any right brackets around non-positional-only parameters, ever. + p.right_bracket_count = 0 + + right_bracket_count = 0 + + def fix_right_bracket_count(desired): + nonlocal right_bracket_count + s = '' + while right_bracket_count < desired: + s += '[' + right_bracket_count += 1 + while right_bracket_count > desired: + s += ']' + right_bracket_count -= 1 + return s + + need_slash = False + added_slash = False + need_a_trailing_slash = False + + # we only need a trailing slash: + # * if this is not a "docstring_only" signature + # * and if the last *shown* parameter is + # positional only + if not f.docstring_only: + for p in reversed(parameters): + if not p.converter.show_in_signature: + continue + if p.is_positional_only(): + need_a_trailing_slash = True + break - # populate "right_bracket_count" field for every parameter - assert parameters, "We should always have a self parameter. " + repr(f) - assert isinstance(parameters[0].converter, self_converter) - # self is always positional-only. - assert parameters[0].is_positional_only() - parameters[0].right_bracket_count = 0 - positional_only = True - for p in parameters[1:]: - if not p.is_positional_only(): - positional_only = False - else: - assert positional_only - if positional_only: - p.right_bracket_count = abs(p.group) - else: - # don't put any right brackets around non-positional-only parameters, ever. - p.right_bracket_count = 0 - - right_bracket_count = 0 - - def fix_right_bracket_count(desired): - nonlocal right_bracket_count - s = '' - while right_bracket_count < desired: - s += '[' - right_bracket_count += 1 - while right_bracket_count > desired: - s += ']' - right_bracket_count -= 1 - return s - need_slash = False - added_slash = False - need_a_trailing_slash = False + added_star = False - # we only need a trailing slash: - # * if this is not a "docstring_only" signature - # * and if the last *shown* parameter is - # positional only - if not f.docstring_only: - for p in reversed(parameters): + first_parameter = True + last_p = parameters[-1] + line_length = len(''.join(text)) + indent = " " * line_length + def add_parameter(text): + nonlocal line_length + nonlocal first_parameter + if first_parameter: + s = text + first_parameter = False + else: + s = ' ' + text + if line_length + len(s) >= 72: + add('\n') + add(indent) + line_length = len(indent) + s = text + line_length += len(s) + add(s) + + for p in parameters: if not p.converter.show_in_signature: continue - if p.is_positional_only(): - need_a_trailing_slash = True - break + assert p.name + is_self = isinstance(p.converter, self_converter) + if is_self and f.docstring_only: + # this isn't a real machine-parsable signature, + # so let's not print the "self" parameter + continue - added_star = False - - first_parameter = True - last_p = parameters[-1] - line_length = len(''.join(text)) - indent = " " * line_length - def add_parameter(text): - nonlocal line_length - nonlocal first_parameter - if first_parameter: - s = text - first_parameter = False - else: - s = ' ' + text - if line_length + len(s) >= 72: - add('\n') - add(indent) - line_length = len(indent) - s = text - line_length += len(s) - add(s) - - for p in parameters: - if not p.converter.show_in_signature: - continue - assert p.name - - is_self = isinstance(p.converter, self_converter) - if is_self and f.docstring_only: - # this isn't a real machine-parsable signature, - # so let's not print the "self" parameter - continue - - if p.is_positional_only(): - need_slash = not f.docstring_only - elif need_slash and not (added_slash or p.is_positional_only()): - added_slash = True - add_parameter('/,') - - if p.is_keyword_only() and not added_star: - added_star = True - add_parameter('*,') - - p_add, p_output = text_accumulator() - p_add(fix_right_bracket_count(p.right_bracket_count)) - - if isinstance(p.converter, self_converter): - # annotate first parameter as being a "self". - # - # if inspect.Signature gets this function, - # and it's already bound, the self parameter - # will be stripped off. - # - # if it's not bound, it should be marked - # as positional-only. - # - # note: we don't print "self" for __init__, - # because this isn't actually the signature - # for __init__. (it can't be, __init__ doesn't - # have a docstring.) if this is an __init__ - # (or __new__), then this signature is for - # calling the class to construct a new instance. - p_add('$') + if p.is_positional_only(): + need_slash = not f.docstring_only + elif need_slash and not (added_slash or p.is_positional_only()): + added_slash = True + add_parameter('/,') + + if p.is_keyword_only() and not added_star: + added_star = True + add_parameter('*,') + + p_add, p_output = text_accumulator() + p_add(fix_right_bracket_count(p.right_bracket_count)) + + if isinstance(p.converter, self_converter): + # annotate first parameter as being a "self". + # + # if inspect.Signature gets this function, + # and it's already bound, the self parameter + # will be stripped off. + # + # if it's not bound, it should be marked + # as positional-only. + # + # note: we don't print "self" for __init__, + # because this isn't actually the signature + # for __init__. (it can't be, __init__ doesn't + # have a docstring.) if this is an __init__ + # (or __new__), then this signature is for + # calling the class to construct a new instance. + p_add('$') - if p.is_vararg(): - p_add("*") + if p.is_vararg(): + p_add("*") - name = p.converter.signature_name or p.name - p_add(name) + name = p.converter.signature_name or p.name + p_add(name) - if not p.is_vararg() and p.converter.is_optional(): - p_add('=') - value = p.converter.py_default - if not value: - value = repr(p.converter.default) - p_add(value) + if not p.is_vararg() and p.converter.is_optional(): + p_add('=') + value = p.converter.py_default + if not value: + value = repr(p.converter.default) + p_add(value) - if (p != last_p) or need_a_trailing_slash: - p_add(',') + if (p != last_p) or need_a_trailing_slash: + p_add(',') - add_parameter(p_output()) + add_parameter(p_output()) - add(fix_right_bracket_count(0)) - if need_a_trailing_slash: - add_parameter('/') - add(')') + add(fix_right_bracket_count(0)) + if need_a_trailing_slash: + add_parameter('/') + add(')') # PEP 8 says: # From 5b76eaf02e1f7450c2b8525de0f7f0b65022850a Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 9 Aug 2023 09:15:27 +0300 Subject: [PATCH 125/632] [3.11] gh-106052: Fix bug in the matching of possessive quantifiers (GH-106515) (GH-107795) It did not work in the case of a subpattern containing backtracking. Temporary implement possessive quantifiers as equivalent greedy qualifiers in atomic groups. (cherry picked from commit 7b6e34e5baeb4162815ffa4d943b09a58e3f6580) --- Lib/re/_compiler.py | 7 +++++++ Lib/test/test_re.py | 12 ++++++++++++ .../2023-07-07-14-52-31.gh-issue-106052.ak8nbs.rst | 2 ++ 3 files changed, 21 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2023-07-07-14-52-31.gh-issue-106052.ak8nbs.rst diff --git a/Lib/re/_compiler.py b/Lib/re/_compiler.py index d8e0d2fdefdcca..e30740b9c30b0e 100644 --- a/Lib/re/_compiler.py +++ b/Lib/re/_compiler.py @@ -100,6 +100,13 @@ def _compile(code, pattern, flags): emit(ANY_ALL) else: emit(ANY) + elif op is POSSESSIVE_REPEAT: + # gh-106052: Possessive quantifiers do not work when the + # subpattern contains backtracking, i.e. "(?:ab?c)*+". + # Implement it as equivalent greedy qualifier in atomic group. + p = [(MAX_REPEAT, av)] + p = [(ATOMIC_GROUP, p)] + _compile(code, p, flags) elif op in REPEATING_CODES: if flags & SRE_FLAG_TEMPLATE: raise error("internal: unsupported template operator %r" % (op,)) diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index 38d6db7c4091b4..d2736dae20b1b4 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -2396,6 +2396,16 @@ def test_template_function_and_flag_is_deprecated(self): self.assertTrue(template_re1.match('ahoy')) self.assertFalse(template_re1.match('nope')) + def test_bug_gh106052(self): + self.assertEqual(re.match("(?>(?:ab?c)+)", "aca").span(), (0, 2)) + self.assertEqual(re.match("(?:ab?c)++", "aca").span(), (0, 2)) + self.assertEqual(re.match("(?>(?:ab?c)*)", "aca").span(), (0, 2)) + self.assertEqual(re.match("(?:ab?c)*+", "aca").span(), (0, 2)) + self.assertEqual(re.match("(?>(?:ab?c)?)", "a").span(), (0, 0)) + self.assertEqual(re.match("(?:ab?c)?+", "a").span(), (0, 0)) + self.assertEqual(re.match("(?>(?:ab?c){1,3})", "aca").span(), (0, 2)) + self.assertEqual(re.match("(?:ab?c){1,3}+", "aca").span(), (0, 2)) + @unittest.skipIf(multiprocessing is None, 'test requires multiprocessing') def test_regression_gh94675(self): pattern = re.compile(r'(?<=[({}])(((//[^\n]*)?[\n])([\000-\040])*)*' @@ -2492,6 +2502,7 @@ def test_atomic_group(self): 17: SUCCESS ''') + @unittest.expectedFailure # gh-106052 def test_possesive_repeat_one(self): self.assertEqual(get_debug_out(r'a?+'), '''\ POSSESSIVE_REPEAT 0 1 @@ -2504,6 +2515,7 @@ def test_possesive_repeat_one(self): 12: SUCCESS ''') + @unittest.expectedFailure # gh-106052 def test_possesive_repeat(self): self.assertEqual(get_debug_out(r'(?:ab)?+'), '''\ POSSESSIVE_REPEAT 0 1 diff --git a/Misc/NEWS.d/next/Library/2023-07-07-14-52-31.gh-issue-106052.ak8nbs.rst b/Misc/NEWS.d/next/Library/2023-07-07-14-52-31.gh-issue-106052.ak8nbs.rst new file mode 100644 index 00000000000000..f2d4c2f7b18ec7 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-07-07-14-52-31.gh-issue-106052.ak8nbs.rst @@ -0,0 +1,2 @@ +:mod:`re` module: fix the matching of possessive quantifiers in the case of +a subpattern containing backtracking. From b0b26af822e601461988d0765cf7b33d3791ff2e Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 9 Aug 2023 09:25:25 +0200 Subject: [PATCH 126/632] [3.11] gh-86457: Add docs for Argument Clinic @text_signature directive (#107747) (#107799) (cherry picked from commit a9aeb99579f24bbce1dd553d605a5a5e2f37a3a2) Co-authored-by: Alex Waygood --- Doc/howto/clinic.rst | 33 +++++++++++++++++++ ...3-08-07-16-30-48.gh-issue-95065.-im4R5.rst | 2 +- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/Doc/howto/clinic.rst b/Doc/howto/clinic.rst index d75ba8a19cff2e..23be8a7a85c093 100644 --- a/Doc/howto/clinic.rst +++ b/Doc/howto/clinic.rst @@ -1893,3 +1893,36 @@ blocks embedded in Python files look slightly different. They look like this: #[python start generated code]*/ def foo(): pass #/*[python checksum:...]*/ + + +.. _clinic-howto-override-signature: + +How to override the generated signature +--------------------------------------- + +You can use the ``@text_signature`` directive to override the default generated +signature in the docstring. +This can be useful for complex signatures that Argument Clinic cannot handle. +The ``@text_signature`` directive takes one argument: +the custom signature as a string. +The provided signature is copied verbatim to the generated docstring. + +Example from :source:`Objects/codeobject.c`:: + + /*[clinic input] + @text_signature "($self, /, **changes)" + code.replace + * + co_argcount: int(c_default="self->co_argcount") = unchanged + co_posonlyargcount: int(c_default="self->co_posonlyargcount") = unchanged + # etc ... + + Return a copy of the code object with new values for the specified fields. + [clinic start generated output]*/ + +The generated docstring ends up looking like this:: + + replace($self, /, **changes) + -- + + Return a copy of the code object with new values for the specified fields. diff --git a/Misc/NEWS.d/next/Tools-Demos/2023-08-07-16-30-48.gh-issue-95065.-im4R5.rst b/Misc/NEWS.d/next/Tools-Demos/2023-08-07-16-30-48.gh-issue-95065.-im4R5.rst index 4768e6767574d8..7284f5bd548810 100644 --- a/Misc/NEWS.d/next/Tools-Demos/2023-08-07-16-30-48.gh-issue-95065.-im4R5.rst +++ b/Misc/NEWS.d/next/Tools-Demos/2023-08-07-16-30-48.gh-issue-95065.-im4R5.rst @@ -1,2 +1,2 @@ Argument Clinic now supports overriding automatically generated signature by -using directive ``@text_signature``. +using directive ``@text_signature``. See :ref:`clinic-howto-override-signature`. From ec0c0c8692e32f1c694a53ab00b17893fc03666b Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 9 Aug 2023 09:54:32 +0200 Subject: [PATCH 127/632] [3.11] Docs: clean up Argument Clinic howto's (#107797) (#107800) (cherry picked from commit 34cafd35e35dcb44b4347a6478fdbf88b900240c) - fix formatting in @text_signature howto and NEWS entry --- Doc/howto/clinic.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Doc/howto/clinic.rst b/Doc/howto/clinic.rst index 23be8a7a85c093..5f4d3977bc1605 100644 --- a/Doc/howto/clinic.rst +++ b/Doc/howto/clinic.rst @@ -1920,7 +1920,9 @@ Example from :source:`Objects/codeobject.c`:: Return a copy of the code object with new values for the specified fields. [clinic start generated output]*/ -The generated docstring ends up looking like this:: +The generated docstring ends up looking like this: + +.. code-block:: none replace($self, /, **changes) -- From eff2042fac6dc7d6e2f8aeec5a64a3a44a1a9648 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 9 Aug 2023 16:15:43 -0700 Subject: [PATCH 128/632] [3.11] gh-107814: Avoid output from Nuget installation in find_python.bat (GH-107815) gh-107814: Avoid output from Nuget installation in find_python.bat (GH-107815) (cherry picked from commit 1e229e2c3d212accbd5fbe3a46cd42f8252b2868) Co-authored-by: Max Bachmann --- .../Build/2023-08-09-17-05-33.gh-issue-107814.c0Oapq.rst | 1 + PCbuild/find_python.bat | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2023-08-09-17-05-33.gh-issue-107814.c0Oapq.rst diff --git a/Misc/NEWS.d/next/Build/2023-08-09-17-05-33.gh-issue-107814.c0Oapq.rst b/Misc/NEWS.d/next/Build/2023-08-09-17-05-33.gh-issue-107814.c0Oapq.rst new file mode 100644 index 00000000000000..d3723353470ce2 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2023-08-09-17-05-33.gh-issue-107814.c0Oapq.rst @@ -0,0 +1 @@ +When calling ``find_python.bat`` with ``-q`` it did not properly silence the output of nuget. That is now fixed. diff --git a/PCbuild/find_python.bat b/PCbuild/find_python.bat index 11d6cba7a172c9..31579e088e4707 100644 --- a/PCbuild/find_python.bat +++ b/PCbuild/find_python.bat @@ -52,7 +52,7 @@ @if "%_Py_NUGET%"=="" (set _Py_NUGET=%_Py_EXTERNALS_DIR%\nuget.exe) @if "%_Py_NUGET_URL%"=="" (set _Py_NUGET_URL=https://aka.ms/nugetclidl) @if NOT exist "%_Py_NUGET%" ( - @echo Downloading nuget... + @if not "%_Py_Quiet%"=="1" @echo Downloading nuget... @rem NB: Must use single quotes around NUGET here, NOT double! @rem Otherwise, a space in the path would break things @rem If it fails, retry with any available copy of Python @@ -63,7 +63,11 @@ ) @if not "%_Py_Quiet%"=="1" @echo Installing Python via nuget... -@"%_Py_NUGET%" install pythonx86 -ExcludeVersion -OutputDirectory "%_Py_EXTERNALS_DIR%" +@if not "%_Py_Quiet%"=="1" ( + @"%_Py_NUGET%" install pythonx86 -ExcludeVersion -OutputDirectory "%_Py_EXTERNALS_DIR%" +) else ( + @"%_Py_NUGET%" install pythonx86 -Verbosity quiet -ExcludeVersion -OutputDirectory "%_Py_EXTERNALS_DIR%" +) @rem Quote it here; it's not quoted later because "py -x.y" wouldn't work @if not errorlevel 1 (set PYTHON="%_Py_EXTERNALS_DIR%\pythonx86\tools\python.exe") & (set _Py_Python_Source=found on nuget.org) & goto :found From fb08b7905eaf67902a89eeec0f8c1aa5ee0c7ea5 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 10 Aug 2023 03:44:31 -0700 Subject: [PATCH 129/632] [3.11] [3.12] GH-106684: Close `asyncio.StreamWriter` when `asyncio.StreamWriter` is not closed by application (GH-107650) (GH-107656) (#107836) [3.12] GH-106684: Close `asyncio.StreamWriter` when `asyncio.StreamWriter` is not closed by application (GH-107650) (GH-107656) GH-106684: raise `ResourceWarning` when `asyncio.StreamWriter` is not closed (GH-107650) (cherry picked from commit 41178e41995992bbe417f94bce158de93f9e3188) (cherry picked from commit 7853c769067699c79c0d4fe4967e9d8f8b8b0a5e) Co-authored-by: Kumar Aditya --- Lib/asyncio/streams.py | 4 ++++ .../Library/2023-08-05-05-10-41.gh-issue-106684.P9zRXb.rst | 1 + 2 files changed, 5 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2023-08-05-05-10-41.gh-issue-106684.P9zRXb.rst diff --git a/Lib/asyncio/streams.py b/Lib/asyncio/streams.py index 560ad8a8510e55..3d577f127041aa 100644 --- a/Lib/asyncio/streams.py +++ b/Lib/asyncio/streams.py @@ -391,6 +391,10 @@ async def start_tls(self, sslcontext, *, self._transport = new_transport protocol._replace_writer(self) + def __del__(self): + if not self._transport.is_closing(): + self.close() + class StreamReader: diff --git a/Misc/NEWS.d/next/Library/2023-08-05-05-10-41.gh-issue-106684.P9zRXb.rst b/Misc/NEWS.d/next/Library/2023-08-05-05-10-41.gh-issue-106684.P9zRXb.rst new file mode 100644 index 00000000000000..85bce76229853f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-08-05-05-10-41.gh-issue-106684.P9zRXb.rst @@ -0,0 +1 @@ +Close :class:`asyncio.StreamWriter` when it is not closed by application leading to memory leaks. Patch by Kumar Aditya. From 202efe1a3bcd499f3bf17bd953c6d36d47747e78 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 10 Aug 2023 14:10:46 -0700 Subject: [PATCH 130/632] [3.11] Fix the long64 reader in umarshal.py (GH-107828) (GH-107850) (cherry picked from commit 50bbc56009ae7303d2482f28eb62f2603664b58f) Co-authored-by: Martin DeMello --- Tools/scripts/umarshal.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Tools/scripts/umarshal.py b/Tools/scripts/umarshal.py index f61570cbaff751..e05d93cf23c921 100644 --- a/Tools/scripts/umarshal.py +++ b/Tools/scripts/umarshal.py @@ -125,10 +125,10 @@ def r_long64(self) -> int: x |= buf[1] << 8 x |= buf[2] << 16 x |= buf[3] << 24 - x |= buf[1] << 32 - x |= buf[1] << 40 - x |= buf[1] << 48 - x |= buf[1] << 56 + x |= buf[4] << 32 + x |= buf[5] << 40 + x |= buf[6] << 48 + x |= buf[7] << 56 x |= -(x & (1<<63)) # Sign-extend return x From 4ddfb042609bebe7fd1d9af5f5d7bbc781ea5b4f Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 11 Aug 2023 09:13:53 -0700 Subject: [PATCH 131/632] [3.11] Docs: Document PyBUF_MAX_NDIM (GH-107865) (#107872) (cherry picked from commit 637f7ff2c60f262659da0334f1cb672bd361f398) Co-authored-by: Erlend E. Aasland --- Doc/c-api/buffer.rst | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Doc/c-api/buffer.rst b/Doc/c-api/buffer.rst index 8ca1c190dab9a9..ba391a5279f205 100644 --- a/Doc/c-api/buffer.rst +++ b/Doc/c-api/buffer.rst @@ -161,10 +161,14 @@ a buffer, see :c:func:`PyObject_GetBuffer`. If it is ``0``, :c:member:`~Py_buffer.buf` points to a single item representing a scalar. In this case, :c:member:`~Py_buffer.shape`, :c:member:`~Py_buffer.strides` and :c:member:`~Py_buffer.suboffsets` MUST be ``NULL``. + The maximum number of dimensions is given by :c:macro:`PyBUF_MAX_NDIM`. - The macro :c:macro:`PyBUF_MAX_NDIM` limits the maximum number of dimensions - to 64. Exporters MUST respect this limit, consumers of multi-dimensional - buffers SHOULD be able to handle up to :c:macro:`PyBUF_MAX_NDIM` dimensions. + .. :c:macro:: PyBUF_MAX_NDIM + + The maximum number of dimensions the memory represents. + Exporters MUST respect this limit, consumers of multi-dimensional + buffers SHOULD be able to handle up to :c:macro:`!PyBUF_MAX_NDIM` dimensions. + Currently set to 64. .. c:member:: Py_ssize_t *shape From ec254c5dfa8c99f1ec061b252d155386e93f19ef Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 12 Aug 2023 14:06:56 +0300 Subject: [PATCH 132/632] [3.11] gh-106844: Fix issues in _winapi.LCMapStringEx (GH-107832) (GH-107875) * Strings with length from 2**31-1 to 2**32-2 always caused MemoryError, it doesn't matter how much memory is available. * Strings with length exactly 2**32-1 caused OSError. * Strings longer than 2**32-1 characters were truncated due to integer overflow bug. Now strings longer than 2**31-1 characters caused OverflowError. (cherry picked from commit 04cc01453db2f0af72a06440831637f8bf512daf) --- Lib/test/test_ntpath.py | 1 + ...-07-18-13-01-26.gh-issue-106844.mci4xO.rst | 1 + Modules/_winapi.c | 26 ++++++++++--------- 3 files changed, 16 insertions(+), 12 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2023-07-18-13-01-26.gh-issue-106844.mci4xO.rst diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py index 646e81d1e2fa9b..75e50d92ed1eb1 100644 --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -908,6 +908,7 @@ def test_path_normcase(self): self._check_function(self.path.normcase) if sys.platform == 'win32': self.assertEqual(ntpath.normcase('\u03a9\u2126'), 'ωΩ') + self.assertEqual(ntpath.normcase('abc\x00def'), 'abc\x00def') def test_path_isabs(self): self._check_function(self.path.isabs) diff --git a/Misc/NEWS.d/next/Windows/2023-07-18-13-01-26.gh-issue-106844.mci4xO.rst b/Misc/NEWS.d/next/Windows/2023-07-18-13-01-26.gh-issue-106844.mci4xO.rst new file mode 100644 index 00000000000000..11fca7e0452f40 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-07-18-13-01-26.gh-issue-106844.mci4xO.rst @@ -0,0 +1 @@ +Fix integer overflow in :func:`!_winapi.LCMapStringEx` which affects :func:`ntpath.normcase`. diff --git a/Modules/_winapi.c b/Modules/_winapi.c index 5c61d99a837515..7fb1f2f561b0f9 100644 --- a/Modules/_winapi.c +++ b/Modules/_winapi.c @@ -1571,24 +1571,26 @@ _winapi_LCMapStringEx_impl(PyObject *module, PyObject *locale, DWORD flags, if (!locale_) { return NULL; } - Py_ssize_t srcLenAsSsize; - int srcLen; - wchar_t *src_ = PyUnicode_AsWideCharString(src, &srcLenAsSsize); + Py_ssize_t src_size; + wchar_t *src_ = PyUnicode_AsWideCharString(src, &src_size); if (!src_) { PyMem_Free(locale_); return NULL; } - srcLen = (int)srcLenAsSsize; - if (srcLen != srcLenAsSsize) { - srcLen = -1; + if (src_size > INT_MAX) { + PyMem_Free(locale_); + PyMem_Free(src_); + PyErr_SetString(PyExc_OverflowError, "input string is too long"); + return NULL; } - int dest_size = LCMapStringEx(locale_, flags, src_, srcLen, NULL, 0, + int dest_size = LCMapStringEx(locale_, flags, src_, (int)src_size, NULL, 0, NULL, NULL, 0); - if (dest_size == 0) { + if (dest_size <= 0) { + DWORD error = GetLastError(); PyMem_Free(locale_); PyMem_Free(src_); - return PyErr_SetFromWindowsErr(0); + return PyErr_SetFromWindowsErr(error); } wchar_t* dest = PyMem_NEW(wchar_t, dest_size); @@ -1598,9 +1600,9 @@ _winapi_LCMapStringEx_impl(PyObject *module, PyObject *locale, DWORD flags, return PyErr_NoMemory(); } - int nmapped = LCMapStringEx(locale_, flags, src_, srcLen, dest, dest_size, + int nmapped = LCMapStringEx(locale_, flags, src_, (int)src_size, dest, dest_size, NULL, NULL, 0); - if (nmapped == 0) { + if (nmapped <= 0) { DWORD error = GetLastError(); PyMem_Free(locale_); PyMem_Free(src_); @@ -1608,9 +1610,9 @@ _winapi_LCMapStringEx_impl(PyObject *module, PyObject *locale, DWORD flags, return PyErr_SetFromWindowsErr(error); } - PyObject *ret = PyUnicode_FromWideChar(dest, dest_size); PyMem_Free(locale_); PyMem_Free(src_); + PyObject *ret = PyUnicode_FromWideChar(dest, nmapped); PyMem_DEL(dest); return ret; From 5f36e5ca5fe6768ae639aaf7c04ed41b665880e1 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 12 Aug 2023 04:57:49 -0700 Subject: [PATCH 133/632] [3.11] gh-107715: Escape class name in regular expression (GH-107716) (GH-107727) This patch escapes the class name before embedding it in the regular expression for `pat` in `doctest.DocTestFinder._find_lineno`. While class names do not ordinarily contain special characters, it is possible to encounter these when a class is created dynamically. Escaping the name will correctly return `None` in this scenario, rather than potentially matching a different class or raising `re.error` depending on the symbols used. (cherry picked from commit 85793278793708ad6b7132a54ac9fb4b2c5bcac1) Co-authored-by: Gertjan van Zwieten --- Lib/doctest.py | 2 +- .../next/Library/2023-08-07-14-12-07.gh-issue-107715.238r2f.rst | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2023-08-07-14-12-07.gh-issue-107715.238r2f.rst diff --git a/Lib/doctest.py b/Lib/doctest.py index dafad505ef0e86..8fcb4ee0a02ee0 100644 --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -1104,7 +1104,7 @@ def _find_lineno(self, obj, source_lines): if source_lines is None: return None pat = re.compile(r'^\s*class\s*%s\b' % - getattr(obj, '__name__', '-')) + re.escape(getattr(obj, '__name__', '-'))) for i, line in enumerate(source_lines): if pat.match(line): lineno = i diff --git a/Misc/NEWS.d/next/Library/2023-08-07-14-12-07.gh-issue-107715.238r2f.rst b/Misc/NEWS.d/next/Library/2023-08-07-14-12-07.gh-issue-107715.238r2f.rst new file mode 100644 index 00000000000000..4bf08c071df2f9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-08-07-14-12-07.gh-issue-107715.238r2f.rst @@ -0,0 +1 @@ +Fix :meth:`doctest.DocTestFinder.find` in presence of class names with special characters. Patch by Gertjan van Zwieten. From e2420c5cae7a173c5242b3507979010a933e53a7 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 13 Aug 2023 06:43:14 -0700 Subject: [PATCH 134/632] [3.11] gh-107877: Update logging levels reference table with usage criteria. (GH-107894) (GH-107921) gh-107877: Update logging levels reference table with usage criteria. (GH-107894) (cherry picked from commit cc2cf85d03cf29994a707aae5cc9a349a4165b84) --- Doc/library/logging.rst | 48 ++++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst index 954681efb44367..09dc887ee24156 100644 --- a/Doc/library/logging.rst +++ b/Doc/library/logging.rst @@ -385,21 +385,39 @@ have specific values relative to the predefined levels. If you define a level with the same numeric value, it overwrites the predefined value; the predefined name is lost. -+-----------------------+---------------+ -| Level | Numeric value | -+=======================+===============+ -| .. py:data:: CRITICAL | 50 | -+-----------------------+---------------+ -| .. py:data:: ERROR | 40 | -+-----------------------+---------------+ -| .. py:data:: WARNING | 30 | -+-----------------------+---------------+ -| .. py:data:: INFO | 20 | -+-----------------------+---------------+ -| .. py:data:: DEBUG | 10 | -+-----------------------+---------------+ -| .. py:data:: NOTSET | 0 | -+-----------------------+---------------+ ++-----------------------+---------------+-------------------------------------+ +| Level | Numeric value | What it means / When to use it | ++=======================+===============+=====================================+ +| .. py:data:: NOTSET | 0 | When set on a logger, indicates that| +| | | ancestor loggers are to be consulted| +| | | to determine the effective level. | +| | | If that still resolves to | +| | | :const:`!NOTSET`, then all events | +| | | are logged. When set on a handler, | +| | | all events are handled. | ++-----------------------+---------------+-------------------------------------+ +| .. py:data:: DEBUG | 10 | Detailed information, typically only| +| | | of interest to a developer trying to| +| | | diagnose a problem. | ++-----------------------+---------------+-------------------------------------+ +| .. py:data:: INFO | 20 | Confirmation that things are working| +| | | as expected. | ++-----------------------+---------------+-------------------------------------+ +| .. py:data:: WARNING | 30 | An indication that something | +| | | unexpected happened, or that a | +| | | problem might occur in the near | +| | | future (e.g. 'disk space low'). The | +| | | software is still working as | +| | | expected. | ++-----------------------+---------------+-------------------------------------+ +| .. py:data:: ERROR | 40 | Due to a more serious problem, the | +| | | software has not been able to | +| | | perform some function. | ++-----------------------+---------------+-------------------------------------+ +| .. py:data:: CRITICAL | 50 | A serious error, indicating that the| +| | | program itself may be unable to | +| | | continue running. | ++-----------------------+---------------+-------------------------------------+ .. _handler: From db4400b5b28dc17c9eac47780402c0b812703358 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 15 Aug 2023 07:53:25 -0700 Subject: [PATCH 135/632] [3.11] gh-107963: Fix set_forkserver_preload to check the type of given list (GH-107965) (gh-107976) gh-107963: Fix set_forkserver_preload to check the type of given list (GH-107965) (cherry picked from commit 6515ec3d3d5acd3d0b99c88794bdec09f0831e5b) gh-107963: Fix set_forkserver_preload to check the type of given list Co-authored-by: Dong-hee Na --- Lib/multiprocessing/forkserver.py | 2 +- Lib/test/_test_multiprocessing.py | 8 ++++++++ .../2023-08-15-18-20-00.gh-issue-107963.20g5BG.rst | 2 ++ 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2023-08-15-18-20-00.gh-issue-107963.20g5BG.rst diff --git a/Lib/multiprocessing/forkserver.py b/Lib/multiprocessing/forkserver.py index 22a911a7a29cdc..4642707dae2f4e 100644 --- a/Lib/multiprocessing/forkserver.py +++ b/Lib/multiprocessing/forkserver.py @@ -61,7 +61,7 @@ def _stop_unlocked(self): def set_forkserver_preload(self, modules_names): '''Set list of module names to try to load in forkserver process.''' - if not all(type(mod) is str for mod in self._preload_modules): + if not all(type(mod) is str for mod in modules_names): raise TypeError('module_names must be a list of strings') self._preload_modules = modules_names diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index aa302c21000dde..29dc3863766497 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -5293,6 +5293,14 @@ def test_context(self): self.assertRaises(ValueError, ctx.set_start_method, None) self.check_context(ctx) + def test_context_check_module_types(self): + try: + ctx = multiprocessing.get_context('forkserver') + except ValueError: + raise unittest.SkipTest('forkserver should be available') + with self.assertRaisesRegex(TypeError, 'module_names must be a list of strings'): + ctx.set_forkserver_preload([1, 2, 3]) + def test_set_get(self): multiprocessing.set_forkserver_preload(PRELOAD) count = 0 diff --git a/Misc/NEWS.d/next/Library/2023-08-15-18-20-00.gh-issue-107963.20g5BG.rst b/Misc/NEWS.d/next/Library/2023-08-15-18-20-00.gh-issue-107963.20g5BG.rst new file mode 100644 index 00000000000000..3a73b2da0c4334 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-08-15-18-20-00.gh-issue-107963.20g5BG.rst @@ -0,0 +1,2 @@ +Fix :func:`multiprocessing.set_forkserver_preload` to check the given list +of modules names. Patch by Dong-hee Na. From ccf81e1088c25a9f4464e478dc3b5c03ed7ee63b Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Tue, 15 Aug 2023 18:07:52 +0100 Subject: [PATCH 136/632] [3.11] gh-106242: Fix path truncation in os.path.normpath (GH-106816) (#107982) Co-authored-by: Finn Womack --- Include/internal/pycore_fileutils.h | 3 +- Lib/test/test_genericpath.py | 4 +++ ...-08-14-23-11-11.gh-issue-106242.71HMym.rst | 1 + Modules/posixmodule.c | 4 ++- Python/fileutils.c | 29 ++++++++++++++----- 5 files changed, 31 insertions(+), 10 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-08-14-23-11-11.gh-issue-106242.71HMym.rst diff --git a/Include/internal/pycore_fileutils.h b/Include/internal/pycore_fileutils.h index 3ce8108e4e04f1..332cc30365b9e2 100644 --- a/Include/internal/pycore_fileutils.h +++ b/Include/internal/pycore_fileutils.h @@ -244,7 +244,8 @@ extern int _Py_add_relfile(wchar_t *dirname, const wchar_t *relfile, size_t bufsize); extern size_t _Py_find_basename(const wchar_t *filename); -PyAPI_FUNC(wchar_t *) _Py_normpath(wchar_t *path, Py_ssize_t size); +PyAPI_FUNC(wchar_t*) _Py_normpath(wchar_t *path, Py_ssize_t size); +extern wchar_t *_Py_normpath_and_size(wchar_t *path, Py_ssize_t size, Py_ssize_t *length); // Macros to protect CRT calls against instant termination when passed an diff --git a/Lib/test/test_genericpath.py b/Lib/test/test_genericpath.py index 489044f8090d3b..4f311c2d498e9f 100644 --- a/Lib/test/test_genericpath.py +++ b/Lib/test/test_genericpath.py @@ -460,6 +460,10 @@ def test_normpath_issue5827(self): for path in ('', '.', '/', '\\', '///foo/.//bar//'): self.assertIsInstance(self.pathmodule.normpath(path), str) + def test_normpath_issue106242(self): + for path in ('\x00', 'foo\x00bar', '\x00\x00', '\x00foo', 'foo\x00'): + self.assertEqual(self.pathmodule.normpath(path), path) + def test_abspath_issue3426(self): # Check that abspath returns unicode when the arg is unicode # with both ASCII and non-ASCII cwds. diff --git a/Misc/NEWS.d/next/Library/2023-08-14-23-11-11.gh-issue-106242.71HMym.rst b/Misc/NEWS.d/next/Library/2023-08-14-23-11-11.gh-issue-106242.71HMym.rst new file mode 100644 index 00000000000000..44237a9f15708c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-08-14-23-11-11.gh-issue-106242.71HMym.rst @@ -0,0 +1 @@ +Fixes :func:`os.path.normpath` to handle embedded null characters without truncating the path. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 9d4228896d230e..5ceea7411818a4 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -4552,7 +4552,9 @@ os__path_normpath_impl(PyObject *module, PyObject *path) if (!buffer) { return NULL; } - PyObject *result = PyUnicode_FromWideChar(_Py_normpath(buffer, len), -1); + Py_ssize_t norm_len; + wchar_t *norm_path = _Py_normpath_and_size(buffer, len, &norm_len); + PyObject *result = PyUnicode_FromWideChar(norm_path, norm_len); PyMem_Free(buffer); return result; } diff --git a/Python/fileutils.c b/Python/fileutils.c index c86ed40b19937e..b310a6c077d91e 100644 --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -2179,12 +2179,14 @@ _Py_find_basename(const wchar_t *filename) path, which will be within the original buffer. Guaranteed to not make the path longer, and will not fail. 'size' is the length of the path, if known. If -1, the first null character will be assumed - to be the end of the path. */ + to be the end of the path. 'normsize' will be set to contain the + length of the resulting normalized path. */ wchar_t * -_Py_normpath(wchar_t *path, Py_ssize_t size) +_Py_normpath_and_size(wchar_t *path, Py_ssize_t size, Py_ssize_t *normsize) { assert(path != NULL); - if (!path[0] || size == 0) { + if ((size < 0 && !path[0]) || size == 0) { + *normsize = 0; return path; } wchar_t *pEnd = size >= 0 ? &path[size] : NULL; @@ -2233,11 +2235,7 @@ _Py_normpath(wchar_t *path, Py_ssize_t size) *p2++ = lastC = *p1; } } - if (sepCount) { - minP2 = p2; // Invalid path - } else { - minP2 = p2 - 1; // Absolute path has SEP at minP2 - } + minP2 = p2 - 1; } #else // Skip past two leading SEPs @@ -2297,13 +2295,28 @@ _Py_normpath(wchar_t *path, Py_ssize_t size) while (--p2 != minP2 && *p2 == SEP) { *p2 = L'\0'; } + } else { + --p2; } + *normsize = p2 - path + 1; #undef SEP_OR_END #undef IS_SEP #undef IS_END return path; } +/* In-place path normalisation. Returns the start of the normalized + path, which will be within the original buffer. Guaranteed to not + make the path longer, and will not fail. 'size' is the length of + the path, if known. If -1, the first null character will be assumed + to be the end of the path. */ +wchar_t * +_Py_normpath(wchar_t *path, Py_ssize_t size) +{ + Py_ssize_t norm_length; + return _Py_normpath_and_size(path, size, &norm_length); +} + /* Get the current directory. buflen is the buffer size in wide characters including the null character. Decode the path from the locale encoding. From 3f7dfb6d392f6278e6aee9233003dcaa45e2e736 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 15 Aug 2023 18:43:28 -0700 Subject: [PATCH 137/632] [3.11] More actionable error message when spawn is incorrectly used. (GH-102203) (#107991) More actionable error message when spawn is incorrectly used. (GH-102203) (cherry picked from commit a794ebeb028f7ef287c780d3890f816db9c21c51) Co-authored-by: Yuxin Wu Co-authored-by: Yuxin Wu Co-authored-by: Oleg Iarygin --- Lib/multiprocessing/spawn.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Lib/multiprocessing/spawn.py b/Lib/multiprocessing/spawn.py index f1af7709104714..daac1ecc34b55e 100644 --- a/Lib/multiprocessing/spawn.py +++ b/Lib/multiprocessing/spawn.py @@ -150,7 +150,11 @@ def _check_not_importing_main(): ... The "freeze_support()" line can be omitted if the program - is not going to be frozen to produce an executable.''') + is not going to be frozen to produce an executable. + + To fix this issue, refer to the "Safe importing of main module" + section in https://docs.python.org/3/library/multiprocessing.html + ''') def get_preparation_data(name): From 4cfbcffd3d0bbaab1610896ad9ff9c588467681d Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 16 Aug 2023 10:44:21 +0300 Subject: [PATCH 138/632] [3.11] gh-99203: shutil.make_archive(): restore select CPython <= 3.10.5 behavior (GH-99802) (GH-107999) Restore following CPython <= 3.10.5 behavior of shutil.make_archive() that went away as part of gh-93160: Do not create an empty archive if root_dir is not a directory, and, in that case, raise FileNotFoundError or NotADirectoryError regardless of format choice. Beyond the brought-back behavior, the function may now also raise these exceptions in dry_run mode. (cherry picked from commit a86df298df5b02e2d69ea6879e9ed10a7adb85d0) Co-authored-by: 6t8k <58048945+6t8k@users.noreply.github.com> --- Lib/shutil.py | 8 +++- Lib/test/test_shutil.py | 43 +++++++++++++++++++ ...2-11-26-22-05-22.gh-issue-99203.j0DUae.rst | 5 +++ 3 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-11-26-22-05-22.gh-issue-99203.j0DUae.rst diff --git a/Lib/shutil.py b/Lib/shutil.py index f4efaef38933d2..1c3a75da55ba5b 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -1114,10 +1114,14 @@ def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, if base_dir is None: base_dir = os.curdir - support_root_dir = format_info[3] + supports_root_dir = format_info[3] save_cwd = None if root_dir is not None: - if support_root_dir: + stmd = os.stat(root_dir).st_mode + if not stat.S_ISDIR(stmd): + raise NotADirectoryError(errno.ENOTDIR, 'Not a directory', root_dir) + + if supports_root_dir: # Support path-like base_name here for backwards-compatibility. base_name = os.fspath(base_name) kwargs['root_dir'] = root_dir diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index bd82aa54368eb6..274a8b7377e3bb 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -1629,6 +1629,49 @@ def test_register_archive_format(self): formats = [name for name, params in get_archive_formats()] self.assertNotIn('xxx', formats) + def test_make_tarfile_rootdir_nodir(self): + # GH-99203 + self.addCleanup(os_helper.unlink, f'{TESTFN}.tar') + for dry_run in (False, True): + with self.subTest(dry_run=dry_run): + tmp_dir = self.mkdtemp() + nonexisting_file = os.path.join(tmp_dir, 'nonexisting') + with self.assertRaises(FileNotFoundError) as cm: + make_archive(TESTFN, 'tar', nonexisting_file, dry_run=dry_run) + self.assertEqual(cm.exception.errno, errno.ENOENT) + self.assertEqual(cm.exception.filename, nonexisting_file) + self.assertFalse(os.path.exists(f'{TESTFN}.tar')) + + tmp_fd, tmp_file = tempfile.mkstemp(dir=tmp_dir) + os.close(tmp_fd) + with self.assertRaises(NotADirectoryError) as cm: + make_archive(TESTFN, 'tar', tmp_file, dry_run=dry_run) + self.assertEqual(cm.exception.errno, errno.ENOTDIR) + self.assertEqual(cm.exception.filename, tmp_file) + self.assertFalse(os.path.exists(f'{TESTFN}.tar')) + + @support.requires_zlib() + def test_make_zipfile_rootdir_nodir(self): + # GH-99203 + self.addCleanup(os_helper.unlink, f'{TESTFN}.zip') + for dry_run in (False, True): + with self.subTest(dry_run=dry_run): + tmp_dir = self.mkdtemp() + nonexisting_file = os.path.join(tmp_dir, 'nonexisting') + with self.assertRaises(FileNotFoundError) as cm: + make_archive(TESTFN, 'zip', nonexisting_file, dry_run=dry_run) + self.assertEqual(cm.exception.errno, errno.ENOENT) + self.assertEqual(cm.exception.filename, nonexisting_file) + self.assertFalse(os.path.exists(f'{TESTFN}.zip')) + + tmp_fd, tmp_file = tempfile.mkstemp(dir=tmp_dir) + os.close(tmp_fd) + with self.assertRaises(NotADirectoryError) as cm: + make_archive(TESTFN, 'zip', tmp_file, dry_run=dry_run) + self.assertEqual(cm.exception.errno, errno.ENOTDIR) + self.assertEqual(cm.exception.filename, tmp_file) + self.assertFalse(os.path.exists(f'{TESTFN}.zip')) + ### shutil.unpack_archive def check_unpack_archive(self, format, **kwargs): diff --git a/Misc/NEWS.d/next/Library/2022-11-26-22-05-22.gh-issue-99203.j0DUae.rst b/Misc/NEWS.d/next/Library/2022-11-26-22-05-22.gh-issue-99203.j0DUae.rst new file mode 100644 index 00000000000000..fcfb044d476acc --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-11-26-22-05-22.gh-issue-99203.j0DUae.rst @@ -0,0 +1,5 @@ +Restore following CPython <= 3.10.5 behavior of :func:`shutil.make_archive`: +do not create an empty archive if ``root_dir`` is not a directory, and, in that +case, raise :class:`FileNotFoundError` or :class:`NotADirectoryError` +regardless of ``format`` choice. Beyond the brought-back behavior, the function +may now also raise these exceptions in ``dry_run`` mode. From af08bcab8f7e0067de54a91b11ca5e4b99ec11b7 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 16 Aug 2023 01:07:02 -0700 Subject: [PATCH 139/632] [3.11] gh-100814: Fix exception for invalid callable value of Tkinter image option (GH-107692) (GH-107723) Passing a callable object as an option value to a Tkinter image now raises the expected TclError instead of an AttributeError. (cherry picked from commit 50e3cc9748eb2103eb7ed6cc5a74d177df3cfb13) Co-authored-by: Serhiy Storchaka --- Lib/tkinter/__init__.py | 4 ---- Lib/tkinter/test/test_tkinter/test_images.py | 16 ++++++++++++++++ ...023-08-06-15-29-00.gh-issue-100814.h195gW.rst | 2 ++ 3 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-08-06-15-29-00.gh-issue-100814.h195gW.rst diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index 054034098880f0..6ae9839055382c 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -4068,8 +4068,6 @@ def __init__(self, imgtype, name=None, cnf={}, master=None, **kw): elif kw: cnf = kw options = () for k, v in cnf.items(): - if callable(v): - v = self._register(v) options = options + ('-'+k, v) self.tk.call(('image', 'create', imgtype, name,) + options) self.name = name @@ -4096,8 +4094,6 @@ def configure(self, **kw): for k, v in _cnfmerge(kw).items(): if v is not None: if k[-1] == '_': k = k[:-1] - if callable(v): - v = self._register(v) res = res + ('-'+k, v) self.tk.call((self.name, 'config') + res) diff --git a/Lib/tkinter/test/test_tkinter/test_images.py b/Lib/tkinter/test/test_tkinter/test_images.py index 8b473cce5f9947..5b3566a3611eb5 100644 --- a/Lib/tkinter/test/test_tkinter/test_images.py +++ b/Lib/tkinter/test/test_tkinter/test_images.py @@ -144,6 +144,14 @@ def test_configure_foreground(self): self.assertEqual(image['foreground'], '-foreground {} {} #000000 yellow') + def test_bug_100814(self): + # gh-100814: Passing a callable option value causes AttributeError. + with self.assertRaises(tkinter.TclError): + tkinter.BitmapImage('::img::test', master=self.root, spam=print) + image = tkinter.BitmapImage('::img::test', master=self.root) + with self.assertRaises(tkinter.TclError): + image.configure(spam=print) + class PhotoImageTest(AbstractTkTest, unittest.TestCase): @@ -274,6 +282,14 @@ def test_configure_palette(self): image.configure(palette='3/4/2') self.assertEqual(image['palette'], '3/4/2') + def test_bug_100814(self): + # gh-100814: Passing a callable option value causes AttributeError. + with self.assertRaises(tkinter.TclError): + tkinter.PhotoImage('::img::test', master=self.root, spam=print) + image = tkinter.PhotoImage('::img::test', master=self.root) + with self.assertRaises(tkinter.TclError): + image.configure(spam=print) + def test_blank(self): image = self.create() image.blank() diff --git a/Misc/NEWS.d/next/Library/2023-08-06-15-29-00.gh-issue-100814.h195gW.rst b/Misc/NEWS.d/next/Library/2023-08-06-15-29-00.gh-issue-100814.h195gW.rst new file mode 100644 index 00000000000000..86cb7bf79f3078 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-08-06-15-29-00.gh-issue-100814.h195gW.rst @@ -0,0 +1,2 @@ +Passing a callable object as an option value to a Tkinter image now raises +the expected TclError instead of an AttributeError. From 9e87e07aa422f95a42073b7ce750fbe19842f960 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 16 Aug 2023 01:15:38 -0700 Subject: [PATCH 140/632] [3.11] gh-91795: Update build optimization part of PCbuild/readme.txt (GH-91849) (GH-107777) (cherry picked from commit 906b73be5eada1995bd667a02c59f7a11998310f) Co-authored-by: Fatih <77548106+fatihkabakk@users.noreply.github.com> --- PCbuild/readme.txt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/PCbuild/readme.txt b/PCbuild/readme.txt index e9a09172274671..092b2cc8c1a7f7 100644 --- a/PCbuild/readme.txt +++ b/PCbuild/readme.txt @@ -250,9 +250,11 @@ against a profiling library and contain extra debug information. The PGUpdate configuration takes the profiling data and generates optimized binaries. -The build_pgo.bat script automates the creation of optimized binaries. -It creates the PGI files, runs the unit test suite or PyBench with the -PGI python, and finally creates the optimized files. +The build.bat script has an argument `--pgo` that automate the creation +of optimized binaries. +It creates the PGI files, runs the unit test suite with the PGI python, +and finally creates the optimized files. +You can customize the job for profiling with `--pgo-job ` option. See https://docs.microsoft.com/en-us/cpp/build/profile-guided-optimizations From 4f35d4f6f258da5fed468cd5561c1171a3735f55 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 16 Aug 2023 01:34:36 -0700 Subject: [PATCH 141/632] [3.11] gh-107237: Fix test_udp_reconnection() of test_logging (GH-107238) (GH-107243) test_logging: Fix test_udp_reconnection() by increasing the timeout from 100 ms to 5 minutes (LONG_TIMEOUT). Replace also blocking wait() with wait(LONG_TIMEOUT) in test_output() to prevent the test to hang. (cherry picked from commit ed082383272c2c238e364e9cc83229234aee23cc) Co-authored-by: Victor Stinner From 26137e2cf70b31e93b8bb26287647915bcaa5c99 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 16 Aug 2023 11:36:36 +0300 Subject: [PATCH 142/632] [3.11] gh-100061: Proper fix of the bug in the matching of possessive quantifiers (GH-102612) (GH-108004) Restore the global Input Stream pointer after trying to match a sub-pattern. Co-authored-by: Ma Lin (cherry picked from commit abd9cc52d94b8e2835322b62c29f09bb0e6fcfe9) Co-authored-by: SKO <41810398+uyw4687@users.noreply.github.com> --- Lib/re/_compiler.py | 7 ------- Lib/test/test_re.py | 12 ++++++++++-- .../2023-03-14-01-19-57.gh-issue-100061.CiXJYn.rst | 2 ++ Modules/_sre/sre_lib.h | 4 ++++ 4 files changed, 16 insertions(+), 9 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-03-14-01-19-57.gh-issue-100061.CiXJYn.rst diff --git a/Lib/re/_compiler.py b/Lib/re/_compiler.py index e30740b9c30b0e..d8e0d2fdefdcca 100644 --- a/Lib/re/_compiler.py +++ b/Lib/re/_compiler.py @@ -100,13 +100,6 @@ def _compile(code, pattern, flags): emit(ANY_ALL) else: emit(ANY) - elif op is POSSESSIVE_REPEAT: - # gh-106052: Possessive quantifiers do not work when the - # subpattern contains backtracking, i.e. "(?:ab?c)*+". - # Implement it as equivalent greedy qualifier in atomic group. - p = [(MAX_REPEAT, av)] - p = [(ATOMIC_GROUP, p)] - _compile(code, p, flags) elif op in REPEATING_CODES: if flags & SRE_FLAG_TEMPLATE: raise error("internal: unsupported template operator %r" % (op,)) diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index d2736dae20b1b4..f4d64dc9fcf2ae 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -2397,6 +2397,16 @@ def test_template_function_and_flag_is_deprecated(self): self.assertFalse(template_re1.match('nope')) def test_bug_gh106052(self): + # gh-100061 + self.assertEqual(re.match('(?>(?:.(?!D))+)', 'ABCDE').span(), (0, 2)) + self.assertEqual(re.match('(?:.(?!D))++', 'ABCDE').span(), (0, 2)) + self.assertEqual(re.match('(?>(?:.(?!D))*)', 'ABCDE').span(), (0, 2)) + self.assertEqual(re.match('(?:.(?!D))*+', 'ABCDE').span(), (0, 2)) + self.assertEqual(re.match('(?>(?:.(?!D))?)', 'CDE').span(), (0, 0)) + self.assertEqual(re.match('(?:.(?!D))?+', 'CDE').span(), (0, 0)) + self.assertEqual(re.match('(?>(?:.(?!D)){1,3})', 'ABCDE').span(), (0, 2)) + self.assertEqual(re.match('(?:.(?!D)){1,3}+', 'ABCDE').span(), (0, 2)) + # gh-106052 self.assertEqual(re.match("(?>(?:ab?c)+)", "aca").span(), (0, 2)) self.assertEqual(re.match("(?:ab?c)++", "aca").span(), (0, 2)) self.assertEqual(re.match("(?>(?:ab?c)*)", "aca").span(), (0, 2)) @@ -2502,7 +2512,6 @@ def test_atomic_group(self): 17: SUCCESS ''') - @unittest.expectedFailure # gh-106052 def test_possesive_repeat_one(self): self.assertEqual(get_debug_out(r'a?+'), '''\ POSSESSIVE_REPEAT 0 1 @@ -2515,7 +2524,6 @@ def test_possesive_repeat_one(self): 12: SUCCESS ''') - @unittest.expectedFailure # gh-106052 def test_possesive_repeat(self): self.assertEqual(get_debug_out(r'(?:ab)?+'), '''\ POSSESSIVE_REPEAT 0 1 diff --git a/Misc/NEWS.d/next/Library/2023-03-14-01-19-57.gh-issue-100061.CiXJYn.rst b/Misc/NEWS.d/next/Library/2023-03-14-01-19-57.gh-issue-100061.CiXJYn.rst new file mode 100644 index 00000000000000..dfed34f6ae9768 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-03-14-01-19-57.gh-issue-100061.CiXJYn.rst @@ -0,0 +1,2 @@ +Fix a bug that causes wrong matches for regular expressions with possessive +qualifier. diff --git a/Modules/_sre/sre_lib.h b/Modules/_sre/sre_lib.h index fb4c18b63d643d..e83149825e2cdb 100644 --- a/Modules/_sre/sre_lib.h +++ b/Modules/_sre/sre_lib.h @@ -1334,6 +1334,10 @@ SRE(match)(SRE_STATE* state, const SRE_CODE* pattern, int toplevel) MARK_POP(ctx->lastmark); LASTMARK_RESTORE(); + /* Restore the global Input Stream pointer + since it can change after jumps. */ + state->ptr = ptr; + /* We have sufficient matches, so exit loop. */ break; } From c1c3f0bfec9de0fab35027cdec627f18e6c05c86 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 16 Aug 2023 02:15:01 -0700 Subject: [PATCH 143/632] [3.11] bpo-18319: gettext() can retrieve a message even if a plural form exists (GH-19869) (GH-107107) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 54632528eeba841e4a8cc95ecbd84c9aca8eef57) Co-authored-by: Gilles Bassière --- Lib/gettext.py | 10 ++++++---- Lib/test/test_gettext.py | 4 ++++ .../Library/2020-05-03-00-33-15.bpo-18319.faPTlx.rst | 2 ++ 3 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2020-05-03-00-33-15.bpo-18319.faPTlx.rst diff --git a/Lib/gettext.py b/Lib/gettext.py index 57f1a449c28a43..b72b15f82d4355 100644 --- a/Lib/gettext.py +++ b/Lib/gettext.py @@ -422,10 +422,12 @@ def gettext(self, message): missing = object() tmsg = self._catalog.get(message, missing) if tmsg is missing: - if self._fallback: - return self._fallback.gettext(message) - return message - return tmsg + tmsg = self._catalog.get((message, self.plural(1)), missing) + if tmsg is not missing: + return tmsg + if self._fallback: + return self._fallback.gettext(message) + return message def ngettext(self, msgid1, msgid2, n): try: diff --git a/Lib/test/test_gettext.py b/Lib/test/test_gettext.py index 7f7b51c19f35c7..8430fc234d00ee 100644 --- a/Lib/test/test_gettext.py +++ b/Lib/test/test_gettext.py @@ -320,6 +320,8 @@ def test_plural_forms1(self): eq(x, 'Hay %s fichero') x = gettext.ngettext('There is %s file', 'There are %s files', 2) eq(x, 'Hay %s ficheros') + x = gettext.gettext('There is %s file') + eq(x, 'Hay %s fichero') def test_plural_context_forms1(self): eq = self.assertEqual @@ -340,6 +342,8 @@ def test_plural_forms2(self): eq(x, 'Hay %s fichero') x = t.ngettext('There is %s file', 'There are %s files', 2) eq(x, 'Hay %s ficheros') + x = t.gettext('There is %s file') + eq(x, 'Hay %s fichero') def test_plural_context_forms2(self): eq = self.assertEqual diff --git a/Misc/NEWS.d/next/Library/2020-05-03-00-33-15.bpo-18319.faPTlx.rst b/Misc/NEWS.d/next/Library/2020-05-03-00-33-15.bpo-18319.faPTlx.rst new file mode 100644 index 00000000000000..94d7cc9deadbb1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-05-03-00-33-15.bpo-18319.faPTlx.rst @@ -0,0 +1,2 @@ +Ensure ``gettext(msg)`` retrieve translations even if a plural form exists. In +other words: ``gettext(msg) == ngettext(msg, '', 1)``. From 6b825c1b8a14460641ca6f1647d83005c68199aa Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 16 Aug 2023 02:59:44 -0700 Subject: [PATCH 144/632] [3.11] gh-106300: Improve errors testing in test_unittest.test_runner (GH-106737) (GH-108007) Use a custom exception to prevent unintentional silence of actual errors. (cherry picked from commit fd9d70a94de5b0756b52b9ae21e236e25545db4f) Co-authored-by: Nikita Sobolev --- Lib/unittest/test/test_runner.py | 117 +++++++++++++++++-------------- 1 file changed, 65 insertions(+), 52 deletions(-) diff --git a/Lib/unittest/test/test_runner.py b/Lib/unittest/test/test_runner.py index e02b2dadc8fdbb..3aefc1e0a5fdd1 100644 --- a/Lib/unittest/test/test_runner.py +++ b/Lib/unittest/test/test_runner.py @@ -21,6 +21,13 @@ def getRunner(): stream=io.StringIO()) +class CustomError(Exception): + pass + +# For test output compat: +CustomErrorRepr = f"{__name__ + '.' if __name__ != '__main__' else ''}CustomError" + + def runTests(*cases): suite = unittest.TestSuite() for case in cases: @@ -43,7 +50,7 @@ def cleanup(ordering, blowUp=False): ordering.append('cleanup_good') else: ordering.append('cleanup_exc') - raise Exception('CleanUpExc') + raise CustomError('CleanUpExc') class TestCM: @@ -105,8 +112,8 @@ def testNothing(self): result = unittest.TestResult() outcome = test._outcome = _Outcome(result=result) - CleanUpExc = Exception('foo') - exc2 = Exception('bar') + CleanUpExc = CustomError('foo') + exc2 = CustomError('bar') def cleanup1(): raise CleanUpExc @@ -122,10 +129,10 @@ def cleanup2(): (_, msg2), (_, msg1) = result.errors self.assertIn('in cleanup1', msg1) self.assertIn('raise CleanUpExc', msg1) - self.assertIn('Exception: foo', msg1) + self.assertIn(f'{CustomErrorRepr}: foo', msg1) self.assertIn('in cleanup2', msg2) self.assertIn('raise exc2', msg2) - self.assertIn('Exception: bar', msg2) + self.assertIn(f'{CustomErrorRepr}: bar', msg2) def testCleanupInRun(self): blowUp = False @@ -136,7 +143,7 @@ def setUp(self): ordering.append('setUp') test.addCleanup(cleanup2) if blowUp: - raise Exception('foo') + raise CustomError('foo') def testNothing(self): ordering.append('test') @@ -277,7 +284,7 @@ def setUpClass(cls): ordering.append('setUpClass') cls.addClassCleanup(cleanup, ordering) if blowUp: - raise Exception() + raise CustomError() def testNothing(self): ordering.append('test') @classmethod @@ -303,7 +310,7 @@ def setUpClass(cls): ordering.append('setUpClass') cls.addClassCleanup(cleanup, ordering) if blowUp: - raise Exception() + raise CustomError() def testNothing(self): ordering.append('test') @classmethod @@ -343,7 +350,7 @@ def tearDownClass(cls): ordering = [] blowUp = True suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestableTest) - with self.assertRaises(Exception) as cm: + with self.assertRaises(CustomError) as cm: suite.debug() self.assertEqual(str(cm.exception), 'CleanUpExc') self.assertEqual(ordering, @@ -363,10 +370,10 @@ def testNothing(self): @classmethod def tearDownClass(cls): ordering.append('tearDownClass') - raise Exception('TearDownClassExc') + raise CustomError('TearDownClassExc') suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestableTest) - with self.assertRaises(Exception) as cm: + with self.assertRaises(CustomError) as cm: suite.debug() self.assertEqual(str(cm.exception), 'TearDownClassExc') self.assertEqual(ordering, ['setUpClass', 'test', 'tearDownClass']) @@ -376,7 +383,7 @@ def tearDownClass(cls): ordering = [] blowUp = True suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestableTest) - with self.assertRaises(Exception) as cm: + with self.assertRaises(CustomError) as cm: suite.debug() self.assertEqual(str(cm.exception), 'TearDownClassExc') self.assertEqual(ordering, ['setUpClass', 'test', 'tearDownClass']) @@ -389,16 +396,22 @@ def testNothing(self): pass def cleanup1(): - raise Exception('cleanup1') + raise CustomError('cleanup1') def cleanup2(): - raise Exception('cleanup2') + raise CustomError('cleanup2') TestableTest.addClassCleanup(cleanup1) TestableTest.addClassCleanup(cleanup2) - with self.assertRaises(Exception) as e: - TestableTest.doClassCleanups() - self.assertEqual(e, 'cleanup1') + TestableTest.doClassCleanups() + + self.assertEqual(len(TestableTest.tearDown_exceptions), 2) + + e1, e2 = TestableTest.tearDown_exceptions + self.assertIsInstance(e1[1], CustomError) + self.assertEqual(str(e1[1]), 'cleanup2') + self.assertIsInstance(e2[1], CustomError) + self.assertEqual(str(e2[1]), 'cleanup1') def test_with_errors_addCleanUp(self): ordering = [] @@ -418,7 +431,7 @@ def tearDownClass(cls): result = runTests(TestableTest) self.assertEqual(result.errors[0][1].splitlines()[-1], - 'Exception: CleanUpExc') + f'{CustomErrorRepr}: CleanUpExc') self.assertEqual(ordering, ['setUpClass', 'setUp', 'cleanup_exc', 'tearDownClass', 'cleanup_good']) @@ -441,7 +454,7 @@ def tearDownClass(cls): result = runTests(TestableTest) self.assertEqual(result.errors[0][1].splitlines()[-1], - 'Exception: CleanUpExc') + f'{CustomErrorRepr}: CleanUpExc') self.assertEqual(ordering, ['setUpClass', 'setUp', 'test', 'cleanup_good', 'tearDownClass', 'cleanup_exc']) @@ -457,11 +470,11 @@ def setUpClass(cls): ordering.append('setUpClass') cls.addClassCleanup(cleanup, ordering, blowUp=True) if class_blow_up: - raise Exception('ClassExc') + raise CustomError('ClassExc') def setUp(self): ordering.append('setUp') if method_blow_up: - raise Exception('MethodExc') + raise CustomError('MethodExc') def testNothing(self): ordering.append('test') @classmethod @@ -470,7 +483,7 @@ def tearDownClass(cls): result = runTests(TestableTest) self.assertEqual(result.errors[0][1].splitlines()[-1], - 'Exception: CleanUpExc') + f'{CustomErrorRepr}: CleanUpExc') self.assertEqual(ordering, ['setUpClass', 'setUp', 'test', 'tearDownClass', 'cleanup_exc']) @@ -480,9 +493,9 @@ def tearDownClass(cls): method_blow_up = False result = runTests(TestableTest) self.assertEqual(result.errors[0][1].splitlines()[-1], - 'Exception: ClassExc') + f'{CustomErrorRepr}: ClassExc') self.assertEqual(result.errors[1][1].splitlines()[-1], - 'Exception: CleanUpExc') + f'{CustomErrorRepr}: CleanUpExc') self.assertEqual(ordering, ['setUpClass', 'cleanup_exc']) @@ -491,9 +504,9 @@ def tearDownClass(cls): method_blow_up = True result = runTests(TestableTest) self.assertEqual(result.errors[0][1].splitlines()[-1], - 'Exception: MethodExc') + f'{CustomErrorRepr}: MethodExc') self.assertEqual(result.errors[1][1].splitlines()[-1], - 'Exception: CleanUpExc') + f'{CustomErrorRepr}: CleanUpExc') self.assertEqual(ordering, ['setUpClass', 'setUp', 'tearDownClass', 'cleanup_exc']) @@ -510,11 +523,11 @@ def testNothing(self): @classmethod def tearDownClass(cls): ordering.append('tearDownClass') - raise Exception('TearDownExc') + raise CustomError('TearDownExc') result = runTests(TestableTest) self.assertEqual(result.errors[0][1].splitlines()[-1], - 'Exception: TearDownExc') + f'{CustomErrorRepr}: TearDownExc') self.assertEqual(ordering, ['setUpClass', 'test', 'tearDownClass', 'cleanup_good']) @@ -607,7 +620,7 @@ def module_cleanup_good(*args, **kwargs): module_cleanups.append((3, args, kwargs)) def module_cleanup_bad(*args, **kwargs): - raise Exception('CleanUpExc') + raise CustomError('CleanUpExc') class Module(object): unittest.addModuleCleanup(module_cleanup_good, 1, 2, 3, @@ -617,7 +630,7 @@ class Module(object): [(module_cleanup_good, (1, 2, 3), dict(four='hello', five='goodbye')), (module_cleanup_bad, (), {})]) - with self.assertRaises(Exception) as e: + with self.assertRaises(CustomError) as e: unittest.case.doModuleCleanups() self.assertEqual(str(e.exception), 'CleanUpExc') self.assertEqual(unittest.case._module_cleanups, []) @@ -646,7 +659,7 @@ def setUpModule(): ordering.append('setUpModule') unittest.addModuleCleanup(cleanup, ordering) if blowUp: - raise Exception('setUpModule Exc') + raise CustomError('setUpModule Exc') @staticmethod def tearDownModule(): ordering.append('tearDownModule') @@ -666,7 +679,7 @@ def tearDownClass(cls): result = runTests(TestableTest) self.assertEqual(ordering, ['setUpModule', 'cleanup_good']) self.assertEqual(result.errors[0][1].splitlines()[-1], - 'Exception: setUpModule Exc') + f'{CustomErrorRepr}: setUpModule Exc') ordering = [] blowUp = False @@ -686,7 +699,7 @@ def setUpModule(): ordering.append('setUpModule') unittest.addModuleCleanup(cleanup, ordering) if blowUp: - raise Exception() + raise CustomError() @staticmethod def tearDownModule(): ordering.append('tearDownModule') @@ -697,7 +710,7 @@ def setUpModule(): ordering.append('setUpModule2') unittest.addModuleCleanup(cleanup, ordering) if blowUp2: - raise Exception() + raise CustomError() @staticmethod def tearDownModule(): ordering.append('tearDownModule2') @@ -786,7 +799,7 @@ def setUpModule(): @staticmethod def tearDownModule(): ordering.append('tearDownModule') - raise Exception('CleanUpExc') + raise CustomError('CleanUpExc') class TestableTest(unittest.TestCase): @classmethod @@ -802,7 +815,7 @@ def tearDownClass(cls): sys.modules['Module'] = Module result = runTests(TestableTest) self.assertEqual(result.errors[0][1].splitlines()[-1], - 'Exception: CleanUpExc') + f'{CustomErrorRepr}: CleanUpExc') self.assertEqual(ordering, ['setUpModule', 'setUpClass', 'test', 'tearDownClass', 'tearDownModule', 'cleanup_good']) @@ -842,7 +855,7 @@ def tearDownClass(cls): ordering = [] blowUp = True suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestableTest) - with self.assertRaises(Exception) as cm: + with self.assertRaises(CustomError) as cm: suite.debug() self.assertEqual(str(cm.exception), 'CleanUpExc') self.assertEqual(ordering, ['setUpModule', 'setUpClass', 'test', @@ -860,7 +873,7 @@ def setUpModule(): @staticmethod def tearDownModule(): ordering.append('tearDownModule') - raise Exception('TearDownModuleExc') + raise CustomError('TearDownModuleExc') class TestableTest(unittest.TestCase): @classmethod @@ -875,7 +888,7 @@ def tearDownClass(cls): TestableTest.__module__ = 'Module' sys.modules['Module'] = Module suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestableTest) - with self.assertRaises(Exception) as cm: + with self.assertRaises(CustomError) as cm: suite.debug() self.assertEqual(str(cm.exception), 'TearDownModuleExc') self.assertEqual(ordering, ['setUpModule', 'setUpClass', 'test', @@ -886,7 +899,7 @@ def tearDownClass(cls): ordering = [] blowUp = True suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestableTest) - with self.assertRaises(Exception) as cm: + with self.assertRaises(CustomError) as cm: suite.debug() self.assertEqual(str(cm.exception), 'TearDownModuleExc') self.assertEqual(ordering, ['setUpModule', 'setUpClass', 'test', @@ -965,7 +978,7 @@ def tearDownClass(cls): result = runTests(TestableTest) self.assertEqual(result.errors[0][1].splitlines()[-1], - 'Exception: CleanUpExc') + f'{CustomErrorRepr}: CleanUpExc') self.assertEqual(ordering, ['setUpModule', 'setUpClass', 'test', 'tearDownClass', 'cleanup_exc', 'tearDownModule', 'cleanup_good']) @@ -995,7 +1008,7 @@ def tearDown(self): result = runTests(TestableTest) self.assertEqual(result.errors[0][1].splitlines()[-1], - 'Exception: CleanUpExc') + f'{CustomErrorRepr}: CleanUpExc') self.assertEqual(ordering, ['setUpModule', 'setUp', 'test', 'tearDown', 'cleanup_exc', 'tearDownModule', 'cleanup_good']) @@ -1011,7 +1024,7 @@ def setUpModule(): ordering.append('setUpModule') unittest.addModuleCleanup(cleanup, ordering, blowUp=True) if module_blow_up: - raise Exception('ModuleExc') + raise CustomError('ModuleExc') @staticmethod def tearDownModule(): ordering.append('tearDownModule') @@ -1021,11 +1034,11 @@ class TestableTest(unittest.TestCase): def setUpClass(cls): ordering.append('setUpClass') if class_blow_up: - raise Exception('ClassExc') + raise CustomError('ClassExc') def setUp(self): ordering.append('setUp') if method_blow_up: - raise Exception('MethodExc') + raise CustomError('MethodExc') def testNothing(self): ordering.append('test') @classmethod @@ -1037,7 +1050,7 @@ def tearDownClass(cls): result = runTests(TestableTest) self.assertEqual(result.errors[0][1].splitlines()[-1], - 'Exception: CleanUpExc') + f'{CustomErrorRepr}: CleanUpExc') self.assertEqual(ordering, ['setUpModule', 'setUpClass', 'setUp', 'test', 'tearDownClass', 'tearDownModule', @@ -1049,9 +1062,9 @@ def tearDownClass(cls): method_blow_up = False result = runTests(TestableTest) self.assertEqual(result.errors[0][1].splitlines()[-1], - 'Exception: ModuleExc') + f'{CustomErrorRepr}: ModuleExc') self.assertEqual(result.errors[1][1].splitlines()[-1], - 'Exception: CleanUpExc') + f'{CustomErrorRepr}: CleanUpExc') self.assertEqual(ordering, ['setUpModule', 'cleanup_exc']) ordering = [] @@ -1060,9 +1073,9 @@ def tearDownClass(cls): method_blow_up = False result = runTests(TestableTest) self.assertEqual(result.errors[0][1].splitlines()[-1], - 'Exception: ClassExc') + f'{CustomErrorRepr}: ClassExc') self.assertEqual(result.errors[1][1].splitlines()[-1], - 'Exception: CleanUpExc') + f'{CustomErrorRepr}: CleanUpExc') self.assertEqual(ordering, ['setUpModule', 'setUpClass', 'tearDownModule', 'cleanup_exc']) @@ -1072,9 +1085,9 @@ def tearDownClass(cls): method_blow_up = True result = runTests(TestableTest) self.assertEqual(result.errors[0][1].splitlines()[-1], - 'Exception: MethodExc') + f'{CustomErrorRepr}: MethodExc') self.assertEqual(result.errors[1][1].splitlines()[-1], - 'Exception: CleanUpExc') + f'{CustomErrorRepr}: CleanUpExc') self.assertEqual(ordering, ['setUpModule', 'setUpClass', 'setUp', 'tearDownClass', 'tearDownModule', 'cleanup_exc']) From 05ff5fa11d5e082906042ef7b48163c2f0f2c888 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 16 Aug 2023 12:02:11 -0700 Subject: [PATCH 145/632] [3.11] Remove Sphinx problem matcher to avoid annotating unchanged files (GH-108005) (#108050) Co-authored-by: Hugo van Kemenade --- .github/problem-matchers/sphinx.json | 40 ---------------------------- .github/workflows/reusable-docs.yml | 4 --- 2 files changed, 44 deletions(-) delete mode 100644 .github/problem-matchers/sphinx.json diff --git a/.github/problem-matchers/sphinx.json b/.github/problem-matchers/sphinx.json deleted file mode 100644 index 09848608a7b034..00000000000000 --- a/.github/problem-matchers/sphinx.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "problemMatcher": [ - { - "owner": "sphinx-problem-matcher", - "pattern": [ - { - "regexp": "^(.*):(\\d+):\\s+(\\w*):\\s+(.*)$", - "file": 1, - "line": 2, - "severity": 3, - "message": 4 - } - ] - }, - { - "owner": "sphinx-problem-matcher-loose", - "pattern": [ - { - "_comment": "A bit of a looser pattern, doesn't look for line numbers, just looks for file names relying on them to start with / and end with .rst", - "regexp": "(\/.*\\.rst):\\s+(\\w*):\\s+(.*)$", - "file": 1, - "severity": 2, - "message": 3 - } - ] - }, - { - "owner": "sphinx-problem-matcher-loose-no-severity", - "pattern": [ - { - "_comment": "Looks for file names ending with .rst and line numbers but without severity", - "regexp": "^(.*\\.rst):(\\d+):(.*)$", - "file": 1, - "line": 2, - "message": 3 - } - ] - } - ] -} \ No newline at end of file diff --git a/.github/workflows/reusable-docs.yml b/.github/workflows/reusable-docs.yml index eade14bc8d6191..9c1ed2788080dc 100644 --- a/.github/workflows/reusable-docs.yml +++ b/.github/workflows/reusable-docs.yml @@ -18,8 +18,6 @@ jobs: timeout-minutes: 60 steps: - uses: actions/checkout@v3 - - name: Register Sphinx problem matcher - run: echo "::add-matcher::.github/problem-matchers/sphinx.json" - name: 'Set up Python' uses: actions/setup-python@v4 with: @@ -65,8 +63,6 @@ jobs: timeout-minutes: 60 steps: - uses: actions/checkout@v3 - - name: Register Sphinx problem matcher - run: echo "::add-matcher::.github/problem-matchers/sphinx.json" - uses: actions/cache@v3 with: path: ~/.cache/pip From 4e5dac1fe17cd7bbc8dad19e3e33c249a8a203c2 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 16 Aug 2023 13:32:17 -0700 Subject: [PATCH 146/632] [3.11] gh-107298: Fix C API Buffer documentation (GH-108011) (#108041) gh-107298: Fix C API Buffer documentation (GH-108011) (cherry picked from commit c2941cba7a986e6158eebb2a0bf33906dcd78616) Co-authored-by: Victor Stinner --- Doc/c-api/buffer.rst | 22 +++++++++++++--------- Doc/c-api/typeobj.rst | 2 +- Doc/tools/.nitignore | 1 - 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/Doc/c-api/buffer.rst b/Doc/c-api/buffer.rst index ba391a5279f205..e572815ffd6259 100644 --- a/Doc/c-api/buffer.rst +++ b/Doc/c-api/buffer.rst @@ -163,13 +163,6 @@ a buffer, see :c:func:`PyObject_GetBuffer`. and :c:member:`~Py_buffer.suboffsets` MUST be ``NULL``. The maximum number of dimensions is given by :c:macro:`PyBUF_MAX_NDIM`. - .. :c:macro:: PyBUF_MAX_NDIM - - The maximum number of dimensions the memory represents. - Exporters MUST respect this limit, consumers of multi-dimensional - buffers SHOULD be able to handle up to :c:macro:`!PyBUF_MAX_NDIM` dimensions. - Currently set to 64. - .. c:member:: Py_ssize_t *shape An array of :c:type:`Py_ssize_t` of length :c:member:`~Py_buffer.ndim` @@ -221,6 +214,17 @@ a buffer, see :c:func:`PyObject_GetBuffer`. freed when the buffer is released. The consumer MUST NOT alter this value. + +Constants: + +.. c:macro:: PyBUF_MAX_NDIM + + The maximum number of dimensions the memory represents. + Exporters MUST respect this limit, consumers of multi-dimensional + buffers SHOULD be able to handle up to :c:macro:`!PyBUF_MAX_NDIM` dimensions. + Currently set to 64. + + .. _buffer-request-types: Buffer request types @@ -444,7 +448,7 @@ Buffer-related functions Send a request to *exporter* to fill in *view* as specified by *flags*. If the exporter cannot provide a buffer of the exact type, it MUST raise - :c:data:`PyExc_BufferError`, set ``view->obj`` to ``NULL`` and + :exc:`BufferError`, set ``view->obj`` to ``NULL`` and return ``-1``. On success, fill in *view*, set ``view->obj`` to a new reference @@ -531,7 +535,7 @@ Buffer-related functions and :c:macro:`PyBUF_WRITABLE` is set in *flags*. On success, set ``view->obj`` to a new reference to *exporter* and - return 0. Otherwise, raise :c:data:`PyExc_BufferError`, set + return 0. Otherwise, raise :exc:`BufferError`, set ``view->obj`` to ``NULL`` and return ``-1``; If this function is used as part of a :ref:`getbufferproc `, diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 40cbcba63d69a6..c2ed4e4391c892 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -2357,7 +2357,7 @@ Buffer Object Structures Except for point (3), an implementation of this function MUST take these steps: - (1) Check if the request can be met. If not, raise :c:data:`PyExc_BufferError`, + (1) Check if the request can be met. If not, raise :exc:`BufferError`, set :c:expr:`view->obj` to ``NULL`` and return ``-1``. (2) Fill in the requested fields. diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index df7dcd607489c8..1859d53e152849 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -2,7 +2,6 @@ # as tested on the CI via check-warnings.py in reusable-docs.yml. # Keep lines sorted lexicographically to help avoid merge conflicts. -Doc/c-api/buffer.rst Doc/c-api/datetime.rst Doc/c-api/descriptor.rst Doc/c-api/exceptions.rst From ba2d6c9d1a796433aa44dfd69d0f2879e4b16639 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 16 Aug 2023 13:50:49 -0700 Subject: [PATCH 147/632] [3.11] GH-92584: Redirect macOS package installation to the PPUG (GH-108044) (#108059) GH-92584: Redirect macOS package installation to the PPUG (GH-108044) (cherry picked from commit 902864256cb261428ae9682ca0ffddd597e1f894) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/using/mac.rst | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Doc/using/mac.rst b/Doc/using/mac.rst index 69cd5c92d884d0..84031a3d6a17b3 100644 --- a/Doc/using/mac.rst +++ b/Doc/using/mac.rst @@ -125,13 +125,9 @@ http://www.hashcollision.org/hkn/python/idle_intro/index.html. Installing Additional Python Packages ===================================== -There are several methods to install additional Python packages: +This section has moved to the `Python Packaging User Guide`_. -* Packages can be installed via the standard Python distutils mode (``python - setup.py install``). - -* Many packages can also be installed via the :program:`setuptools` extension - or :program:`pip` wrapper, see https://pip.pypa.io/. +.. _Python Packaging User Guide: https://packaging.python.org/en/latest/tutorials/installing-packages/ GUI Programming on the Mac From e3a11e12ab912f0614a90ade7acd34dda7e7f15e Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 16 Aug 2023 22:01:27 +0100 Subject: [PATCH 148/632] [3.11] GH-92584: Remove reference to Distutils in ``cx_Freeze``'s description (GH-108047) (#108061) Remove reference to Distutils in ``cx_Freeze``'s description. (cherry picked from commit 57fcf96e4f21b8955b3ae4b4d70e4b756949712f) --- Doc/using/windows.rst | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst index 2a07e42f8443ce..7dee82a2223a26 100644 --- a/Doc/using/windows.rst +++ b/Doc/using/windows.rst @@ -1233,11 +1233,10 @@ shipped with PyWin32. It is an embeddable IDE with a built-in debugger. cx_Freeze --------- -`cx_Freeze `_ is a :mod:`distutils` -extension (see :ref:`extending-distutils`) which wraps Python scripts into -executable Windows programs (:file:`{*}.exe` files). When you have done this, -you can distribute your application without requiring your users to install -Python. +`cx_Freeze `_ +wraps Python scripts into executable Windows programs +(:file:`{*}.exe` files). When you have done this, you can distribute your +application without requiring your users to install Python. Compiling Python on Windows From b8e62cfb01f072cc18d32c23e38b3f1107f19a7f Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 16 Aug 2023 23:54:53 -0700 Subject: [PATCH 149/632] [3.11] gh-107298: Add standard exceptions and warnings in the nitpick_ignore list (GH-108029) (GH-108071) (cherry picked from commit c9d83f93d804b80ee14480466ebee63a6f97dac2) Co-authored-by: Serhiy Storchaka --- Doc/conf.py | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/Doc/conf.py b/Doc/conf.py index d9e7c9f11e7c8c..cfe34244fee390 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -149,6 +149,77 @@ ('envvar', 'USER'), ('envvar', 'USERNAME'), ('envvar', 'USERPROFILE'), +] + +# Temporary undocumented names. +# In future this list must be empty. +nitpick_ignore += [ + # C API: Standard Python exception classes + ('c:data', 'PyExc_ArithmeticError'), + ('c:data', 'PyExc_AssertionError'), + ('c:data', 'PyExc_AttributeError'), + ('c:data', 'PyExc_BaseException'), + ('c:data', 'PyExc_BlockingIOError'), + ('c:data', 'PyExc_BrokenPipeError'), + ('c:data', 'PyExc_BufferError'), + ('c:data', 'PyExc_ChildProcessError'), + ('c:data', 'PyExc_ConnectionAbortedError'), + ('c:data', 'PyExc_ConnectionError'), + ('c:data', 'PyExc_ConnectionRefusedError'), + ('c:data', 'PyExc_ConnectionResetError'), + ('c:data', 'PyExc_EOFError'), + ('c:data', 'PyExc_Exception'), + ('c:data', 'PyExc_FileExistsError'), + ('c:data', 'PyExc_FileNotFoundError'), + ('c:data', 'PyExc_FloatingPointError'), + ('c:data', 'PyExc_GeneratorExit'), + ('c:data', 'PyExc_ImportError'), + ('c:data', 'PyExc_IndentationError'), + ('c:data', 'PyExc_IndexError'), + ('c:data', 'PyExc_InterruptedError'), + ('c:data', 'PyExc_IsADirectoryError'), + ('c:data', 'PyExc_KeyboardInterrupt'), + ('c:data', 'PyExc_KeyError'), + ('c:data', 'PyExc_LookupError'), + ('c:data', 'PyExc_MemoryError'), + ('c:data', 'PyExc_ModuleNotFoundError'), + ('c:data', 'PyExc_NameError'), + ('c:data', 'PyExc_NotADirectoryError'), + ('c:data', 'PyExc_NotImplementedError'), + ('c:data', 'PyExc_OSError'), + ('c:data', 'PyExc_OverflowError'), + ('c:data', 'PyExc_PermissionError'), + ('c:data', 'PyExc_ProcessLookupError'), + ('c:data', 'PyExc_RecursionError'), + ('c:data', 'PyExc_ReferenceError'), + ('c:data', 'PyExc_RuntimeError'), + ('c:data', 'PyExc_StopAsyncIteration'), + ('c:data', 'PyExc_StopIteration'), + ('c:data', 'PyExc_SyntaxError'), + ('c:data', 'PyExc_SystemError'), + ('c:data', 'PyExc_SystemExit'), + ('c:data', 'PyExc_TabError'), + ('c:data', 'PyExc_TimeoutError'), + ('c:data', 'PyExc_TypeError'), + ('c:data', 'PyExc_UnboundLocalError'), + ('c:data', 'PyExc_UnicodeDecodeError'), + ('c:data', 'PyExc_UnicodeEncodeError'), + ('c:data', 'PyExc_UnicodeError'), + ('c:data', 'PyExc_UnicodeTranslateError'), + ('c:data', 'PyExc_ValueError'), + ('c:data', 'PyExc_ZeroDivisionError'), + # C API: Standard Python warning classes + ('c:data', 'PyExc_BytesWarning'), + ('c:data', 'PyExc_DeprecationWarning'), + ('c:data', 'PyExc_FutureWarning'), + ('c:data', 'PyExc_ImportWarning'), + ('c:data', 'PyExc_PendingDeprecationWarning'), + ('c:data', 'PyExc_ResourceWarning'), + ('c:data', 'PyExc_RuntimeWarning'), + ('c:data', 'PyExc_SyntaxWarning'), + ('c:data', 'PyExc_UnicodeWarning'), + ('c:data', 'PyExc_UserWarning'), + ('c:data', 'PyExc_Warning'), # Do not error nit-picky mode builds when _SubParsersAction.add_parser cannot # be resolved, as the method is currently undocumented. For context, see # https://github.com/python/cpython/pull/103289. From 358b1acc69c09d110eecfca8fa6b72cea376c5f7 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 17 Aug 2023 11:30:47 +0300 Subject: [PATCH 150/632] [3.11] gh-107298: Fix some references in the C API documentation (GH-108072) (GH-108076) (cherry picked from commit f51f0466c07eabc6177c2f64f70c952dada050e8) --- Doc/c-api/typeobj.rst | 2 +- Doc/extending/extending.rst | 4 ++-- Doc/using/configure.rst | 4 ++-- Doc/whatsnew/2.2.rst | 2 +- Doc/whatsnew/2.3.rst | 2 +- Doc/whatsnew/2.4.rst | 2 +- Doc/whatsnew/2.6.rst | 6 +++--- Doc/whatsnew/2.7.rst | 12 ++++++------ Doc/whatsnew/3.0.rst | 2 +- Doc/whatsnew/3.1.rst | 2 +- Doc/whatsnew/3.2.rst | 2 +- 11 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index c2ed4e4391c892..62711802a783ae 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -1634,7 +1634,7 @@ and :c:data:`PyType_Type` effectively act as defaults.) to a pointer, are valid C99 address constants. However, the unary '&' operator applied to a non-static variable - like :c:func:`PyBaseObject_Type` is not required to produce an address + like :c:data:`PyBaseObject_Type` is not required to produce an address constant. Compilers may support this (gcc does), MSVC does not. Both compilers are strictly standard conforming in this particular behavior. diff --git a/Doc/extending/extending.rst b/Doc/extending/extending.rst index 72e30a68828072..68f8e0c6674657 100644 --- a/Doc/extending/extending.rst +++ b/Doc/extending/extending.rst @@ -242,7 +242,7 @@ needed to ensure that it will not be discarded, causing :c:data:`!SpamError` to become a dangling pointer. Should it become a dangling pointer, C code which raises the exception could cause a core dump or other unintended side effects. -We discuss the use of ``PyMODINIT_FUNC`` as a function return type later in this +We discuss the use of :c:macro:`PyMODINIT_FUNC` as a function return type later in this sample. The :exc:`!spam.error` exception can be raised in your extension module using a @@ -363,7 +363,7 @@ only non-\ ``static`` item defined in the module file:: return PyModule_Create(&spammodule); } -Note that PyMODINIT_FUNC declares the function as ``PyObject *`` return type, +Note that :c:macro:`PyMODINIT_FUNC` declares the function as ``PyObject *`` return type, declares any special linkage declarations required by the platform, and for C++ declares the function as ``extern "C"``. diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst index 468228f12cfa0a..25ba5084d5daa7 100644 --- a/Doc/using/configure.rst +++ b/Doc/using/configure.rst @@ -679,8 +679,8 @@ Extensions defined after the ``*shared*`` marker are built as dynamic libraries. The :file:`setup.py` script only builds C extensions as shared libraries using the :mod:`distutils` module. -The :c:macro:`PyAPI_FUNC()`, :c:macro:`PyAPI_API()` and -:c:macro:`PyMODINIT_FUNC()` macros of :file:`Include/pyport.h` are defined +The :c:macro:`PyAPI_FUNC()`, :c:macro:`PyAPI_DATA()` and +:c:macro:`PyMODINIT_FUNC` macros of :file:`Include/pyport.h` are defined differently depending if the ``Py_BUILD_CORE_MODULE`` macro is defined: * Use ``Py_EXPORTED_SYMBOL`` if the ``Py_BUILD_CORE_MODULE`` is defined diff --git a/Doc/whatsnew/2.2.rst b/Doc/whatsnew/2.2.rst index 44e9bd8d492bfc..7de48a40263034 100644 --- a/Doc/whatsnew/2.2.rst +++ b/Doc/whatsnew/2.2.rst @@ -1109,7 +1109,7 @@ code, none of the changes described here will affect you very much. definition tables to simplify implementation of methods with no arguments or a single untyped argument. Calling such methods is more efficient than calling a corresponding method that uses :c:macro:`METH_VARARGS`. Also, the old - :c:macro:`METH_OLDARGS` style of writing C methods is now officially deprecated. + :c:macro:`!METH_OLDARGS` style of writing C methods is now officially deprecated. * Two new wrapper functions, :c:func:`PyOS_snprintf` and :c:func:`PyOS_vsnprintf` were added to provide cross-platform implementations for the relatively new diff --git a/Doc/whatsnew/2.3.rst b/Doc/whatsnew/2.3.rst index 68a8917a943e78..4390f245d3ab46 100644 --- a/Doc/whatsnew/2.3.rst +++ b/Doc/whatsnew/2.3.rst @@ -1886,7 +1886,7 @@ Changes to Python's build process and to the C API include: (:file:`libpython2.3.so`) by supplying :option:`!--enable-shared` when running Python's :program:`configure` script. (Contributed by Ondrej Palkovsky.) -* The :c:macro:`DL_EXPORT` and :c:macro:`DL_IMPORT` macros are now deprecated. +* The :c:macro:`!DL_EXPORT` and :c:macro:`!DL_IMPORT` macros are now deprecated. Initialization functions for Python extension modules should now be declared using the new macro :c:macro:`PyMODINIT_FUNC`, while the Python core will generally use the :c:macro:`PyAPI_FUNC` and :c:macro:`PyAPI_DATA` macros. diff --git a/Doc/whatsnew/2.4.rst b/Doc/whatsnew/2.4.rst index a00c4360b9c685..4272928fb67894 100644 --- a/Doc/whatsnew/2.4.rst +++ b/Doc/whatsnew/2.4.rst @@ -1476,7 +1476,7 @@ Some of the changes to Python's build process and to the C API are: :c:func:`PyArg_ParseTupleAndKeywords` but takes a :c:type:`va_list` instead of a number of arguments. (Contributed by Greg Chapman.) -* A new method flag, :c:macro:`METH_COEXISTS`, allows a function defined in slots +* A new method flag, :c:macro:`METH_COEXIST`, allows a function defined in slots to co-exist with a :c:type:`PyCFunction` having the same name. This can halve the access time for a method such as :meth:`set.__contains__`. (Contributed by Raymond Hettinger.) diff --git a/Doc/whatsnew/2.6.rst b/Doc/whatsnew/2.6.rst index 2b8fa1546a5884..563fa461d2a868 100644 --- a/Doc/whatsnew/2.6.rst +++ b/Doc/whatsnew/2.6.rst @@ -3060,9 +3060,9 @@ Changes to Python's build process and to the C API include: * Some macros were renamed in both 3.0 and 2.6 to make it clearer that they are macros, - not functions. :c:macro:`Py_Size()` became :c:macro:`Py_SIZE()`, - :c:macro:`Py_Type()` became :c:macro:`Py_TYPE()`, and - :c:macro:`Py_Refcnt()` became :c:macro:`Py_REFCNT()`. + not functions. :c:macro:`!Py_Size()` became :c:macro:`Py_SIZE()`, + :c:macro:`!Py_Type()` became :c:macro:`Py_TYPE()`, and + :c:macro:`!Py_Refcnt()` became :c:macro:`Py_REFCNT()`. The mixed-case macros are still available in Python 2.6 for backward compatibility. (:issue:`1629`) diff --git a/Doc/whatsnew/2.7.rst b/Doc/whatsnew/2.7.rst index 09798ca82d261f..bc62a2155f1b63 100644 --- a/Doc/whatsnew/2.7.rst +++ b/Doc/whatsnew/2.7.rst @@ -2286,10 +2286,10 @@ object, and then get the ``void *`` pointer, which will usually point to an array of pointers to the module's various API functions. There is an existing data type already used for this, -:c:type:`PyCObject`, but it doesn't provide type safety. Evil code +:c:type:`!PyCObject`, but it doesn't provide type safety. Evil code written in pure Python could cause a segmentation fault by taking a -:c:type:`PyCObject` from module A and somehow substituting it for the -:c:type:`PyCObject` in module B. Capsules know their own name, +:c:type:`!PyCObject` from module A and somehow substituting it for the +:c:type:`!PyCObject` in module B. Capsules know their own name, and getting the pointer requires providing the name: .. code-block:: c @@ -2309,10 +2309,10 @@ detect the mismatched name and return false. Refer to :ref:`using-capsules` for more information on using these objects. Python 2.7 now uses capsules internally to provide various -extension-module APIs, but the :c:func:`PyCObject_AsVoidPtr` was +extension-module APIs, but the :c:func:`!PyCObject_AsVoidPtr` was modified to handle capsules, preserving compile-time compatibility -with the :c:type:`CObject` interface. Use of -:c:func:`PyCObject_AsVoidPtr` will signal a +with the :c:type:`!PyCObject` interface. Use of +:c:func:`!PyCObject_AsVoidPtr` will signal a :exc:`PendingDeprecationWarning`, which is silent by default. Implemented in Python 3.1 and backported to 2.7 by Larry Hastings; diff --git a/Doc/whatsnew/3.0.rst b/Doc/whatsnew/3.0.rst index 6f2a155abe10b6..65fcc071a5cf54 100644 --- a/Doc/whatsnew/3.0.rst +++ b/Doc/whatsnew/3.0.rst @@ -875,7 +875,7 @@ to the C API. * Renamed the boolean conversion C-level slot and method: ``nb_nonzero`` is now ``nb_bool``. -* Removed :c:macro:`METH_OLDARGS` and :c:macro:`WITH_CYCLE_GC` from the C API. +* Removed :c:macro:`!METH_OLDARGS` and :c:macro:`!WITH_CYCLE_GC` from the C API. .. ====================================================================== diff --git a/Doc/whatsnew/3.1.rst b/Doc/whatsnew/3.1.rst index e4365d44928b5b..ceef622f9f8daa 100644 --- a/Doc/whatsnew/3.1.rst +++ b/Doc/whatsnew/3.1.rst @@ -510,7 +510,7 @@ Changes to Python's build process and to the C API include: (Contributed by Mark Dickinson; :issue:`5914`.) -* Added :c:type:`PyCapsule` as a replacement for the :c:type:`PyCObject` API. +* Added :c:type:`PyCapsule` as a replacement for the :c:type:`!PyCObject` API. The principal difference is that the new type has a well defined interface for passing typing safety information and a less complicated signature for calling a destructor. The old type had a problematic API and is now diff --git a/Doc/whatsnew/3.2.rst b/Doc/whatsnew/3.2.rst index 01f5cf5204bcf7..de355c19ac0e48 100644 --- a/Doc/whatsnew/3.2.rst +++ b/Doc/whatsnew/3.2.rst @@ -2656,7 +2656,7 @@ require changes to your code: * "t#" format has been removed: use "s#" or "s*" instead * "w" and "w#" formats has been removed: use "w*" instead -* The :c:type:`PyCObject` type, deprecated in 3.1, has been removed. To wrap +* The :c:type:`!PyCObject` type, deprecated in 3.1, has been removed. To wrap opaque C pointers in Python objects, the :c:type:`PyCapsule` API should be used instead; the new type has a well-defined interface for passing typing safety information and a less complicated signature for calling a destructor. From b3f7ddd040d7ef8226cf2a23d88bbda1d0e5d242 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Thu, 17 Aug 2023 14:57:48 +0100 Subject: [PATCH 151/632] [3.11] gh-102215: importlib documentation cleanups (#108056) (cherry picked from commit 4cd95dce0b8d7bb8a16468ec8b5b3429555417f1) Co-authored-by: Sam Morris --- Doc/library/importlib.metadata.rst | 14 +++++++------- Doc/library/importlib.resources.rst | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Doc/library/importlib.metadata.rst b/Doc/library/importlib.metadata.rst index a768e934583bc5..3566f7468f1087 100644 --- a/Doc/library/importlib.metadata.rst +++ b/Doc/library/importlib.metadata.rst @@ -1,11 +1,11 @@ .. _using: -================================= - Using :mod:`!importlib.metadata` -================================= +======================================================== +:mod:`!importlib.metadata` -- Accessing package metadata +======================================================== .. module:: importlib.metadata - :synopsis: The implementation of the importlib metadata. + :synopsis: Accessing package metadata .. versionadded:: 3.8 .. versionchanged:: 3.10 @@ -13,7 +13,7 @@ **Source code:** :source:`Lib/importlib/metadata/__init__.py` -``importlib_metadata`` is a library that provides access to +``importlib.metadata`` is a library that provides access to the metadata of an installed `Distribution Package `_, such as its entry points or its top-level names (`Import Package `_\s, modules, if any). @@ -24,7 +24,7 @@ API`_ and `metadata API`_ of ``pkg_resources``. Along with this package can eliminate the need to use the older and less efficient ``pkg_resources`` package. -``importlib_metadata`` operates on third-party *distribution packages* +``importlib.metadata`` operates on third-party *distribution packages* installed into Python's ``site-packages`` directory via tools such as `pip `_. Specifically, it works with distributions with discoverable @@ -365,7 +365,7 @@ system `finders`_. To find a distribution package's metadata, ``importlib.metadata`` queries the list of :term:`meta path finders ` on :data:`sys.meta_path`. -By default ``importlib_metadata`` installs a finder for distribution packages +By default ``importlib.metadata`` installs a finder for distribution packages found on the file system. This finder doesn't actually find any *distributions*, but it can find their metadata. diff --git a/Doc/library/importlib.resources.rst b/Doc/library/importlib.resources.rst index 827e7d8d5aced4..437da6927ab2f4 100644 --- a/Doc/library/importlib.resources.rst +++ b/Doc/library/importlib.resources.rst @@ -1,5 +1,5 @@ -:mod:`importlib.resources` -- Resources ---------------------------------------- +:mod:`importlib.resources` -- Package resource reading, opening and access +-------------------------------------------------------------------------- .. module:: importlib.resources :synopsis: Package resource reading, opening, and access @@ -79,7 +79,7 @@ for example, a package and its resources can be imported from a zip file using .. versionadded:: 3.9 Deprecated functions --------------------- +^^^^^^^^^^^^^^^^^^^^ An older, deprecated set of functions is still available, but is scheduled for removal in a future version of Python. From 1482b99061e1d7d4a49721d6290d7e48de8291c0 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 17 Aug 2023 07:22:39 -0700 Subject: [PATCH 152/632] [3.11] gh-107801: Document SEEK_HOLE and SEEK_DATA (GH-107936) (#108087) (cherry picked from commit 8a19f225b948db1eebe1d9fc71a486258841f578) Co-authored-by: Erlend E. Aasland Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Co-authored-by: Antoine Pitrou --- Doc/library/os.rst | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 5577921478452a..1d0d1049ebd7ed 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -1061,6 +1061,10 @@ as internal buffering of data. current position; :const:`SEEK_END` or ``2`` to set it relative to the end of the file. Return the new cursor position in bytes, starting from the beginning. + .. versionchanged:: 3.3 + + Add support for :const:`SEEK_HOLE` and :const:`SEEK_DATA`. + .. data:: SEEK_SET SEEK_CUR @@ -1069,9 +1073,30 @@ as internal buffering of data. Parameters to the :func:`lseek` function. Their values are 0, 1, and 2, respectively. + +.. data:: SEEK_HOLE + SEEK_DATA + + Parameters to the :func:`lseek` function and the :meth:`~io.IOBase.seek` + method on file objects, for seeking file data and holes on sparsely + allocated files. + + :data:`!SEEK_DATA` + Adjust the file offset to the next location containing data, + relative to the seek position. + + :data:`!SEEK_HOLE` + Adjust the file offset to the next location containing a hole, + relative to the seek position. + A hole is defined as a sequence of zeros. + + .. note:: + + These operations only make sense for filesystems that support them. + + .. availability:: Linux >= 3.1, macOS, Unix + .. versionadded:: 3.3 - Some operating systems could support additional values, like - :const:`os.SEEK_HOLE` or :const:`os.SEEK_DATA`. .. function:: open(path, flags, mode=0o777, *, dir_fd=None) From b389939be96f21162d7e8c187dceccc62e8a1c82 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 17 Aug 2023 12:29:10 -0700 Subject: [PATCH 153/632] [3.11] Docs: Fix Sphinx warnings in io.rst (GH-107903) (#108094) - Mark up parameter and argument names properly - If possible, link to docs for methods like `seek`, `tell`, `write`, `read`, etc. (cherry picked from commit 5c76899dadf3bdcfdedf6f30b3ab9742cb87af04) Co-authored-by: Erlend E. Aasland Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/library/io.rst | 71 ++++++++++++++++++++++---------------------- Doc/tools/.nitignore | 1 - 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/Doc/library/io.rst b/Doc/library/io.rst index 7eec1f87583b87..66273d9ed1ff0a 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -38,9 +38,9 @@ location), or only sequential access (for example in the case of a socket or pipe). All streams are careful about the type of data you give to them. For example -giving a :class:`str` object to the ``write()`` method of a binary stream +giving a :class:`str` object to the :meth:`!write` method of a binary stream will raise a :exc:`TypeError`. So will giving a :class:`bytes` object to the -``write()`` method of a text stream. +:meth:`!write` method of a text stream. .. versionchanged:: 3.3 Operations that used to raise :exc:`IOError` now raise :exc:`OSError`, since @@ -146,7 +146,7 @@ Opt-in EncodingWarning See :pep:`597` for more details. To find where the default locale encoding is used, you can enable -the ``-X warn_default_encoding`` command line option or set the +the :option:`-X warn_default_encoding <-X>` command line option or set the :envvar:`PYTHONWARNDEFAULTENCODING` environment variable, which will emit an :exc:`EncodingWarning` when the default encoding is used. @@ -175,7 +175,7 @@ High-level Module Interface .. audit-event:: open path,mode,flags io.open This function raises an :ref:`auditing event ` ``open`` with - arguments ``path``, ``mode`` and ``flags``. The ``mode`` and ``flags`` + arguments *path*, *mode* and *flags*. The *mode* and *flags* arguments may have been modified or inferred from the original call. @@ -184,10 +184,10 @@ High-level Module Interface Opens the provided file with mode ``'rb'``. This function should be used when the intent is to treat the contents as executable code. - ``path`` should be a :class:`str` and an absolute path. + *path* should be a :class:`str` and an absolute path. The behavior of this function may be overridden by an earlier call to the - :c:func:`PyFile_SetOpenCodeHook`. However, assuming that ``path`` is a + :c:func:`PyFile_SetOpenCodeHook`. However, assuming that *path* is a :class:`str` and an absolute path, ``open_code(path)`` should always behave the same as ``open(path, 'rb')``. Overriding the behavior is intended for additional validation or preprocessing of the file. @@ -258,7 +258,7 @@ standard stream implementations. The abstract base classes also provide default implementations of some methods in order to help implementation of concrete stream classes. For example, :class:`BufferedIOBase` provides unoptimized implementations of - :meth:`~IOBase.readinto` and :meth:`~IOBase.readline`. + :meth:`!readinto` and :meth:`!readline`. At the top of the I/O hierarchy is the abstract base class :class:`IOBase`. It defines the basic interface to a stream. Note, however, that there is no @@ -320,8 +320,8 @@ I/O Base Classes implementations represent a file that cannot be read, written or seeked. - Even though :class:`IOBase` does not declare :meth:`read` - or :meth:`write` because their signatures will vary, implementations and + Even though :class:`IOBase` does not declare :meth:`!read` + or :meth:`!write` because their signatures will vary, implementations and clients should consider those methods part of the interface. Also, implementations may raise a :exc:`ValueError` (or :exc:`UnsupportedOperation`) when operations they do not support are called. @@ -379,8 +379,8 @@ I/O Base Classes .. method:: readable() - Return ``True`` if the stream can be read from. If ``False``, :meth:`read` - will raise :exc:`OSError`. + Return ``True`` if the stream can be read from. + If ``False``, :meth:`!read` will raise :exc:`OSError`. .. method:: readline(size=-1, /) @@ -401,25 +401,25 @@ I/O Base Classes hint. Note that it's already possible to iterate on file objects using ``for - line in file: ...`` without calling ``file.readlines()``. + line in file: ...`` without calling :meth:`!file.readlines`. .. method:: seek(offset, whence=SEEK_SET, /) Change the stream position to the given byte *offset*. *offset* is interpreted relative to the position indicated by *whence*. The default - value for *whence* is :data:`SEEK_SET`. Values for *whence* are: + value for *whence* is :data:`!SEEK_SET`. Values for *whence* are: - * :data:`SEEK_SET` or ``0`` -- start of the stream (the default); + * :data:`!SEEK_SET` or ``0`` -- start of the stream (the default); *offset* should be zero or positive - * :data:`SEEK_CUR` or ``1`` -- current stream position; *offset* may + * :data:`!SEEK_CUR` or ``1`` -- current stream position; *offset* may be negative - * :data:`SEEK_END` or ``2`` -- end of the stream; *offset* is usually + * :data:`!SEEK_END` or ``2`` -- end of the stream; *offset* is usually negative Return the new absolute position. .. versionadded:: 3.1 - The ``SEEK_*`` constants. + The :data:`!SEEK_*` constants. .. versionadded:: 3.3 Some operating systems could support additional values, like @@ -450,7 +450,7 @@ I/O Base Classes .. method:: writable() Return ``True`` if the stream supports writing. If ``False``, - :meth:`write` and :meth:`truncate` will raise :exc:`OSError`. + :meth:`!write` and :meth:`truncate` will raise :exc:`OSError`. .. method:: writelines(lines, /) @@ -654,8 +654,9 @@ Raw File I/O implies writing, so this mode behaves in a similar way to ``'w'``. Add a ``'+'`` to the mode to allow simultaneous reading and writing. - The :meth:`read` (when called with a positive argument), :meth:`readinto` - and :meth:`write` methods on this class will only make one system call. + The :meth:`~RawIOBase.read` (when called with a positive argument), + :meth:`~RawIOBase.readinto` and :meth:`~RawIOBase.write` methods on this + class will only make one system call. A custom opener can be used by passing a callable as *opener*. The underlying file descriptor for the file object is then obtained by calling *opener* with @@ -791,8 +792,8 @@ than raw I/O does. object under various conditions, including: * when the buffer gets too small for all pending data; - * when :meth:`flush()` is called; - * when a :meth:`seek()` is requested (for :class:`BufferedRandom` objects); + * when :meth:`flush` is called; + * when a :meth:`~IOBase.seek` is requested (for :class:`BufferedRandom` objects); * when the :class:`BufferedWriter` object is closed or destroyed. The constructor creates a :class:`BufferedWriter` for the given writeable @@ -826,8 +827,8 @@ than raw I/O does. :data:`DEFAULT_BUFFER_SIZE`. :class:`BufferedRandom` is capable of anything :class:`BufferedReader` or - :class:`BufferedWriter` can do. In addition, :meth:`seek` and :meth:`tell` - are guaranteed to be implemented. + :class:`BufferedWriter` can do. In addition, :meth:`~IOBase.seek` and + :meth:`~IOBase.tell` are guaranteed to be implemented. .. class:: BufferedRWPair(reader, writer, buffer_size=DEFAULT_BUFFER_SIZE, /) @@ -904,7 +905,7 @@ Text I/O .. method:: readline(size=-1, /) - Read until newline or EOF and return a single ``str``. If the stream is + Read until newline or EOF and return a single :class:`str`. If the stream is already at EOF, an empty string is returned. If *size* is specified, at most *size* characters will be read. @@ -913,22 +914,22 @@ Text I/O Change the stream position to the given *offset*. Behaviour depends on the *whence* parameter. The default value for *whence* is - :data:`SEEK_SET`. + :data:`!SEEK_SET`. - * :data:`SEEK_SET` or ``0``: seek from the start of the stream + * :data:`!SEEK_SET` or ``0``: seek from the start of the stream (the default); *offset* must either be a number returned by :meth:`TextIOBase.tell`, or zero. Any other *offset* value produces undefined behaviour. - * :data:`SEEK_CUR` or ``1``: "seek" to the current position; + * :data:`!SEEK_CUR` or ``1``: "seek" to the current position; *offset* must be zero, which is a no-operation (all other values are unsupported). - * :data:`SEEK_END` or ``2``: seek to the end of the stream; + * :data:`!SEEK_END` or ``2``: seek to the end of the stream; *offset* must be zero (all other values are unsupported). Return the new absolute position as an opaque number. .. versionadded:: 3.1 - The ``SEEK_*`` constants. + The :data:`!SEEK_*` constants. .. method:: tell() @@ -988,10 +989,10 @@ Text I/O takes place. If *newline* is any of the other legal values, any ``'\n'`` characters written are translated to the given string. - If *line_buffering* is ``True``, :meth:`flush` is implied when a call to + If *line_buffering* is ``True``, :meth:`~IOBase.flush` is implied when a call to write contains a newline character or a carriage return. - If *write_through* is ``True``, calls to :meth:`write` are guaranteed + If *write_through* is ``True``, calls to :meth:`~BufferedIOBase.write` are guaranteed not to be buffered: any data written on the :class:`TextIOWrapper` object is immediately handled to its underlying binary *buffer*. @@ -1070,7 +1071,7 @@ Text I/O .. method:: getvalue() - Return a ``str`` containing the entire contents of the buffer. + Return a :class:`str` containing the entire contents of the buffer. Newlines are decoded as if by :meth:`~TextIOBase.read`, although the stream position is not changed. @@ -1125,7 +1126,7 @@ Text I/O over a binary storage (such as a file) is significantly slower than binary I/O over the same storage, because it requires conversions between unicode and binary data using a character codec. This can become noticeable handling huge amounts of text data like large log files. Also, -:meth:`TextIOWrapper.tell` and :meth:`TextIOWrapper.seek` are both quite slow +:meth:`~TextIOBase.tell` and :meth:`~TextIOBase.seek` are both quite slow due to the reconstruction algorithm used. :class:`StringIO`, however, is a native in-memory unicode container and will @@ -1135,7 +1136,7 @@ Multi-threading ^^^^^^^^^^^^^^^ :class:`FileIO` objects are thread-safe to the extent that the operating system -calls (such as ``read(2)`` under Unix) they wrap are thread-safe too. +calls (such as :manpage:`read(2)` under Unix) they wrap are thread-safe too. Binary buffered objects (instances of :class:`BufferedReader`, :class:`BufferedWriter`, :class:`BufferedRandom` and :class:`BufferedRWPair`) diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 1859d53e152849..407a4bd837c549 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -90,7 +90,6 @@ Doc/library/http.server.rst Doc/library/importlib.resources.rst Doc/library/importlib.rst Doc/library/inspect.rst -Doc/library/io.rst Doc/library/locale.rst Doc/library/logging.config.rst Doc/library/logging.handlers.rst From d6555abfa7384b5a40435a11bdd2aa6bbf8f5cfc Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 17 Aug 2023 13:52:43 -0700 Subject: [PATCH 154/632] [3.11] gh-107801: Improve the docs of the SEEK_* constants (GH-108099) (#108100) (cherry picked from commit 02079b010c39a89b284e8f0bb6d5f378e554260e) Co-authored-by: Erlend E. Aasland --- Doc/library/os.rst | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 1d0d1049ebd7ed..993236c521a87e 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -1070,16 +1070,26 @@ as internal buffering of data. SEEK_CUR SEEK_END - Parameters to the :func:`lseek` function. Their values are 0, 1, and 2, - respectively. + Parameters to the :func:`lseek` function and the :meth:`~io.IOBase.seek` + method on :term:`file-like objects `, + for whence to adjust the file position indicator. + + :const:`SEEK_SET` + Adjust the file position relative to the beginning of the file. + :const:`SEEK_CUR` + Adjust the file position relative to the current file position. + :const:`SEEK_END` + Adjust the file position relative to the end of the file. + + Their values are 0, 1, and 2, respectively. .. data:: SEEK_HOLE SEEK_DATA Parameters to the :func:`lseek` function and the :meth:`~io.IOBase.seek` - method on file objects, for seeking file data and holes on sparsely - allocated files. + method on :term:`file-like objects `, + for seeking file data and holes on sparsely allocated files. :data:`!SEEK_DATA` Adjust the file offset to the next location containing data, From 6dd1729a888c50ddb312fae758cc5a92aaf5055d Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Fri, 18 Aug 2023 08:12:07 +0100 Subject: [PATCH 155/632] [3.11] GH-107987: Remove the Distributing Python Modules guide (GH-108016) (#108091) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/conf.py | 2 - Doc/contents.rst | 1 - Doc/distributing/index.rst | 177 +++---------------------------------- Doc/installing/index.rst | 4 +- Doc/tutorial/venv.rst | 4 +- 5 files changed, 17 insertions(+), 171 deletions(-) diff --git a/Doc/conf.py b/Doc/conf.py index cfe34244fee390..161a8dec69b5d2 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -346,8 +346,6 @@ latex_documents = [ ('c-api/index', 'c-api.tex', 'The Python/C API', _stdauthor, 'manual'), - ('distributing/index', 'distributing.tex', - 'Distributing Python Modules', _stdauthor, 'manual'), ('extending/index', 'extending.tex', 'Extending and Embedding Python', _stdauthor, 'manual'), ('installing/index', 'installing.tex', diff --git a/Doc/contents.rst b/Doc/contents.rst index 8690de77bf3d82..3eedede7ba49ab 100644 --- a/Doc/contents.rst +++ b/Doc/contents.rst @@ -11,7 +11,6 @@ library/index.rst extending/index.rst c-api/index.rst - distributing/index.rst installing/index.rst howto/index.rst faq/index.rst diff --git a/Doc/distributing/index.rst b/Doc/distributing/index.rst index 8e70c24e70266d..8622ec60c0f2f6 100644 --- a/Doc/distributing/index.rst +++ b/Doc/distributing/index.rst @@ -1,175 +1,20 @@ +:orphan: + +.. This page is retained solely for existing links to /distributing/index.html. + Direct readers to the PPUG instead. + .. _distributing-index: +.. _publishing-python-packages: ############################### Distributing Python Modules ############################### -:Email: distutils-sig@python.org - - -As a popular open source development project, Python has an active -supporting community of contributors and users that also make their software -available for other Python developers to use under open source license terms. - -This allows Python users to share and collaborate effectively, benefiting -from the solutions others have already created to common (and sometimes -even rare!) problems, as well as potentially contributing their own -solutions to the common pool. - -This guide covers the distribution part of the process. For a guide to -installing other Python projects, refer to the -:ref:`installation guide `. - .. note:: - For corporate and other institutional users, be aware that many - organisations have their own policies around using and contributing to - open source software. Please take such policies into account when making - use of the distribution and installation tools provided with Python. - - -Key terms -========= - -* the `Python Package Index `__ is a public - repository of open source licensed packages made available for use by - other Python users -* the `Python Packaging Authority - `__ are the group of - developers and documentation authors responsible for the maintenance and - evolution of the standard packaging tools and the associated metadata and - file format standards. They maintain a variety of tools, documentation - and issue trackers on both `GitHub `__ and - `Bitbucket `__. -* :mod:`distutils` is the original build and distribution system first added - to the Python standard library in 1998. While direct use of :mod:`distutils` - is being phased out, it still laid the foundation for the current packaging - and distribution infrastructure, and it not only remains part of the - standard library, but its name lives on in other ways (such as the name - of the mailing list used to coordinate Python packaging standards - development). -* `setuptools`_ is a (largely) drop-in replacement for :mod:`distutils` first - published in 2004. Its most notable addition over the unmodified - :mod:`distutils` tools was the ability to declare dependencies on other - packages. It is currently recommended as a more regularly updated - alternative to :mod:`distutils` that offers consistent support for more - recent packaging standards across a wide range of Python versions. -* `wheel`_ (in this context) is a project that adds the ``bdist_wheel`` - command to :mod:`distutils`/`setuptools`_. This produces a cross platform - binary packaging format (called "wheels" or "wheel files" and defined in - :pep:`427`) that allows Python libraries, even those including binary - extensions, to be installed on a system without needing to be built - locally. - -.. _setuptools: https://setuptools.readthedocs.io/en/latest/ -.. _wheel: https://wheel.readthedocs.io/ - -Open source licensing and collaboration -======================================= - -In most parts of the world, software is automatically covered by copyright. -This means that other developers require explicit permission to copy, use, -modify and redistribute the software. - -Open source licensing is a way of explicitly granting such permission in a -relatively consistent way, allowing developers to share and collaborate -efficiently by making common solutions to various problems freely available. -This leaves many developers free to spend more time focusing on the problems -that are relatively unique to their specific situation. - -The distribution tools provided with Python are designed to make it -reasonably straightforward for developers to make their own contributions -back to that common pool of software if they choose to do so. - -The same distribution tools can also be used to distribute software within -an organisation, regardless of whether that software is published as open -source software or not. - - -Installing the tools -==================== - -The standard library does not include build tools that support modern -Python packaging standards, as the core development team has found that it -is important to have standard tools that work consistently, even on older -versions of Python. - -The currently recommended build and distribution tools can be installed -by invoking the ``pip`` module at the command line:: - - python -m pip install setuptools wheel twine - -.. note:: - - For POSIX users (including macOS and Linux users), these instructions - assume the use of a :term:`virtual environment`. - - For Windows users, these instructions assume that the option to - adjust the system PATH environment variable was selected when installing - Python. - -The Python Packaging User Guide includes more details on the `currently -recommended tools`_. - -.. _currently recommended tools: https://packaging.python.org/guides/tool-recommendations/#packaging-tool-recommendations - -.. index:: - single: Python Package Index (PyPI) - single: PyPI; (see Python Package Index (PyPI)) - -.. _publishing-python-packages: - -Reading the Python Packaging User Guide -======================================= - -The Python Packaging User Guide covers the various key steps and elements -involved in creating and publishing a project: - -* `Project structure`_ -* `Building and packaging the project`_ -* `Uploading the project to the Python Package Index`_ -* `The .pypirc file`_ - -.. _Project structure: https://packaging.python.org/tutorials/packaging-projects/#packaging-python-projects -.. _Building and packaging the project: https://packaging.python.org/tutorials/packaging-projects/#creating-the-package-files -.. _Uploading the project to the Python Package Index: https://packaging.python.org/tutorials/packaging-projects/#uploading-the-distribution-archives -.. _The .pypirc file: https://packaging.python.org/specifications/pypirc/ - - -How do I...? -============ - -These are quick answers or links for some common tasks. - -... choose a name for my project? ---------------------------------- - -This isn't an easy topic, but here are a few tips: - -* check the Python Package Index to see if the name is already in use -* check popular hosting sites like GitHub, Bitbucket, etc to see if there - is already a project with that name -* check what comes up in a web search for the name you're considering -* avoid particularly common words, especially ones with multiple meanings, - as they can make it difficult for users to find your software when - searching for it - - -... create and distribute binary extensions? --------------------------------------------- - -This is actually quite a complex topic, with a variety of alternatives -available depending on exactly what you're aiming to achieve. See the -Python Packaging User Guide for more information and recommendations. - -.. seealso:: - - `Python Packaging User Guide: Binary Extensions - `__ - -.. other topics: + Information and guidance on distributing Python modules and packages + has been moved to the `Python Packaging User Guide`_, + and the tutorial on `packaging Python projects`_. - Once the Development & Deployment part of PPUG is fleshed out, some of - those sections should be linked from new questions here (most notably, - we should have a question about avoiding depending on PyPI that links to - https://packaging.python.org/en/latest/mirrors/) + .. _Python Packaging User Guide: https://packaging.python.org/ + .. _packaging Python projects: https://packaging.python.org/en/latest/tutorials/packaging-projects/ diff --git a/Doc/installing/index.rst b/Doc/installing/index.rst index e158bf1c4c0c7f..da4a2b3c186a77 100644 --- a/Doc/installing/index.rst +++ b/Doc/installing/index.rst @@ -19,7 +19,9 @@ solutions to the common pool. This guide covers the installation part of the process. For a guide to creating and sharing your own Python projects, refer to the -:ref:`distribution guide `. +`Python packaging user guide`_. + +.. _Python Packaging User Guide: https://packaging.python.org/en/latest/tutorials/packaging-projects/ .. note:: diff --git a/Doc/tutorial/venv.rst b/Doc/tutorial/venv.rst index 9a3f49d1380d8d..d91fc3de4f92d8 100644 --- a/Doc/tutorial/venv.rst +++ b/Doc/tutorial/venv.rst @@ -207,4 +207,6 @@ necessary packages with ``install -r``: ``pip`` has many more options. Consult the :ref:`installing-index` guide for complete documentation for ``pip``. When you've written a package and want to make it available on the Python Package Index, -consult the :ref:`distributing-index` guide. +consult the `Python packaging user guide`_. + +.. _Python Packaging User Guide: https://packaging.python.org/en/latest/tutorials/packaging-projects/ From 9a70989a6f5f3f9d6fd36de002dab0ab1e13a9e3 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 18 Aug 2023 03:25:08 -0700 Subject: [PATCH 156/632] [3.11] Docs: emphasise warning and add accurate markups for sys.unraisablehook (GH-108105) (#108110) (cherry picked from commit cc58ec9724772a8d5c4a5c9a6525f9f96e994227) Co-authored-by: Erlend E. Aasland --- Doc/library/sys.rst | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 641be05a16fb3c..b5ad0b6860b953 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -1747,35 +1747,39 @@ always available. The *unraisable* argument has the following attributes: - * *exc_type*: Exception type. - * *exc_value*: Exception value, can be ``None``. - * *exc_traceback*: Exception traceback, can be ``None``. - * *err_msg*: Error message, can be ``None``. - * *object*: Object causing the exception, can be ``None``. + * :attr:`!exc_type`: Exception type. + * :attr:`!exc_value`: Exception value, can be ``None``. + * :attr:`!exc_traceback`: Exception traceback, can be ``None``. + * :attr:`!err_msg`: Error message, can be ``None``. + * :attr:`!object`: Object causing the exception, can be ``None``. - The default hook formats *err_msg* and *object* as: + The default hook formats :attr:`!err_msg` and :attr:`!object` as: ``f'{err_msg}: {object!r}'``; use "Exception ignored in" error message - if *err_msg* is ``None``. + if :attr:`!err_msg` is ``None``. :func:`sys.unraisablehook` can be overridden to control how unraisable exceptions are handled. - Storing *exc_value* using a custom hook can create a reference cycle. It - should be cleared explicitly to break the reference cycle when the - exception is no longer needed. + .. seealso:: + + :func:`excepthook` which handles uncaught exceptions. + + .. warning:: - Storing *object* using a custom hook can resurrect it if it is set to an - object which is being finalized. Avoid storing *object* after the custom - hook completes to avoid resurrecting objects. + Storing :attr:`!exc_value` using a custom hook can create a reference cycle. + It should be cleared explicitly to break the reference cycle when the + exception is no longer needed. - See also :func:`excepthook` which handles uncaught exceptions. + Storing :attr:`!object` using a custom hook can resurrect it if it is set to an + object which is being finalized. Avoid storing :attr:`!object` after the custom + hook completes to avoid resurrecting objects. .. audit-event:: sys.unraisablehook hook,unraisable sys.unraisablehook Raise an auditing event ``sys.unraisablehook`` with arguments - ``hook``, ``unraisable`` when an exception that cannot be handled occurs. - The ``unraisable`` object is the same as what will be passed to the hook. - If no hook has been set, ``hook`` may be ``None``. + *hook*, *unraisable* when an exception that cannot be handled occurs. + The *unraisable* object is the same as what will be passed to the hook. + If no hook has been set, *hook* may be ``None``. .. versionadded:: 3.8 From 441797d4ffb12acda257370b9e5e19ed8d6e8a71 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Fri, 18 Aug 2023 16:50:11 -0400 Subject: [PATCH 157/632] [3.11] gh-107565: Update multissltests and GitHub CI workflows to use OpenSSL 1.1.1v, 3.0.10, and 3.1.2. (GH-108119) --- .github/workflows/build.yml | 6 +++--- .../2023-08-12-13-18-15.gh-issue-107565.Tv22Ne.rst | 2 ++ Tools/ssl/multissltests.py | 6 +++--- 3 files changed, 8 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Tools-Demos/2023-08-12-13-18-15.gh-issue-107565.Tv22Ne.rst diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e7d3488ef8d9e3..8e7d831f4232d8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -273,7 +273,7 @@ jobs: needs: check_source if: needs.check_source.outputs.run_tests == 'true' env: - OPENSSL_VER: 1.1.1u + OPENSSL_VER: 1.1.1v PYTHONSTRICTEXTENSIONBUILD: 1 steps: - uses: actions/checkout@v3 @@ -342,7 +342,7 @@ jobs: strategy: fail-fast: false matrix: - openssl_ver: [1.1.1u, 3.0.9, 3.1.1] + openssl_ver: [1.1.1v, 3.0.10, 3.1.2] env: OPENSSL_VER: ${{ matrix.openssl_ver }} MULTISSL_DIR: ${{ github.workspace }}/multissl @@ -394,7 +394,7 @@ jobs: needs: check_source if: needs.check_source.outputs.run_tests == 'true' env: - OPENSSL_VER: 1.1.1u + OPENSSL_VER: 1.1.1v PYTHONSTRICTEXTENSIONBUILD: 1 ASAN_OPTIONS: detect_leaks=0:allocator_may_return_null=1:handle_segv=0 steps: diff --git a/Misc/NEWS.d/next/Tools-Demos/2023-08-12-13-18-15.gh-issue-107565.Tv22Ne.rst b/Misc/NEWS.d/next/Tools-Demos/2023-08-12-13-18-15.gh-issue-107565.Tv22Ne.rst new file mode 100644 index 00000000000000..c43ee680e8158e --- /dev/null +++ b/Misc/NEWS.d/next/Tools-Demos/2023-08-12-13-18-15.gh-issue-107565.Tv22Ne.rst @@ -0,0 +1,2 @@ +Update multissltests and GitHub CI workflows to use OpenSSL 1.1.1v, 3.0.10, +and 3.1.2. diff --git a/Tools/ssl/multissltests.py b/Tools/ssl/multissltests.py index a9c6e89a1571d8..96500b024c00b7 100755 --- a/Tools/ssl/multissltests.py +++ b/Tools/ssl/multissltests.py @@ -47,9 +47,9 @@ ] OPENSSL_RECENT_VERSIONS = [ - "1.1.1u", - "3.0.9", - "3.1.1", + "1.1.1v", + "3.0.10", + "3.1.2", ] LIBRESSL_OLD_VERSIONS = [ From 0673d4c762d6dc4c742e71e76cee66181c457f24 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 19 Aug 2023 02:13:09 -0700 Subject: [PATCH 158/632] [3.11] Docs: format sys.float_info properly (GH-108107) (#108131) Docs: format sys.float_info properly (GH-108107) - Normalise capitalisation and punctuation - Use attribute markup for named tuple attributes - Use :c:macro: markup for C macros - Use a list for the 'rounds' attribute values - Use list-table, for better .rst readability - Remove one unneeded sys.float_info.dig link (cherry picked from commit ca0c6c1f1ef79d10bc49b61d638d87cde265aa94) Co-authored-by: Erlend E. Aasland Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/library/sys.rst | 125 ++++++++++++++++++++++++++------------------ 1 file changed, 73 insertions(+), 52 deletions(-) diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index b5ad0b6860b953..08bf2d13572f41 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -570,61 +570,82 @@ always available. programming language; see section 5.2.4.2.2 of the 1999 ISO/IEC C standard [C99]_, 'Characteristics of floating types', for details. - .. tabularcolumns:: |l|l|L| - - +---------------------+---------------------+--------------------------------------------------+ - | attribute | float.h macro | explanation | - +=====================+=====================+==================================================+ - | ``epsilon`` | ``DBL_EPSILON`` | difference between 1.0 and the least value | - | | | greater than 1.0 that is representable as a float| - | | | | - | | | See also :func:`math.ulp`. | - +---------------------+---------------------+--------------------------------------------------+ - | ``dig`` | ``DBL_DIG`` | maximum number of decimal digits that can be | - | | | faithfully represented in a float; see below | - +---------------------+---------------------+--------------------------------------------------+ - | ``mant_dig`` | ``DBL_MANT_DIG`` | float precision: the number of base-``radix`` | - | | | digits in the significand of a float | - +---------------------+---------------------+--------------------------------------------------+ - | ``max`` | ``DBL_MAX`` | maximum representable positive finite float | - +---------------------+---------------------+--------------------------------------------------+ - | ``max_exp`` | ``DBL_MAX_EXP`` | maximum integer *e* such that ``radix**(e-1)`` is| - | | | a representable finite float | - +---------------------+---------------------+--------------------------------------------------+ - | ``max_10_exp`` | ``DBL_MAX_10_EXP`` | maximum integer *e* such that ``10**e`` is in the| - | | | range of representable finite floats | - +---------------------+---------------------+--------------------------------------------------+ - | ``min`` | ``DBL_MIN`` | minimum representable positive *normalized* float| - | | | | - | | | Use :func:`math.ulp(0.0) ` to get the | - | | | smallest positive *denormalized* representable | - | | | float. | - +---------------------+---------------------+--------------------------------------------------+ - | ``min_exp`` | ``DBL_MIN_EXP`` | minimum integer *e* such that ``radix**(e-1)`` is| - | | | a normalized float | - +---------------------+---------------------+--------------------------------------------------+ - | ``min_10_exp`` | ``DBL_MIN_10_EXP`` | minimum integer *e* such that ``10**e`` is a | - | | | normalized float | - +---------------------+---------------------+--------------------------------------------------+ - | ``radix`` | ``FLT_RADIX`` | radix of exponent representation | - +---------------------+---------------------+--------------------------------------------------+ - | ``rounds`` | ``FLT_ROUNDS`` | integer representing the rounding mode for | - | | | floating-point arithmetic. This reflects the | - | | | value of the system ``FLT_ROUNDS`` macro at | - | | | interpreter startup time: | - | | | ``-1`` indeterminable, | - | | | ``0`` toward zero, | - | | | ``1`` to nearest, | - | | | ``2`` toward positive infinity, | - | | | ``3`` toward negative infinity | - | | | | - | | | All other values for ``FLT_ROUNDS`` characterize | - | | | implementation-defined rounding behavior. | - +---------------------+---------------------+--------------------------------------------------+ + .. list-table:: Attributes of the :data:`!float_info` :term:`named tuple` + :header-rows: 1 + + * - attribute + - float.h macro + - explanation + + * - .. attribute:: float_info.epsilon + - :c:macro:`!DBL_EPSILON` + - difference between 1.0 and the least value greater than 1.0 that is + representable as a float. + + See also :func:`math.ulp`. + + * - .. attribute:: float_info.dig + - :c:macro:`!DBL_DIG` + - The maximum number of decimal digits that can be faithfully + represented in a float; see below. + + * - .. attribute:: float_info.mant_dig + - :c:macro:`!DBL_MANT_DIG` + - Float precision: the number of base-``radix`` digits in the + significand of a float. + + * - .. attribute:: float_info.max + - :c:macro:`!DBL_MAX` + - The maximum representable positive finite float. + + * - .. attribute:: float_info.max_exp + - :c:macro:`!DBL_MAX_EXP` + - The maximum integer *e* such that ``radix**(e-1)`` is a representable + finite float. + + * - .. attribute:: float_info.max_10_exp + - :c:macro:`!DBL_MAX_10_EXP` + - The maximum integer *e* such that ``10**e`` is in the range of + representable finite floats. + + * - .. attribute:: float_info.min + - :c:macro:`!DBL_MIN` + - The minimum representable positive *normalized* float. + + Use :func:`math.ulp(0.0) ` to get the smallest positive + *denormalized* representable float. + + * - .. attribute:: float_info.min_exp + - :c:macro:`!DBL_MIN_EXP` + - The minimum integer *e* such that ``radix**(e-1)`` is a normalized + float. + + * - .. attribute:: float_info.min_10_exp + - :c:macro:`!DBL_MIN_10_EXP` + - The minimum integer *e* such that ``10**e`` is a normalized float. + + * - .. attribute:: float_info.radix + - :c:macro:`!FLT_RADIX` + - The radix of exponent representation. + + * - .. attribute:: float_info.rounds + - :c:macro:`!FLT_ROUNDS` + - An integer representing the rounding mode for floating-point arithmetic. + This reflects the value of the system :c:macro:`!FLT_ROUNDS` macro + at interpreter startup time: + + * ``-1``: indeterminable + * ``0``: toward zero + * ``1``: to nearest + * ``2``: toward positive infinity + * ``3``: toward negative infinity + + All other values for :c:macro:`!FLT_ROUNDS` characterize + implementation-defined rounding behavior. The attribute :attr:`sys.float_info.dig` needs further explanation. If ``s`` is any string representing a decimal number with at most - :attr:`sys.float_info.dig` significant digits, then converting ``s`` to a + :attr:`!sys.float_info.dig` significant digits, then converting ``s`` to a float and back again will recover a string representing the same decimal value:: From 92a578409b70b5b9dabc9e55d4d13dd8239fe2f5 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 19 Aug 2023 15:22:13 +0300 Subject: [PATCH 159/632] [3.11] gh-107915: Handle errors in C API functions PyErr_Set*() and PyErr_Format() (GH-107918) (GH-108135) Such C API functions as PyErr_SetString(), PyErr_Format(), PyErr_SetFromErrnoWithFilename() and many others no longer crash or ignore errors if it failed to format the error message or decode the filename. Instead, they keep a corresponding error. (cherry picked from commit 633ea217a85f6b6ba5bdbc73094254d5811b3485) --- ...-08-13-12-33-00.gh-issue-107915.jQ0wOi.rst | 4 ++ Python/errors.c | 37 +++++++++++++++---- 2 files changed, 33 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2023-08-13-12-33-00.gh-issue-107915.jQ0wOi.rst diff --git a/Misc/NEWS.d/next/C API/2023-08-13-12-33-00.gh-issue-107915.jQ0wOi.rst b/Misc/NEWS.d/next/C API/2023-08-13-12-33-00.gh-issue-107915.jQ0wOi.rst new file mode 100644 index 00000000000000..58ee3f169a28cc --- /dev/null +++ b/Misc/NEWS.d/next/C API/2023-08-13-12-33-00.gh-issue-107915.jQ0wOi.rst @@ -0,0 +1,4 @@ +Such C API functions as ``PyErr_SetString()``, ``PyErr_Format()``, +``PyErr_SetFromErrnoWithFilename()`` and many others no longer crash or +ignore errors if it failed to format the error message or decode the +filename. Instead, they keep a corresponding error. diff --git a/Python/errors.c b/Python/errors.c index 3eb8a5ef04d284..d693f3a24126a9 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -225,8 +225,10 @@ _PyErr_SetString(PyThreadState *tstate, PyObject *exception, const char *string) { PyObject *value = PyUnicode_FromString(string); - _PyErr_SetObject(tstate, exception, value); - Py_XDECREF(value); + if (value != NULL) { + _PyErr_SetObject(tstate, exception, value); + Py_DECREF(value); + } } void @@ -852,7 +854,13 @@ PyErr_SetFromErrnoWithFilenameObjects(PyObject *exc, PyObject *filenameObject, P PyObject * PyErr_SetFromErrnoWithFilename(PyObject *exc, const char *filename) { - PyObject *name = filename ? PyUnicode_DecodeFSDefault(filename) : NULL; + PyObject *name = NULL; + if (filename) { + name = PyUnicode_DecodeFSDefault(filename); + if (name == NULL) { + return NULL; + } + } PyObject *result = PyErr_SetFromErrnoWithFilenameObjects(exc, name, NULL); Py_XDECREF(name); return result; @@ -949,7 +957,13 @@ PyObject *PyErr_SetExcFromWindowsErrWithFilename( int ierr, const char *filename) { - PyObject *name = filename ? PyUnicode_DecodeFSDefault(filename) : NULL; + PyObject *name = NULL; + if (filename) { + name = PyUnicode_DecodeFSDefault(filename); + if (name == NULL) { + return NULL; + } + } PyObject *ret = PyErr_SetExcFromWindowsErrWithFilenameObjects(exc, ierr, name, @@ -973,7 +987,13 @@ PyObject *PyErr_SetFromWindowsErrWithFilename( int ierr, const char *filename) { - PyObject *name = filename ? PyUnicode_DecodeFSDefault(filename) : NULL; + PyObject *name = NULL; + if (filename) { + name = PyUnicode_DecodeFSDefault(filename); + if (name == NULL) { + return NULL; + } + } PyObject *result = PyErr_SetExcFromWindowsErrWithFilenameObjects( PyExc_OSError, ierr, name, NULL); @@ -1076,9 +1096,10 @@ _PyErr_FormatV(PyThreadState *tstate, PyObject *exception, _PyErr_Clear(tstate); string = PyUnicode_FromFormatV(format, vargs); - - _PyErr_SetObject(tstate, exception, string); - Py_XDECREF(string); + if (string != NULL) { + _PyErr_SetObject(tstate, exception, string); + Py_DECREF(string); + } return NULL; } From e0ca3424ad6736600ee850348c3bba05f0c05d4b Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sat, 19 Aug 2023 15:06:36 +0200 Subject: [PATCH 160/632] [3.11] gh-107801: Improve the accuracy of os.lseek docs (#107935) (#108137) - name the last parameter *whence*, like it is for seek() methods on file objects - add param docstrings - structure the valid *whence* params (cherry picked from commit dd4442c8f597af1ec3eaf20f7ad89c4ac7e2dbc9) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/library/os.rst | 17 +++++++++++------ Modules/clinic/posixmodule.c.h | 17 +++++++++++++---- Modules/posixmodule.c | 13 +++++++++---- 3 files changed, 33 insertions(+), 14 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 993236c521a87e..5bbb4089d621aa 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -1053,17 +1053,22 @@ as internal buffering of data. .. versionadded:: 3.11 -.. function:: lseek(fd, pos, how, /) +.. function:: lseek(fd, pos, whence, /) Set the current position of file descriptor *fd* to position *pos*, modified - by *how*: :const:`SEEK_SET` or ``0`` to set the position relative to the - beginning of the file; :const:`SEEK_CUR` or ``1`` to set it relative to the - current position; :const:`SEEK_END` or ``2`` to set it relative to the end of - the file. Return the new cursor position in bytes, starting from the beginning. + by *whence*, and return the new position in bytes relative to + the start of the file. + Valid values for *whence* are: + + * :const:`SEEK_SET` or ``0`` -- set *pos* relative to the beginning of the file + * :const:`SEEK_CUR` or ``1`` -- set *pos* relative to the current file position + * :const:`SEEK_END` or ``2`` -- set *pos* relative to the end of the file + * :const:`SEEK_HOLE` -- set *pos* to the next data location, relative to *pos* + * :const:`SEEK_DATA` -- set *pos* to the next data hole, relative to *pos* .. versionchanged:: 3.3 - Add support for :const:`SEEK_HOLE` and :const:`SEEK_DATA`. + Add support for :const:`!SEEK_HOLE` and :const:`!SEEK_DATA`. .. data:: SEEK_SET diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index b66cd857e44963..e5efd1a3a547ab 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -4803,13 +4803,22 @@ os_lockf(PyObject *module, PyObject *const *args, Py_ssize_t nargs) #endif /* defined(HAVE_LOCKF) */ PyDoc_STRVAR(os_lseek__doc__, -"lseek($module, fd, position, how, /)\n" +"lseek($module, fd, position, whence, /)\n" "--\n" "\n" "Set the position of a file descriptor. Return the new position.\n" "\n" -"Return the new cursor position in number of bytes\n" -"relative to the beginning of the file."); +" fd\n" +" An open file descriptor, as returned by os.open().\n" +" position\n" +" Position, interpreted relative to \'whence\'.\n" +" whence\n" +" The relative position to seek from. Valid values are:\n" +" - SEEK_SET: seek from the start of the file.\n" +" - SEEK_CUR: seek from the current file position.\n" +" - SEEK_END: seek from the end of the file.\n" +"\n" +"The return value is the number of bytes relative to the beginning of the file."); #define OS_LSEEK_METHODDEF \ {"lseek", _PyCFunction_CAST(os_lseek), METH_FASTCALL, os_lseek__doc__}, @@ -9379,4 +9388,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=8dd784bf1e41b881 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=8277545a0dea1505 input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 5ceea7411818a4..cfadb6a06602fd 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -9521,19 +9521,24 @@ os_lockf_impl(PyObject *module, int fd, int command, Py_off_t length) os.lseek -> Py_off_t fd: int + An open file descriptor, as returned by os.open(). position: Py_off_t - how: int + Position, interpreted relative to 'whence'. + whence as how: int + The relative position to seek from. Valid values are: + - SEEK_SET: seek from the start of the file. + - SEEK_CUR: seek from the current file position. + - SEEK_END: seek from the end of the file. / Set the position of a file descriptor. Return the new position. -Return the new cursor position in number of bytes -relative to the beginning of the file. +The return value is the number of bytes relative to the beginning of the file. [clinic start generated code]*/ static Py_off_t os_lseek_impl(PyObject *module, int fd, Py_off_t position, int how) -/*[clinic end generated code: output=971e1efb6b30bd2f input=902654ad3f96a6d3]*/ +/*[clinic end generated code: output=971e1efb6b30bd2f input=f096e754c5367504]*/ { Py_off_t result; From 49abeb5eb49e5c0f16d8d02d406763592c66f3bd Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 19 Aug 2023 10:11:29 -0700 Subject: [PATCH 161/632] [3.11] Docs: Remove links to external C functions and macros in os.rst (GH-108138) (#108144) (cherry picked from commit c31c61c04e55ef431615ffec959d84ac73a3db81) Co-authored-by: Erlend E. Aasland Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/library/os.rst | 52 ++++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 5bbb4089d621aa..38024f78f4f048 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -215,7 +215,7 @@ process and user. On some platforms, including FreeBSD and macOS, setting ``environ`` may cause memory leaks. Refer to the system documentation for - :c:func:`putenv`. + :c:func:`!putenv`. You can delete items in this mapping to unset environment variables. :func:`unsetenv` will be called automatically when an item is deleted from @@ -553,7 +553,7 @@ process and user. .. note:: On some platforms, including FreeBSD and macOS, setting ``environ`` may - cause memory leaks. Refer to the system documentation for :c:func:`putenv`. + cause memory leaks. Refer to the system documentation for :c:func:`!putenv`. .. audit-event:: os.putenv key,value os.putenv @@ -597,7 +597,7 @@ process and user. .. function:: setpgrp() - Call the system call :c:func:`setpgrp` or ``setpgrp(0, 0)`` depending on + Call the system call :c:func:`!setpgrp` or ``setpgrp(0, 0)`` depending on which version is implemented (if any). See the Unix manual for the semantics. .. availability:: Unix, not Emscripten, not WASI. @@ -605,7 +605,7 @@ process and user. .. function:: setpgid(pid, pgrp, /) - Call the system call :c:func:`setpgid` to set the process group id of the + Call the system call :c:func:`!setpgid` to set the process group id of the process with id *pid* to the process group with id *pgrp*. See the Unix manual for the semantics. @@ -971,7 +971,7 @@ as internal buffering of data. .. function:: fsync(fd) Force write of file with filedescriptor *fd* to disk. On Unix, this calls the - native :c:func:`fsync` function; on Windows, the MS :c:func:`_commit` function. + native :c:func:`!fsync` function; on Windows, the MS :c:func:`!_commit` function. If you're starting with a buffered Python :term:`file object` *f*, first do ``f.flush()``, and then do ``os.fsync(f.fileno())``, to ensure that all internal @@ -2127,7 +2127,7 @@ features: .. function:: lstat(path, *, dir_fd=None) - Perform the equivalent of an :c:func:`lstat` system call on the given path. + Perform the equivalent of an :c:func:`!lstat` system call on the given path. Similar to :func:`~os.stat`, but does not follow symbolic links. Return a :class:`stat_result` object. @@ -2936,14 +2936,16 @@ features: Windows file attributes: ``dwFileAttributes`` member of the ``BY_HANDLE_FILE_INFORMATION`` structure returned by - :c:func:`GetFileInformationByHandle`. See the ``FILE_ATTRIBUTE_*`` + :c:func:`!GetFileInformationByHandle`. + See the :const:`!FILE_ATTRIBUTE_* ` constants in the :mod:`stat` module. .. attribute:: st_reparse_tag - When :attr:`st_file_attributes` has the ``FILE_ATTRIBUTE_REPARSE_POINT`` + When :attr:`st_file_attributes` has the :const:`~stat.FILE_ATTRIBUTE_REPARSE_POINT` set, this field contains the tag identifying the type of reparse point. - See the ``IO_REPARSE_TAG_*`` constants in the :mod:`stat` module. + See the :const:`IO_REPARSE_TAG_* ` + constants in the :mod:`stat` module. The standard module :mod:`stat` defines functions and constants that are useful for extracting information from a :c:struct:`stat` structure. (On @@ -2982,7 +2984,7 @@ features: .. function:: statvfs(path) - Perform a :c:func:`statvfs` system call on the given path. The return value is + Perform a :c:func:`!statvfs` system call on the given path. The return value is an object whose attributes describe the filesystem on the given path, and correspond to the members of the :c:struct:`statvfs` structure, namely: :attr:`f_bsize`, :attr:`f_frsize`, :attr:`f_blocks`, :attr:`f_bfree`, @@ -4063,7 +4065,7 @@ written in Python, such as a mail server's external command delivery program. setpgroup=None, resetids=False, setsid=False, setsigmask=(), \ setsigdef=(), scheduler=None) - Wraps the :c:func:`posix_spawn` C library API for use from Python. + Wraps the :c:func:`!posix_spawn` C library API for use from Python. Most users should use :func:`subprocess.run` instead of :func:`posix_spawn`. @@ -4099,16 +4101,16 @@ written in Python, such as a mail server's external command delivery program. Performs ``os.dup2(fd, new_fd)``. These tuples correspond to the C library - :c:func:`posix_spawn_file_actions_addopen`, - :c:func:`posix_spawn_file_actions_addclose`, and - :c:func:`posix_spawn_file_actions_adddup2` API calls used to prepare - for the :c:func:`posix_spawn` call itself. + :c:func:`!posix_spawn_file_actions_addopen`, + :c:func:`!posix_spawn_file_actions_addclose`, and + :c:func:`!posix_spawn_file_actions_adddup2` API calls used to prepare + for the :c:func:`!posix_spawn` call itself. The *setpgroup* argument will set the process group of the child to the value specified. If the value specified is 0, the child's process group ID will be made the same as its process ID. If the value of *setpgroup* is not set, the child will inherit the parent's process group ID. This argument corresponds - to the C library :c:macro:`POSIX_SPAWN_SETPGROUP` flag. + to the C library :c:macro:`!POSIX_SPAWN_SETPGROUP` flag. If the *resetids* argument is ``True`` it will reset the effective UID and GID of the child to the real UID and GID of the parent process. If the @@ -4116,27 +4118,27 @@ written in Python, such as a mail server's external command delivery program. the parent. In either case, if the set-user-ID and set-group-ID permission bits are enabled on the executable file, their effect will override the setting of the effective UID and GID. This argument corresponds to the C - library :c:macro:`POSIX_SPAWN_RESETIDS` flag. + library :c:macro:`!POSIX_SPAWN_RESETIDS` flag. If the *setsid* argument is ``True``, it will create a new session ID - for ``posix_spawn``. *setsid* requires :c:macro:`POSIX_SPAWN_SETSID` - or :c:macro:`POSIX_SPAWN_SETSID_NP` flag. Otherwise, :exc:`NotImplementedError` + for ``posix_spawn``. *setsid* requires :c:macro:`!POSIX_SPAWN_SETSID` + or :c:macro:`!POSIX_SPAWN_SETSID_NP` flag. Otherwise, :exc:`NotImplementedError` is raised. The *setsigmask* argument will set the signal mask to the signal set specified. If the parameter is not used, then the child inherits the parent's signal mask. This argument corresponds to the C library - :c:macro:`POSIX_SPAWN_SETSIGMASK` flag. + :c:macro:`!POSIX_SPAWN_SETSIGMASK` flag. The *sigdef* argument will reset the disposition of all signals in the set specified. This argument corresponds to the C library - :c:macro:`POSIX_SPAWN_SETSIGDEF` flag. + :c:macro:`!POSIX_SPAWN_SETSIGDEF` flag. The *scheduler* argument must be a tuple containing the (optional) scheduler policy and an instance of :class:`sched_param` with the scheduler parameters. A value of ``None`` in the place of the scheduler policy indicates that is not being provided. This argument is a combination of the C library - :c:macro:`POSIX_SPAWN_SETSCHEDPARAM` and :c:macro:`POSIX_SPAWN_SETSCHEDULER` + :c:macro:`!POSIX_SPAWN_SETSCHEDPARAM` and :c:macro:`!POSIX_SPAWN_SETSCHEDULER` flags. .. audit-event:: os.posix_spawn path,argv,env os.posix_spawn @@ -4149,7 +4151,7 @@ written in Python, such as a mail server's external command delivery program. setpgroup=None, resetids=False, setsid=False, setsigmask=(), \ setsigdef=(), scheduler=None) - Wraps the :c:func:`posix_spawnp` C library API for use from Python. + Wraps the :c:func:`!posix_spawnp` C library API for use from Python. Similar to :func:`posix_spawn` except that the system searches for the *executable* file in the list of directories specified by the @@ -4330,7 +4332,7 @@ written in Python, such as a mail server's external command delivery program. Use *show_cmd* to override the default window style. Whether this has any effect will depend on the application being launched. Values are integers as - supported by the Win32 :c:func:`ShellExecute` function. + supported by the Win32 :c:func:`!ShellExecute` function. :func:`startfile` returns as soon as the associated application is launched. There is no option to wait for the application to close, and no way to retrieve @@ -4340,7 +4342,7 @@ written in Python, such as a mail server's external command delivery program. :func:`os.path.normpath` function to ensure that paths are properly encoded for Win32. - To reduce interpreter startup overhead, the Win32 :c:func:`ShellExecute` + To reduce interpreter startup overhead, the Win32 :c:func:`!ShellExecute` function is not resolved until this function is first called. If the function cannot be resolved, :exc:`NotImplementedError` will be raised. From f8b14fea33a5566dbb369ef9aacea12924de2c7a Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 19 Aug 2023 16:02:12 -0700 Subject: [PATCH 162/632] [3.11] gh-107565: Update macOS installer to use OpenSSL 3.0.10. (GH-107897) (#108122) --- Mac/BuildScript/build-installer.py | 6 +++--- .../macOS/2023-08-12-13-33-57.gh-issue-107565.SJwqf4.rst | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/macOS/2023-08-12-13-33-57.gh-issue-107565.SJwqf4.rst diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index 71fbe3c0777e1b..c10daa0a2dd07c 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -246,9 +246,9 @@ def library_recipes(): result.extend([ dict( - name="OpenSSL 3.0.9", - url="https://www.openssl.org/source/openssl-3.0.9.tar.gz", - checksum='eb1ab04781474360f77c318ab89d8c5a03abc38e63d65a603cabbf1b00a1dc90', + name="OpenSSL 3.0.10", + url="https://www.openssl.org/source/openssl-3.0.10.tar.gz", + checksum='1761d4f5b13a1028b9b6f3d4b8e17feb0cedc9370f6afe61d7193d2cdce83323', buildrecipe=build_universal_openssl, configure=None, install=None, diff --git a/Misc/NEWS.d/next/macOS/2023-08-12-13-33-57.gh-issue-107565.SJwqf4.rst b/Misc/NEWS.d/next/macOS/2023-08-12-13-33-57.gh-issue-107565.SJwqf4.rst new file mode 100644 index 00000000000000..c238c4760239e1 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2023-08-12-13-33-57.gh-issue-107565.SJwqf4.rst @@ -0,0 +1 @@ +Update macOS installer to use OpenSSL 3.0.10. From cdb5e9f76f6a3ecd0fbf1f49ea7de8befac1622d Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 20 Aug 2023 01:20:57 -0700 Subject: [PATCH 163/632] [3.11] Fix misspellings in sysconfig docs (GH-108156) (#108158) Fix misspellings in sysconfig docs (GH-108156) (cherry picked from commit 1dc0c58d2b17819720d184ec0287a8a9b1dc347e) Co-authored-by: Rafael Fontenelle --- Doc/library/sysconfig.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Doc/library/sysconfig.rst b/Doc/library/sysconfig.rst index dfe6934e6da8cc..17448f2a2abace 100644 --- a/Doc/library/sysconfig.rst +++ b/Doc/library/sysconfig.rst @@ -84,13 +84,13 @@ Python currently supports nine schemes: through Distutils and the *user* option is used. This scheme defines paths located under the user home directory. - *posix_venv*: scheme for :mod:`Python virtual environments ` on POSIX - platforms; by default it is the same as *posix_prefix* . + platforms; by default it is the same as *posix_prefix*. - *nt*: scheme for NT platforms like Windows. - *nt_user*: scheme for NT platforms, when the *user* option is used. - *nt_venv*: scheme for :mod:`Python virtual environments ` on NT - platforms; by default it is the same as *nt* . -- *venv*: a scheme with values from ether *posix_venv* or *nt_venv* depending - on the platform Python runs on + platforms; by default it is the same as *nt*. +- *venv*: a scheme with values from either *posix_venv* or *nt_venv* depending + on the platform Python runs on. - *osx_framework_user*: scheme for macOS, when the *user* option is used. Each scheme is itself composed of a series of paths and each path has a unique @@ -187,7 +187,7 @@ identifier. Python currently uses eight paths: platform is used. If *vars* is provided, it must be a dictionary of variables that will update - the dictionary return by :func:`get_config_vars`. + the dictionary returned by :func:`get_config_vars`. If *expand* is set to ``False``, the path will not be expanded using the variables. From 5d366f28ae5a3a6be9819280e4faf7e12e4407ca Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 20 Aug 2023 03:12:15 -0700 Subject: [PATCH 164/632] [3.11] gh-107980: fix doc role for asyncio.timeouts (GH-108126) (#108153) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gh-107980: fix doc role for asyncio.timeouts (GH-108126) (cherry picked from commit a47c13cae5b32e6f3d7532cc6dbb4e1ac31219de) Co-authored-by: Tin Tvrtković --- Doc/library/asyncio-task.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst index ad5466b12b651e..ef17f384c1bfeb 100644 --- a/Doc/library/asyncio-task.rst +++ b/Doc/library/asyncio-task.rst @@ -578,9 +578,9 @@ Shielding From Cancellation Timeouts ======== -.. coroutinefunction:: timeout(delay) +.. function:: timeout(delay) - An :ref:`asynchronous context manager ` + Return an :ref:`asynchronous context manager ` that can be used to limit the amount of time spent waiting on something. @@ -671,7 +671,7 @@ Timeouts .. versionadded:: 3.11 -.. coroutinefunction:: timeout_at(when) +.. function:: timeout_at(when) Similar to :func:`asyncio.timeout`, except *when* is the absolute time to stop waiting, or ``None``. From 4c042a36ce26d6920abdc7e5a54f80d288b9cc80 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 20 Aug 2023 03:39:43 -0700 Subject: [PATCH 165/632] [3.11] gh-107619: Extend functools LRU cache docs with generators and async functions (GH-107934) (#108162) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gh-107619: Extend functools LRU cache docs with generators and async functions (GH-107934) (cherry picked from commit 1a713eac47b26899044752f02cbfcb4d628dda2a) Co-authored-by: Hadházy Tamás <85063808+Hels15@users.noreply.github.com> Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Co-authored-by: Kumar Aditya --- Doc/library/functools.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Doc/library/functools.rst b/Doc/library/functools.rst index 62fcddec090419..9dadbb69fc2f24 100644 --- a/Doc/library/functools.rst +++ b/Doc/library/functools.rst @@ -211,8 +211,9 @@ The :mod:`functools` module defines the following functions: In general, the LRU cache should only be used when you want to reuse previously computed values. Accordingly, it doesn't make sense to cache - functions with side-effects, functions that need to create distinct mutable - objects on each call, or impure functions such as time() or random(). + functions with side-effects, functions that need to create + distinct mutable objects on each call (such as generators and async functions), + or impure functions such as time() or random(). Example of an LRU cache for static web content:: From ba6370656f7bf2b2b5882aad7d3c060f9b117540 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 20 Aug 2023 04:05:24 -0700 Subject: [PATCH 166/632] [3.11] gh-107659: Improve wording of the description of `ctypes.pointer` and `ctypes.POINTER` (GH-107769) (#108164) gh-107659: Improve wording of the description of `ctypes.pointer` and `ctypes.POINTER` (GH-107769) (cherry picked from commit beffb30dc7a07044f4198245d049ddda1f4b24db) Co-authored-by: Tomas R Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Co-authored-by: Kumar Aditya --- Doc/library/ctypes.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index debc1c0adde841..e2dde683a1a5d3 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -1988,17 +1988,17 @@ Utility functions specifying an address, or a ctypes instance. -.. function:: POINTER(type) +.. function:: POINTER(type, /) - This factory function creates and returns a new ctypes pointer type. Pointer - types are cached and reused internally, so calling this function repeatedly is - cheap. *type* must be a ctypes type. + Create and return a new ctypes pointer type. Pointer types are cached and + reused internally, so calling this function repeatedly is cheap. + *type* must be a ctypes type. -.. function:: pointer(obj) +.. function:: pointer(obj, /) - This function creates a new pointer instance, pointing to *obj*. The returned - object is of the type ``POINTER(type(obj))``. + Create a new pointer instance, pointing to *obj*. + The returned object is of the type ``POINTER(type(obj))``. Note: If you just want to pass a pointer to an object to a foreign function call, you should use ``byref(obj)`` which is much faster. From 5af34d2a4e41c17765cc77e956b8ab8008db1b34 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 20 Aug 2023 06:52:44 -0700 Subject: [PATCH 167/632] [3.11] Docs: Fix Sphinx warnings in sys.rst (GH-108106) (#108166) Docs: Fix Sphinx warnings in sys.rst (GH-108106) - Mark up named tuple attributes as attributes - Remove links for external functions - io.BufferedIOBase has no 'buffer' attribute; remove the link and mark up using :attr:`!buffer` - (Re)format some tables as bullet lists: - sys._emscripten_info - sys.hash_info - sys.int_info - sys.thread_info - In the paragraphs mentioning 'f_trace_lines' and 'f_trace_opcodes', add links to the frame objects reference. (cherry picked from commit 29fa7afef94d74e18d97485c085d1ccf80c16ca3) Co-authored-by: Erlend E. Aasland Co-authored-by: Alex Waygood Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/library/sys.rst | 265 ++++++++++++++++++++++----------------- Doc/library/textwrap.rst | 2 +- Doc/tools/.nitignore | 1 - 3 files changed, 153 insertions(+), 115 deletions(-) diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 08bf2d13572f41..ecad0ed881da45 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -329,23 +329,21 @@ always available. *wasm32-emscripten* platform. The named tuple is provisional and may change in the future. - .. tabularcolumns:: |l|L| - - +-----------------------------+----------------------------------------------+ - | Attribute | Explanation | - +=============================+==============================================+ - | :const:`emscripten_version` | Emscripten version as tuple of ints | - | | (major, minor, micro), e.g. ``(3, 1, 8)``. | - +-----------------------------+----------------------------------------------+ - | :const:`runtime` | Runtime string, e.g. browser user agent, | - | | ``'Node.js v14.18.2'``, or ``'UNKNOWN'``. | - +-----------------------------+----------------------------------------------+ - | :const:`pthreads` | ``True`` if Python is compiled with | - | | Emscripten pthreads support. | - +-----------------------------+----------------------------------------------+ - | :const:`shared_memory` | ``True`` if Python is compiled with shared | - | | memory support. | - +-----------------------------+----------------------------------------------+ + .. attribute:: _emscripten_info.emscripten_version + + Emscripten version as tuple of ints (major, minor, micro), e.g. ``(3, 1, 8)``. + + .. attribute:: _emscripten_info.runtime + + Runtime string, e.g. browser user agent, ``'Node.js v14.18.2'``, or ``'UNKNOWN'``. + + .. attribute:: _emscripten_info.pthreads + + ``True`` if Python is compiled with Emscripten pthreads support. + + .. attribute:: _emscripten_info.shared_memory + + ``True`` if Python is compiled with shared memory support. .. availability:: Emscripten. @@ -511,28 +509,62 @@ always available. The :term:`named tuple` *flags* exposes the status of command line flags. The attributes are read only. - ============================== ============================================================================================================== - attribute flag - ============================== ============================================================================================================== - :const:`debug` :option:`-d` - :const:`inspect` :option:`-i` - :const:`interactive` :option:`-i` - :const:`isolated` :option:`-I` - :const:`optimize` :option:`-O` or :option:`-OO` - :const:`dont_write_bytecode` :option:`-B` - :const:`no_user_site` :option:`-s` - :const:`no_site` :option:`-S` - :const:`ignore_environment` :option:`-E` - :const:`verbose` :option:`-v` - :const:`bytes_warning` :option:`-b` - :const:`quiet` :option:`-q` - :const:`hash_randomization` :option:`-R` - :const:`dev_mode` :option:`-X dev <-X>` (:ref:`Python Development Mode `) - :const:`utf8_mode` :option:`-X utf8 <-X>` - :const:`safe_path` :option:`-P` - :const:`int_max_str_digits` :option:`-X int_max_str_digits <-X>` (:ref:`integer string conversion length limitation `) - :const:`warn_default_encoding` :option:`-X warn_default_encoding <-X>` - ============================== ============================================================================================================== + .. list-table:: + + * - .. attribute:: flags.debug + - :option:`-d` + + * - .. attribute:: flags.inspect + - :option:`-i` + + * - .. attribute:: flags.interactive + - :option:`-i` + + * - .. attribute:: flags.isolated + - :option:`-I` + + * - .. attribute:: flags.optimize + - :option:`-O` or :option:`-OO` + + * - .. attribute:: flags.dont_write_bytecode + - :option:`-B` + + * - .. attribute:: flags.no_user_site + - :option:`-s` + + * - .. attribute:: flags.no_site + - :option:`-S` + + * - .. attribute:: flags.ignore_environment + - :option:`-E` + + * - .. attribute:: flags.verbose + - :option:`-v` + + * - .. attribute:: flags.bytes_warning + - :option:`-b` + + * - .. attribute:: flags.quiet + - :option:`-q` + + * - .. attribute:: flags.hash_randomization + - :option:`-R` + + * - .. attribute:: flags.dev_mode + - :option:`-X dev <-X>` (:ref:`Python Development Mode `) + + * - .. attribute:: flags.utf8_mode + - :option:`-X utf8 <-X>` + + * - .. attribute:: flags.safe_path + - :option:`-P` + + * - .. attribute:: flags.int_max_str_digits + - :option:`-X int_max_str_digits <-X>` + (:ref:`integer string conversion length limitation `) + + * - .. attribute:: flags.warn_default_encoding + - :option:`-X warn_default_encoding <-X>` .. versionchanged:: 3.2 Added ``quiet`` attribute for the new :option:`-q` flag. @@ -890,8 +922,8 @@ always available. | | a domain controller. | +---------------------------------------+---------------------------------+ - This function wraps the Win32 :c:func:`GetVersionEx` function; see the - Microsoft documentation on :c:func:`OSVERSIONINFOEX` for more information + This function wraps the Win32 :c:func:`!GetVersionEx` function; see the + Microsoft documentation on :c:func:`!OSVERSIONINFOEX` for more information about these fields. *platform_version* returns the major version, minor version and @@ -949,28 +981,37 @@ always available. implementation. For more details about hashing of numeric types, see :ref:`numeric-hash`. - +---------------------+--------------------------------------------------+ - | attribute | explanation | - +=====================+==================================================+ - | :const:`width` | width in bits used for hash values | - +---------------------+--------------------------------------------------+ - | :const:`modulus` | prime modulus P used for numeric hash scheme | - +---------------------+--------------------------------------------------+ - | :const:`inf` | hash value returned for a positive infinity | - +---------------------+--------------------------------------------------+ - | :const:`nan` | (this attribute is no longer used) | - +---------------------+--------------------------------------------------+ - | :const:`imag` | multiplier used for the imaginary part of a | - | | complex number | - +---------------------+--------------------------------------------------+ - | :const:`algorithm` | name of the algorithm for hashing of str, bytes, | - | | and memoryview | - +---------------------+--------------------------------------------------+ - | :const:`hash_bits` | internal output size of the hash algorithm | - +---------------------+--------------------------------------------------+ - | :const:`seed_bits` | size of the seed key of the hash algorithm | - +---------------------+--------------------------------------------------+ + .. attribute:: hash_info.width + + The width in bits used for hash values + + .. attribute:: hash_info.modulus + The prime modulus P used for numeric hash scheme + + .. attribute:: hash_info.inf + + The hash value returned for a positive infinity + + .. attribute:: hash_info.nan + + (This attribute is no longer used) + + .. attribute:: hash_info.imag + + The multiplier used for the imaginary part of a complex number + + .. attribute:: hash_info.algorithm + + The name of the algorithm for hashing of str, bytes, and memoryview + + .. attribute:: hash_info.hash_bits + + The internal output size of the hash algorithm + + .. attribute:: hash_info.seed_bits + + The size of the seed key of the hash algorithm .. versionadded:: 3.2 @@ -1048,32 +1089,31 @@ always available. A :term:`named tuple` that holds information about Python's internal representation of integers. The attributes are read only. - .. tabularcolumns:: |l|L| - - +----------------------------------------+-----------------------------------------------+ - | Attribute | Explanation | - +========================================+===============================================+ - | :const:`bits_per_digit` | number of bits held in each digit. Python | - | | integers are stored internally in base | - | | ``2**int_info.bits_per_digit`` | - +----------------------------------------+-----------------------------------------------+ - | :const:`sizeof_digit` | size in bytes of the C type used to | - | | represent a digit | - +----------------------------------------+-----------------------------------------------+ - | :const:`default_max_str_digits` | default value for | - | | :func:`sys.get_int_max_str_digits` when it | - | | is not otherwise explicitly configured. | - +----------------------------------------+-----------------------------------------------+ - | :const:`str_digits_check_threshold` | minimum non-zero value for | - | | :func:`sys.set_int_max_str_digits`, | - | | :envvar:`PYTHONINTMAXSTRDIGITS`, or | - | | :option:`-X int_max_str_digits <-X>`. | - +----------------------------------------+-----------------------------------------------+ + .. attribute:: int_info.bits_per_digit + + The number of bits held in each digit. + Python integers are stored internally in base ``2**int_info.bits_per_digit``. + + .. attribute:: int_info.sizeof_digit + + The size in bytes of the C type used to represent a digit. + + .. attribute:: int_info.default_max_str_digits + + The default value for :func:`sys.get_int_max_str_digits` + when it is not otherwise explicitly configured. + + .. attribute:: int_info.str_digits_check_threshold + + The minimum non-zero value for :func:`sys.set_int_max_str_digits`, + :envvar:`PYTHONINTMAXSTRDIGITS`, or :option:`-X int_max_str_digits <-X>`. .. versionadded:: 3.1 .. versionchanged:: 3.11 - Added ``default_max_str_digits`` and ``str_digits_check_threshold``. + + Added :attr:`~int_info.default_max_str_digits` and + :attr:`~int_info.str_digits_check_threshold`. .. data:: __interactivehook__ @@ -1501,7 +1541,7 @@ 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 frame. + :attr:`!f_trace_lines` to :const:`False` on that :ref:`frame `. ``'return'`` A function (or other code block) is about to return. The local trace @@ -1519,8 +1559,8 @@ 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 - frame. + requested by setting :attr:`!f_trace_opcodes` to :const:`True` on the + :ref:`frame `. Note that as an exception is propagated down the chain of callers, an ``'exception'`` event is generated at each level. @@ -1549,8 +1589,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:`!f_trace_lines` and + :attr:`!f_trace_opcodes` attributes added to frames .. function:: set_asyncgen_hooks(firstiter, finalizer) @@ -1675,7 +1715,7 @@ always available. However, if you are writing a library (and do not control in which context its code will be executed), be aware that the standard streams may be replaced with file-like objects like :class:`io.StringIO` which - do not support the :attr:`~io.BufferedIOBase.buffer` attribute. + do not support the :attr:!buffer` attribute. .. data:: __stdin__ @@ -1723,29 +1763,28 @@ always available. A :term:`named tuple` holding information about the thread implementation. - .. tabularcolumns:: |l|p{0.7\linewidth}| - - +------------------+---------------------------------------------------------+ - | Attribute | Explanation | - +==================+=========================================================+ - | :const:`name` | Name of the thread implementation: | - | | | - | | * ``'nt'``: Windows threads | - | | * ``'pthread'``: POSIX threads | - | | * ``'pthread-stubs'``: stub POSIX threads | - | | (on WebAssembly platforms without threading support) | - | | * ``'solaris'``: Solaris threads | - +------------------+---------------------------------------------------------+ - | :const:`lock` | Name of the lock implementation: | - | | | - | | * ``'semaphore'``: a lock uses a semaphore | - | | * ``'mutex+cond'``: a lock uses a mutex | - | | and a condition variable | - | | * ``None`` if this information is unknown | - +------------------+---------------------------------------------------------+ - | :const:`version` | Name and version of the thread library. It is a string, | - | | or ``None`` if this information is unknown. | - +------------------+---------------------------------------------------------+ + .. attribute:: thread_info.name + + The name of the thread implementation: + + * ``"nt"``: Windows threads + * ``"pthread"``: POSIX threads + * ``"pthread-stubs"``: stub POSIX threads + (on WebAssembly platforms without threading support) + * ``"solaris"``: Solaris threads + + .. attribute:: thread_info.lock + + The name of the lock implementation: + + * ``"semaphore"``: a lock uses a semaphore + * ``"mutex+cond"``: a lock uses a mutex and a condition variable + * ``None`` if this information is unknown + + .. attribute:: thread_info.version + + The name and version of the thread library. + It is a string, or ``None`` if this information is unknown. .. versionadded:: 3.3 diff --git a/Doc/library/textwrap.rst b/Doc/library/textwrap.rst index a150eefbf932ef..e2952ce3cc2ca3 100644 --- a/Doc/library/textwrap.rst +++ b/Doc/library/textwrap.rst @@ -60,7 +60,7 @@ functions should be good enough; otherwise, you should use an instance of First the whitespace in *text* is collapsed (all whitespace is replaced by single spaces). If the result fits in the *width*, it is returned. Otherwise, enough words are dropped from the end so that the remaining words - plus the :attr:`.placeholder` fit within :attr:`.width`:: + plus the *placeholder* fit within *width*:: >>> textwrap.shorten("Hello world!", width=12) 'Hello world!' diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 407a4bd837c549..763f02935cd02b 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -128,7 +128,6 @@ Doc/library/ssl.rst Doc/library/stdtypes.rst Doc/library/string.rst Doc/library/subprocess.rst -Doc/library/sys.rst Doc/library/sys_path_init.rst Doc/library/syslog.rst Doc/library/tarfile.rst From bc055a21cc3f0ece881db8535bfc9457def1a368 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 20 Aug 2023 07:01:24 -0700 Subject: [PATCH 168/632] [3.11] Resolve reference warnings in faq/programming.rst (GH-108150) (#108171) Resolve reference warnings in faq/programming.rst (GH-108150) (cherry picked from commit a390ec20f5a85b9c16e8708f117667783d08863c) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/faq/programming.rst | 6 +++--- Doc/tools/.nitignore | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst index ab5618db84f77e..f43f69b8a1ea91 100644 --- a/Doc/faq/programming.rst +++ b/Doc/faq/programming.rst @@ -454,7 +454,7 @@ There are two factors that produce this result: (the list), and both ``x`` and ``y`` refer to it. 2) Lists are :term:`mutable`, which means that you can change their content. -After the call to :meth:`~list.append`, the content of the mutable object has +After the call to :meth:`!append`, the content of the mutable object has changed from ``[]`` to ``[10]``. Since both the variables refer to the same object, using either name accesses the modified value ``[10]``. @@ -1397,7 +1397,7 @@ To see why this happens, you need to know that (a) if an object implements an :meth:`~object.__iadd__` magic method, it gets called when the ``+=`` augmented assignment is executed, and its return value is what gets used in the assignment statement; -and (b) for lists, :meth:`!__iadd__` is equivalent to calling :meth:`~list.extend` on the list +and (b) for lists, :meth:`!__iadd__` is equivalent to calling :meth:`!extend` on the list and returning the list. That's why we say that for lists, ``+=`` is a "shorthand" for :meth:`!list.extend`:: @@ -1903,7 +1903,7 @@ identity tests. This prevents the code from being confused by objects such as ``float('NaN')`` that are not equal to themselves. For example, here is the implementation of -:meth:`collections.abc.Sequence.__contains__`:: +:meth:`!collections.abc.Sequence.__contains__`:: def __contains__(self, value): for v in self: diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 763f02935cd02b..2ce7274cb37a7f 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -27,7 +27,6 @@ Doc/extending/newtypes.rst Doc/faq/design.rst Doc/faq/gui.rst Doc/faq/library.rst -Doc/faq/programming.rst Doc/glossary.rst Doc/howto/descriptor.rst Doc/howto/enum.rst From 797e3c9e4e4ace5c3693a1d4cfa37059d6e0e430 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sun, 20 Aug 2023 18:27:44 +0200 Subject: [PATCH 169/632] [3.11] Docs: Fix Sphinx warnings in license.rst (#108142) (#108176) (cherry picked by commit 4d4393139fae39db26dead33529b6ae0bafbfc58) - Fix links to stdlib modules - Silence links to external functions --- Doc/license.rst | 8 ++++---- Doc/tools/.nitignore | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Doc/license.rst b/Doc/license.rst index 4e8a25ed5ecc18..6644df65170a0c 100644 --- a/Doc/license.rst +++ b/Doc/license.rst @@ -351,8 +351,8 @@ the verbatim comments from the original code:: Sockets ------- -The :mod:`socket` module uses the functions, :func:`getaddrinfo`, and -:func:`getnameinfo`, which are coded in separate source files from the WIDE +The :mod:`socket` module uses the functions, :c:func:`!getaddrinfo`, and +:c:func:`!getnameinfo`, which are coded in separate source files from the WIDE Project, https://www.wide.ad.jp/. :: Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -537,7 +537,7 @@ The :mod:`xmlrpc.client` module contains the following notice:: test_epoll ---------- -The :mod:`test_epoll` module contains the following notice:: +The :mod:`!test.test_epoll` module contains the following notice:: Copyright (c) 2001-2006 Twisted Matrix Laboratories. @@ -842,7 +842,7 @@ and later releases derived from that, the Apache License v2 applies:: expat ----- -The :mod:`pyexpat` extension is built using an included copy of the expat +The :mod:`pyexpat ` extension is built using an included copy of the expat sources unless the build is configured ``--with-system-expat``:: Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 2ce7274cb37a7f..d80c8f1142a71e 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -160,7 +160,6 @@ Doc/library/xml.sax.rst Doc/library/xmlrpc.client.rst Doc/library/xmlrpc.server.rst Doc/library/zlib.rst -Doc/license.rst Doc/reference/compound_stmts.rst Doc/reference/datamodel.rst Doc/reference/expressions.rst From e5e87f230699f903cb286b2bb7a5529717dd432c Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sun, 20 Aug 2023 18:40:57 +0200 Subject: [PATCH 170/632] [3.11] Docs: Fix Sphinx warnings in logging.rst (GH-108139) (#108175) (cherry picked from commit c735e79afb62324624864e1943f84825249f58ed) Co-authored-by: Adam Turner <9087854+aa-turner@users.noreply.github.com> --- Doc/library/logging.rst | 38 ++++++++++++++++++++------------------ Doc/tools/.nitignore | 1 - 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst index 09dc887ee24156..f9658fa22d8b27 100644 --- a/Doc/library/logging.rst +++ b/Doc/library/logging.rst @@ -427,7 +427,7 @@ Handler Objects Handlers have the following attributes and methods. Note that :class:`Handler` is never instantiated directly; this class acts as a base for more useful -subclasses. However, the :meth:`__init__` method in subclasses needs to call +subclasses. However, the :meth:`!__init__` method in subclasses needs to call :meth:`Handler.__init__`. .. class:: Handler @@ -994,23 +994,25 @@ information into logging calls. For a usage example, see the section on 'extra'. The return value is a (*msg*, *kwargs*) tuple which has the (possibly modified) versions of the arguments passed in. -In addition to the above, :class:`LoggerAdapter` supports the following -methods of :class:`Logger`: :meth:`~Logger.debug`, :meth:`~Logger.info`, -:meth:`~Logger.warning`, :meth:`~Logger.error`, :meth:`~Logger.exception`, -:meth:`~Logger.critical`, :meth:`~Logger.log`, :meth:`~Logger.isEnabledFor`, -:meth:`~Logger.getEffectiveLevel`, :meth:`~Logger.setLevel` and -:meth:`~Logger.hasHandlers`. These methods have the same signatures as their -counterparts in :class:`Logger`, so you can use the two types of instances -interchangeably. + In addition to the above, :class:`LoggerAdapter` supports the following + methods of :class:`Logger`: :meth:`~Logger.debug`, :meth:`~Logger.info`, + :meth:`~Logger.warning`, :meth:`~Logger.error`, :meth:`~Logger.exception`, + :meth:`~Logger.critical`, :meth:`~Logger.log`, :meth:`~Logger.isEnabledFor`, + :meth:`~Logger.getEffectiveLevel`, :meth:`~Logger.setLevel` and + :meth:`~Logger.hasHandlers`. These methods have the same signatures as their + counterparts in :class:`Logger`, so you can use the two types of instances + interchangeably. -.. versionchanged:: 3.2 - The :meth:`~Logger.isEnabledFor`, :meth:`~Logger.getEffectiveLevel`, - :meth:`~Logger.setLevel` and :meth:`~Logger.hasHandlers` methods were added - to :class:`LoggerAdapter`. These methods delegate to the underlying logger. + .. versionchanged:: 3.2 + + The :meth:`~Logger.isEnabledFor`, :meth:`~Logger.getEffectiveLevel`, + :meth:`~Logger.setLevel` and :meth:`~Logger.hasHandlers` methods were added + to :class:`LoggerAdapter`. These methods delegate to the underlying logger. + + .. versionchanged:: 3.6 -.. versionchanged:: 3.6 - Attribute :attr:`manager` and method :meth:`_log` were added, which - delegate to the underlying logger and allow adapters to be nested. + Attribute :attr:`!manager` and method :meth:`!_log` were added, which + delegate to the underlying logger and allow adapters to be nested. Thread Safety @@ -1381,8 +1383,8 @@ functions. .. function:: setLoggerClass(klass) Tells the logging system to use the class *klass* when instantiating a logger. - The class should define :meth:`__init__` such that only a name argument is - required, and the :meth:`__init__` should call :meth:`Logger.__init__`. This + The class should define :meth:`!__init__` such that only a name argument is + required, and the :meth:`!__init__` should call :meth:`!Logger.__init__`. This function is typically called before any loggers are instantiated by applications which need to use custom logger behavior. After this call, as at any other time, do not instantiate loggers directly using the subclass: continue to use diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index d80c8f1142a71e..f36e2b8dc98542 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -92,7 +92,6 @@ Doc/library/inspect.rst Doc/library/locale.rst Doc/library/logging.config.rst Doc/library/logging.handlers.rst -Doc/library/logging.rst Doc/library/lzma.rst Doc/library/mailbox.rst Doc/library/mmap.rst From ff82da17b615c13cb4e46f251a5c0c6cdeedc633 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Sun, 20 Aug 2023 19:56:14 +0100 Subject: [PATCH 171/632] [3.11] Resolve reference warnings in faq/design.rst (GH-108148) (#108181) --- Doc/faq/design.rst | 4 ++-- Doc/tools/.nitignore | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Doc/faq/design.rst b/Doc/faq/design.rst index 9dbfacd73cc6c7..83c0152c85e84a 100644 --- a/Doc/faq/design.rst +++ b/Doc/faq/design.rst @@ -581,9 +581,9 @@ exhaustive test suites that exercise every line of code in a module. An appropriate testing discipline can help build large complex applications in Python as well as having interface specifications would. In fact, it can be better because an interface specification cannot test certain properties of a -program. For example, the :meth:`append` method is expected to add new elements +program. For example, the :meth:`!list.append` method is expected to add new elements to the end of some internal list; an interface specification cannot test that -your :meth:`append` implementation will actually do this correctly, but it's +your :meth:`!list.append` implementation will actually do this correctly, but it's trivial to check this property in a test suite. Writing test suites is very helpful, and you might want to design your code to diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index f36e2b8dc98542..319c77776b7b84 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -24,7 +24,6 @@ Doc/c-api/typeobj.rst Doc/c-api/unicode.rst Doc/extending/extending.rst Doc/extending/newtypes.rst -Doc/faq/design.rst Doc/faq/gui.rst Doc/faq/library.rst Doc/glossary.rst From 4be05aab33c4342d37c5b6b083975f08a16c8cc1 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 20 Aug 2023 12:26:01 -0700 Subject: [PATCH 172/632] [3.11] Resolve reference warnings in faq/library.rst (GH-108149) (#108183) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Co-authored-by: Erlend E. Aasland Co-authored-by: Hugo van Kemenade --- Doc/faq/library.rst | 13 ++++++++----- Doc/tools/.nitignore | 1 - 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Doc/faq/library.rst b/Doc/faq/library.rst index b43c7505c0401c..c69910718f0c92 100644 --- a/Doc/faq/library.rst +++ b/Doc/faq/library.rst @@ -111,7 +111,7 @@ Is there an equivalent to C's onexit() in Python? ------------------------------------------------- The :mod:`atexit` module provides a register function that is similar to C's -:c:func:`onexit`. +:c:func:`!onexit`. Why don't my signal handlers work? @@ -397,7 +397,7 @@ These aren't:: D[x] = D[x] + 1 Operations that replace other objects may invoke those other objects' -:meth:`__del__` method when their reference count reaches zero, and that can +:meth:`~object.__del__` method when their reference count reaches zero, and that can affect things. This is especially true for the mass updates to dictionaries and lists. When in doubt, use a mutex! @@ -765,14 +765,17 @@ The :mod:`select` module is commonly used to help with asynchronous I/O on sockets. To prevent the TCP connect from blocking, you can set the socket to non-blocking -mode. Then when you do the :meth:`socket.connect`, you will either connect immediately +mode. Then when you do the :meth:`~socket.socket.connect`, +you will either connect immediately (unlikely) or get an exception that contains the error number as ``.errno``. ``errno.EINPROGRESS`` indicates that the connection is in progress, but hasn't finished yet. Different OSes will return different values, so you're going to have to check what's returned on your system. -You can use the :meth:`socket.connect_ex` method to avoid creating an exception. It will -just return the errno value. To poll, you can call :meth:`socket.connect_ex` again later +You can use the :meth:`~socket.socket.connect_ex` method +to avoid creating an exception. +It will just return the errno value. +To poll, you can call :meth:`~socket.socket.connect_ex` again later -- ``0`` or ``errno.EISCONN`` indicate that you're connected -- or you can pass this socket to :meth:`select.select` to check if it's writable. diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 319c77776b7b84..8325f5b3364139 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -25,7 +25,6 @@ Doc/c-api/unicode.rst Doc/extending/extending.rst Doc/extending/newtypes.rst Doc/faq/gui.rst -Doc/faq/library.rst Doc/glossary.rst Doc/howto/descriptor.rst Doc/howto/enum.rst From d4c66bd7328f6aadb584d54f81ae5281a66d08c4 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 21 Aug 2023 01:36:37 -0700 Subject: [PATCH 173/632] [3.11] gh-107895: Fix test_asyncio.test_runners when run it in CPython's "development mode" (GH-108168) (GH-108197) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 014a5b71e7538926ae1c03c8c5ea13c96e741be3) Co-authored-by: Joon Hwan 김준환 --- Lib/test/test_asyncio/test_runners.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_asyncio/test_runners.py b/Lib/test/test_asyncio/test_runners.py index 4e3acb97c85d37..8a4aba6d470bac 100644 --- a/Lib/test/test_asyncio/test_runners.py +++ b/Lib/test/test_asyncio/test_runners.py @@ -102,11 +102,14 @@ async def main(expected): loop = asyncio.get_event_loop() self.assertIs(loop.get_debug(), expected) - asyncio.run(main(False)) + asyncio.run(main(False), debug=False) asyncio.run(main(True), debug=True) with mock.patch('asyncio.coroutines._is_debug_mode', lambda: True): asyncio.run(main(True)) asyncio.run(main(False), debug=False) + with mock.patch('asyncio.coroutines._is_debug_mode', lambda: False): + asyncio.run(main(True), debug=True) + asyncio.run(main(False)) def test_asyncio_run_from_running_loop(self): async def main(): From a1d2e2c2ac93d125576592ce227ec999f5a3a8c5 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 21 Aug 2023 03:37:09 -0700 Subject: [PATCH 174/632] [3.11] gh-105736: Sync pure python version of OrderedDict with the C version (GH-108098) (GH-108201) (cherry picked from commit 20cc90c0df3e368fe7cb63d958f0b17a78fa9d0a) Co-authored-by: Raymond Hettinger --- Lib/collections/__init__.py | 16 +++++++++------- Lib/test/test_ordered_dict.py | 11 +++++++++++ ...023-08-17-14-45-25.gh-issue-105736.NJsH7r.rst | 3 +++ 3 files changed, 23 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-08-17-14-45-25.gh-issue-105736.NJsH7r.rst diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py index 7af8dcd526df81..69398ac1164685 100644 --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -90,17 +90,19 @@ class OrderedDict(dict): # Individual links are kept alive by the hard reference in self.__map. # Those hard references disappear when a key is deleted from an OrderedDict. + def __new__(cls, /, *args, **kwds): + "Create the ordered dict object and set up the underlying structures." + self = dict.__new__(cls) + self.__hardroot = _Link() + self.__root = root = _proxy(self.__hardroot) + root.prev = root.next = root + self.__map = {} + return self + def __init__(self, other=(), /, **kwds): '''Initialize an ordered dictionary. The signature is the same as regular dictionaries. Keyword argument order is preserved. ''' - try: - self.__root - except AttributeError: - self.__hardroot = _Link() - self.__root = root = _proxy(self.__hardroot) - root.prev = root.next = root - self.__map = {} self.__update(other, **kwds) def __setitem__(self, key, value, diff --git a/Lib/test/test_ordered_dict.py b/Lib/test/test_ordered_dict.py index 37447fd249b8c0..dfd329aa0a9fa7 100644 --- a/Lib/test/test_ordered_dict.py +++ b/Lib/test/test_ordered_dict.py @@ -122,6 +122,17 @@ def items(self): self.OrderedDict(Spam()) self.assertEqual(calls, ['keys']) + def test_overridden_init(self): + # Sync-up pure Python OD class with C class where + # a consistent internal state is created in __new__ + # rather than __init__. + OrderedDict = self.OrderedDict + class ODNI(OrderedDict): + def __init__(*args, **kwargs): + pass + od = ODNI() + od['a'] = 1 # This used to fail because __init__ was bypassed + def test_fromkeys(self): OrderedDict = self.OrderedDict od = OrderedDict.fromkeys('abc') diff --git a/Misc/NEWS.d/next/Library/2023-08-17-14-45-25.gh-issue-105736.NJsH7r.rst b/Misc/NEWS.d/next/Library/2023-08-17-14-45-25.gh-issue-105736.NJsH7r.rst new file mode 100644 index 00000000000000..7fed54a5184c0f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-08-17-14-45-25.gh-issue-105736.NJsH7r.rst @@ -0,0 +1,3 @@ +Harmonized the pure Python version of OrderedDict with the C version. Now, +both versions set up their internal state in ``__new__``. Formerly, the pure +Python version did the set up in ``__init__``. From a372274a9b1d1d474831ccc6dd9d4de291acd37f Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 21 Aug 2023 03:44:25 -0700 Subject: [PATCH 175/632] [3.11] Docs: document 'manager' and '_log' attrs of logging.Logging (GH-108145) (GH-108189) (cherry picked from commit f904aa4e1f6943e5bd9a8a73cf762f063e6fa247) Authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Co-authored-by: Erlend E. Aasland --- Doc/library/logging.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst index f9658fa22d8b27..1fd1d0966a6120 100644 --- a/Doc/library/logging.rst +++ b/Doc/library/logging.rst @@ -994,6 +994,14 @@ information into logging calls. For a usage example, see the section on 'extra'. The return value is a (*msg*, *kwargs*) tuple which has the (possibly modified) versions of the arguments passed in. + .. attribute:: manager + + Delegates to the underlying :attr:`!manager`` on *logger*. + + .. attribute:: _log + + Delegates to the underlying :meth:`!_log`` method on *logger*. + In addition to the above, :class:`LoggerAdapter` supports the following methods of :class:`Logger`: :meth:`~Logger.debug`, :meth:`~Logger.info`, :meth:`~Logger.warning`, :meth:`~Logger.error`, :meth:`~Logger.exception`, From be9965308022733bc7b0412a99da9fc80c6c7bef Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 21 Aug 2023 13:53:36 +0300 Subject: [PATCH 176/632] [3.11] Improve references in the tutorial (GH-108069) (GH-108204) * Use full qualified names for references (even if they do not work now, they will work in future). * Silence references to examples. (cherry picked from commit 622ddc41674c2566062af82f7b079aa01d2aae8c) --- Doc/tools/.nitignore | 2 -- Doc/tutorial/classes.rst | 43 +++++++++++++++++---------------- Doc/tutorial/controlflow.rst | 2 +- Doc/tutorial/datastructures.rst | 12 ++++----- Doc/tutorial/inputoutput.rst | 8 +++--- Doc/tutorial/modules.rst | 30 +++++++++++------------ 6 files changed, 48 insertions(+), 49 deletions(-) diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 8325f5b3364139..8c44199cb861d3 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -166,9 +166,7 @@ Doc/tutorial/appendix.rst Doc/tutorial/classes.rst Doc/tutorial/controlflow.rst Doc/tutorial/datastructures.rst -Doc/tutorial/inputoutput.rst Doc/tutorial/introduction.rst -Doc/tutorial/modules.rst Doc/using/cmdline.rst Doc/using/configure.rst Doc/using/windows.rst diff --git a/Doc/tutorial/classes.rst b/Doc/tutorial/classes.rst index db4ca92ae34227..e4002cfa351936 100644 --- a/Doc/tutorial/classes.rst +++ b/Doc/tutorial/classes.rst @@ -91,7 +91,7 @@ Attributes may be read-only or writable. In the latter case, assignment to attributes is possible. Module attributes are writable: you can write ``modname.the_answer = 42``. Writable attributes may also be deleted with the :keyword:`del` statement. For example, ``del modname.the_answer`` will remove -the attribute :attr:`the_answer` from the object named by ``modname``. +the attribute :attr:`!the_answer` from the object named by ``modname``. Namespaces are created at different moments and have different lifetimes. The namespace containing the built-in names is created when the Python interpreter @@ -249,7 +249,7 @@ created. This is basically a wrapper around the contents of the namespace created by the class definition; we'll learn more about class objects in the next section. The original local scope (the one in effect just before the class definition was entered) is reinstated, and the class object is bound here to the -class name given in the class definition header (:class:`ClassName` in the +class name given in the class definition header (:class:`!ClassName` in the example). @@ -291,20 +291,20 @@ variable ``x``. The instantiation operation ("calling" a class object) creates an empty object. Many classes like to create objects with instances customized to a specific initial state. Therefore a class may define a special method named -:meth:`__init__`, like this:: +:meth:`~object.__init__`, like this:: def __init__(self): self.data = [] -When a class defines an :meth:`__init__` method, class instantiation -automatically invokes :meth:`__init__` for the newly created class instance. So +When a class defines an :meth:`~object.__init__` method, class instantiation +automatically invokes :meth:`!__init__` for the newly created class instance. So in this example, a new, initialized instance can be obtained by:: x = MyClass() -Of course, the :meth:`__init__` method may have arguments for greater +Of course, the :meth:`~object.__init__` method may have arguments for greater flexibility. In that case, arguments given to the class instantiation operator -are passed on to :meth:`__init__`. For example, :: +are passed on to :meth:`!__init__`. For example, :: >>> class Complex: ... def __init__(self, realpart, imagpart): @@ -328,7 +328,7 @@ attribute names: data attributes and methods. *data attributes* correspond to "instance variables" in Smalltalk, and to "data members" in C++. Data attributes need not be declared; like local variables, they spring into existence when they are first assigned to. For example, if -``x`` is the instance of :class:`MyClass` created above, the following piece of +``x`` is the instance of :class:`!MyClass` created above, the following piece of code will print the value ``16``, without leaving a trace:: x.counter = 1 @@ -363,7 +363,7 @@ Usually, a method is called right after it is bound:: x.f() -In the :class:`MyClass` example, this will return the string ``'hello world'``. +In the :class:`!MyClass` example, this will return the string ``'hello world'``. However, it is not necessary to call a method right away: ``x.f`` is a method object, and can be stored away and called at a later time. For example:: @@ -375,7 +375,7 @@ will continue to print ``hello world`` until the end of time. What exactly happens when a method is called? You may have noticed that ``x.f()`` was called without an argument above, even though the function -definition for :meth:`f` specified an argument. What happened to the argument? +definition for :meth:`!f` specified an argument. What happened to the argument? Surely Python raises an exception when a function that requires an argument is called without any --- even if the argument isn't actually used... @@ -532,9 +532,9 @@ variable in the class is also ok. For example:: h = g -Now ``f``, ``g`` and ``h`` are all attributes of class :class:`C` that refer to +Now ``f``, ``g`` and ``h`` are all attributes of class :class:`!C` that refer to function objects, and consequently they are all methods of instances of -:class:`C` --- ``h`` being exactly equivalent to ``g``. Note that this practice +:class:`!C` --- ``h`` being exactly equivalent to ``g``. Note that this practice usually only serves to confuse the reader of a program. Methods may call other methods by using method attributes of the ``self`` @@ -581,7 +581,7 @@ this:: . -The name :class:`BaseClassName` must be defined in a scope containing the +The name :class:`!BaseClassName` must be defined in a scope containing the derived class definition. In place of a base class name, other arbitrary expressions are also allowed. This can be useful, for example, when the base class is defined in another module:: @@ -644,9 +644,9 @@ multiple base classes looks like this:: For most purposes, in the simplest cases, you can think of the search for attributes inherited from a parent class as depth-first, left-to-right, not searching twice in the same class where there is an overlap in the hierarchy. -Thus, if an attribute is not found in :class:`DerivedClassName`, it is searched -for in :class:`Base1`, then (recursively) in the base classes of :class:`Base1`, -and if it was not found there, it was searched for in :class:`Base2`, and so on. +Thus, if an attribute is not found in :class:`!DerivedClassName`, it is searched +for in :class:`!Base1`, then (recursively) in the base classes of :class:`!Base1`, +and if it was not found there, it was searched for in :class:`!Base2`, and so on. In fact, it is slightly more complex than that; the method resolution order changes dynamically to support cooperative calls to :func:`super`. This @@ -759,7 +759,8 @@ is to use :mod:`dataclasses` for this purpose:: A piece of Python code that expects a particular abstract data type can often be passed a class that emulates the methods of that data type instead. For instance, if you have a function that formats some data from a file object, you -can define a class with methods :meth:`read` and :meth:`!readline` that get the +can define a class with methods :meth:`~io.TextIOBase.read` and +:meth:`~io.TextIOBase.readline` that get the data from a string buffer instead, and pass it as an argument. .. (Unfortunately, this technique has its limitations: a class can't define @@ -768,7 +769,7 @@ data from a string buffer instead, and pass it as an argument. 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 +object with the method :meth:`!m`, and ``m.__func__`` is the function object corresponding to the method. @@ -817,9 +818,9 @@ using the :func:`next` built-in function; this example shows how it all works:: StopIteration Having seen the mechanics behind the iterator protocol, it is easy to add -iterator behavior to your classes. Define an :meth:`__iter__` method which +iterator behavior to your classes. Define an :meth:`~container.__iter__` method which returns an object with a :meth:`~iterator.__next__` method. If the class -defines :meth:`__next__`, then :meth:`__iter__` can just return ``self``:: +defines :meth:`!__next__`, then :meth:`!__iter__` can just return ``self``:: class Reverse: """Iterator for looping over a sequence backwards.""" @@ -878,7 +879,7 @@ easy to create:: Anything that can be done with generators can also be done with class-based iterators as described in the previous section. What makes generators so -compact is that the :meth:`__iter__` and :meth:`~generator.__next__` methods +compact is that the :meth:`~iterator.__iter__` and :meth:`~generator.__next__` methods are created automatically. Another key feature is that the local variables and execution state are diff --git a/Doc/tutorial/controlflow.rst b/Doc/tutorial/controlflow.rst index 138d87f892e891..4bcc3768111ccd 100644 --- a/Doc/tutorial/controlflow.rst +++ b/Doc/tutorial/controlflow.rst @@ -534,7 +534,7 @@ This example, as usual, demonstrates some new Python features: Different types define different methods. Methods of different types may have the same name without causing ambiguity. (It is possible to define your own object types and methods, using *classes*, see :ref:`tut-classes`) - The method :meth:`append` shown in the example is defined for list objects; it + The method :meth:`~list.append` shown in the example is defined for list objects; it adds a new element at the end of the list. In this example it is equivalent to ``result = result + [a]``, but more efficient. diff --git a/Doc/tutorial/datastructures.rst b/Doc/tutorial/datastructures.rst index c8e89d9b79bddd..87614d082a1d4e 100644 --- a/Doc/tutorial/datastructures.rst +++ b/Doc/tutorial/datastructures.rst @@ -143,8 +143,8 @@ Using Lists as Stacks The list methods make it very easy to use a list as a stack, where the last element added is the first element retrieved ("last-in, first-out"). To add an -item to the top of the stack, use :meth:`append`. To retrieve an item from the -top of the stack, use :meth:`pop` without an explicit index. For example:: +item to the top of the stack, use :meth:`~list.append`. To retrieve an item from the +top of the stack, use :meth:`~list.pop` without an explicit index. For example:: >>> stack = [3, 4, 5] >>> stack.append(6) @@ -341,7 +341,7 @@ The :keyword:`!del` statement ============================= There is a way to remove an item from a list given its index instead of its -value: the :keyword:`del` statement. This differs from the :meth:`pop` method +value: the :keyword:`del` statement. This differs from the :meth:`~list.pop` method which returns a value. The :keyword:`!del` statement can also be used to remove slices from a list or clear the entire list (which we did earlier by assignment of an empty list to the slice). For example:: @@ -501,8 +501,8 @@ any immutable type; strings and numbers can always be keys. Tuples can be used as keys if they contain only strings, numbers, or tuples; if a tuple contains any mutable object either directly or indirectly, it cannot be used as a key. You can't use lists as keys, since lists can be modified in place using index -assignments, slice assignments, or methods like :meth:`append` and -:meth:`extend`. +assignments, slice assignments, or methods like :meth:`~list.append` and +:meth:`~list.extend`. It is best to think of a dictionary as a set of *key: value* pairs, with the requirement that the keys are unique (within one dictionary). A pair of @@ -567,7 +567,7 @@ Looping Techniques ================== When looping through dictionaries, the key and corresponding value can be -retrieved at the same time using the :meth:`items` method. :: +retrieved at the same time using the :meth:`~dict.items` method. :: >>> knights = {'gallahad': 'the pure', 'robin': 'the brave'} >>> for k, v in knights.items(): diff --git a/Doc/tutorial/inputoutput.rst b/Doc/tutorial/inputoutput.rst index f5cdd84cbadefe..fe9ca9ccb9c7e0 100644 --- a/Doc/tutorial/inputoutput.rst +++ b/Doc/tutorial/inputoutput.rst @@ -15,7 +15,7 @@ Fancier Output Formatting ========================= So far we've encountered two ways of writing values: *expression statements* and -the :func:`print` function. (A third way is using the :meth:`write` method +the :func:`print` function. (A third way is using the :meth:`~io.TextIOBase.write` method of file objects; the standard output file can be referenced as ``sys.stdout``. See the Library Reference for more information on this.) @@ -456,8 +456,8 @@ to the very file end with ``seek(0, 2)``) and the only valid *offset* values are those returned from the ``f.tell()``, or zero. Any other *offset* value produces undefined behaviour. -File objects have some additional methods, such as :meth:`~file.isatty` and -:meth:`~file.truncate` which are less frequently used; consult the Library +File objects have some additional methods, such as :meth:`~io.IOBase.isatty` and +:meth:`~io.IOBase.truncate` which are less frequently used; consult the Library Reference for a complete guide to file objects. @@ -469,7 +469,7 @@ Saving structured data with :mod:`json` .. index:: pair: module; json Strings can easily be written to and read from a file. Numbers take a bit more -effort, since the :meth:`read` method only returns strings, which will have to +effort, since the :meth:`~io.TextIOBase.read` method only returns strings, which will have to be passed to a function like :func:`int`, which takes a string like ``'123'`` and returns its numeric value 123. When you want to save more complex data types like nested lists and dictionaries, parsing and serializing by hand diff --git a/Doc/tutorial/modules.rst b/Doc/tutorial/modules.rst index 734dd1cfe6871a..bf9e8e0b7b8066 100644 --- a/Doc/tutorial/modules.rst +++ b/Doc/tutorial/modules.rst @@ -183,7 +183,7 @@ The Module Search Path .. index:: triple: module; search; path -When a module named :mod:`spam` is imported, the interpreter first searches for +When a module named :mod:`!spam` is imported, the interpreter first searches for a built-in module with that name. These module names are listed in :data:`sys.builtin_module_names`. If not found, it then searches for a file named :file:`spam.py` in a list of directories given by the variable @@ -389,7 +389,7 @@ Packages ======== Packages are a way of structuring Python's module namespace by using "dotted -module names". For example, the module name :mod:`A.B` designates a submodule +module names". For example, the module name :mod:`!A.B` designates a submodule named ``B`` in a package named ``A``. Just like the use of modules saves the authors of different modules from having to worry about each other's global variable names, the use of dotted module names saves the authors of multi-module @@ -448,7 +448,7 @@ example:: import sound.effects.echo -This loads the submodule :mod:`sound.effects.echo`. It must be referenced with +This loads the submodule :mod:`!sound.effects.echo`. It must be referenced with its full name. :: sound.effects.echo.echofilter(input, output, delay=0.7, atten=4) @@ -457,7 +457,7 @@ An alternative way of importing the submodule is:: from sound.effects import echo -This also loads the submodule :mod:`echo`, and makes it available without its +This also loads the submodule :mod:`!echo`, and makes it available without its package prefix, so it can be used as follows:: echo.echofilter(input, output, delay=0.7, atten=4) @@ -466,8 +466,8 @@ Yet another variation is to import the desired function or variable directly:: from sound.effects.echo import echofilter -Again, this loads the submodule :mod:`echo`, but this makes its function -:func:`echofilter` directly available:: +Again, this loads the submodule :mod:`!echo`, but this makes its function +:func:`!echofilter` directly available:: echofilter(input, output, delay=0.7, atten=4) @@ -510,7 +510,7 @@ code:: __all__ = ["echo", "surround", "reverse"] This would mean that ``from sound.effects import *`` would import the three -named submodules of the :mod:`sound.effects` package. +named submodules of the :mod:`!sound.effects` package. Be aware that submodules might become shadowed by locally defined names. For example, if you added a ``reverse`` function to the @@ -529,8 +529,8 @@ would only import the two submodules ``echo`` and ``surround``, but *not* the return msg[::-1] # in the case of a 'from sound.effects import *' If ``__all__`` is not defined, the statement ``from sound.effects import *`` -does *not* import all submodules from the package :mod:`sound.effects` into the -current namespace; it only ensures that the package :mod:`sound.effects` has +does *not* import all submodules from the package :mod:`!sound.effects` into the +current namespace; it only ensures that the package :mod:`!sound.effects` has been imported (possibly running any initialization code in :file:`__init__.py`) and then imports whatever names are defined in the package. This includes any names defined (and submodules explicitly loaded) by :file:`__init__.py`. It @@ -541,8 +541,8 @@ previous :keyword:`import` statements. Consider this code:: import sound.effects.surround from sound.effects import * -In this example, the :mod:`echo` and :mod:`surround` modules are imported in the -current namespace because they are defined in the :mod:`sound.effects` package +In this example, the :mod:`!echo` and :mod:`!surround` modules are imported in the +current namespace because they are defined in the :mod:`!sound.effects` package when the ``from...import`` statement is executed. (This also works when ``__all__`` is defined.) @@ -561,15 +561,15 @@ packages. Intra-package References ------------------------ -When packages are structured into subpackages (as with the :mod:`sound` package +When packages are structured into subpackages (as with the :mod:`!sound` package in the example), you can use absolute imports to refer to submodules of siblings -packages. For example, if the module :mod:`sound.filters.vocoder` needs to use -the :mod:`echo` module in the :mod:`sound.effects` package, it can use ``from +packages. For example, if the module :mod:`!sound.filters.vocoder` needs to use +the :mod:`!echo` module in the :mod:`!sound.effects` package, it can use ``from sound.effects import echo``. You can also write relative imports, with the ``from module import name`` form of import statement. These imports use leading dots to indicate the current and -parent packages involved in the relative import. From the :mod:`surround` +parent packages involved in the relative import. From the :mod:`!surround` module for example, you might use:: from . import echo From ed67e60f48d4e2d881eab0ab12e31e259b126f28 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 21 Aug 2023 04:53:05 -0700 Subject: [PATCH 177/632] [3.11] gh-107916: Save the error code before decoding the filename in PyErr_SetFromErrnoWithFilename() etc (GH-107929) (GH-108206) (cherry picked from commit 80bdebdd8593f007a2232ec04a7729bba6ebf12c) Co-authored-by: Serhiy Storchaka --- .../C API/2023-08-14-10-59-03.gh-issue-107916.KH4Muo.rst | 4 ++++ Python/errors.c | 8 ++++++++ 2 files changed, 12 insertions(+) create mode 100644 Misc/NEWS.d/next/C API/2023-08-14-10-59-03.gh-issue-107916.KH4Muo.rst diff --git a/Misc/NEWS.d/next/C API/2023-08-14-10-59-03.gh-issue-107916.KH4Muo.rst b/Misc/NEWS.d/next/C API/2023-08-14-10-59-03.gh-issue-107916.KH4Muo.rst new file mode 100644 index 00000000000000..f1f16609b118ba --- /dev/null +++ b/Misc/NEWS.d/next/C API/2023-08-14-10-59-03.gh-issue-107916.KH4Muo.rst @@ -0,0 +1,4 @@ +C API functions :c:func:`PyErr_SetFromErrnoWithFilename`, +:c:func:`PyErr_SetExcFromWindowsErrWithFilename` and +:c:func:`PyErr_SetFromWindowsErrWithFilename` save now the error code before +calling :c:func:`PyUnicode_DecodeFSDefault`. diff --git a/Python/errors.c b/Python/errors.c index d693f3a24126a9..8e150c3d008b7e 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -856,10 +856,12 @@ PyErr_SetFromErrnoWithFilename(PyObject *exc, const char *filename) { PyObject *name = NULL; if (filename) { + int i = errno; name = PyUnicode_DecodeFSDefault(filename); if (name == NULL) { return NULL; } + errno = i; } PyObject *result = PyErr_SetFromErrnoWithFilenameObjects(exc, name, NULL); Py_XDECREF(name); @@ -959,6 +961,9 @@ PyObject *PyErr_SetExcFromWindowsErrWithFilename( { PyObject *name = NULL; if (filename) { + if ((DWORD)ierr == 0) { + ierr = (int)GetLastError(); + } name = PyUnicode_DecodeFSDefault(filename); if (name == NULL) { return NULL; @@ -989,6 +994,9 @@ PyObject *PyErr_SetFromWindowsErrWithFilename( { PyObject *name = NULL; if (filename) { + if ((DWORD)ierr == 0) { + ierr = (int)GetLastError(); + } name = PyUnicode_DecodeFSDefault(filename); if (name == NULL) { return NULL; From 75617ac3f002a8e9d0dedab45f2293d921cd1a25 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 21 Aug 2023 08:16:59 -0700 Subject: [PATCH 178/632] [3.11] gh-107396: tarfiles: set self.exception before _init_read_gz() (GH-107485) (GH-108208) gh-107396: tarfiles: set self.exception before _init_read_gz() (GH-107485) In the stack call of: _init_read_gz() ``` _read, tarfile.py:548 read, tarfile.py:526 _init_read_gz, tarfile.py:491 ``` a try;except exists that uses `self.exception`, so it needs to be set before calling _init_read_gz(). (cherry picked from commit 37135d25e269ede92bc7da363bebfa574782e59a) Co-authored-by: balmeida-nokia <83089745+balmeida-nokia@users.noreply.github.com> --- Lib/tarfile.py | 2 +- Lib/test/test_tarfile.py | 17 +++++++++++++++++ ...23-07-31-07-36-24.gh-issue-107396.3_Kh6D.rst | 1 + 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2023-07-31-07-36-24.gh-issue-107396.3_Kh6D.rst diff --git a/Lib/tarfile.py b/Lib/tarfile.py index 130b5e0f45dcd5..b7adff6e1723bc 100755 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -372,8 +372,8 @@ def __init__(self, name, mode, comptype, fileobj, bufsize): self.zlib = zlib self.crc = zlib.crc32(b"") if mode == "r": - self._init_read_gz() self.exception = zlib.error + self._init_read_gz() else: self._init_write_gz() diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index cdea033ec1244a..dc7ff852363cf5 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -908,6 +908,23 @@ class LzmaDetectReadTest(LzmaTest, DetectReadTest): pass +class GzipBrokenHeaderCorrectException(GzipTest, unittest.TestCase): + """ + See: https://github.com/python/cpython/issues/107396 + """ + def runTest(self): + f = io.BytesIO( + b'\x1f\x8b' # header + b'\x08' # compression method + b'\x04' # flags + b'\0\0\0\0\0\0' # timestamp, compression data, OS ID + b'\0\x01' # size + b'\0\0\0\0\0' # corrupt data (zeros) + ) + with self.assertRaises(tarfile.ReadError): + tarfile.open(fileobj=f, mode='r|gz') + + class MemberReadTest(ReadTest, unittest.TestCase): def _test_member(self, tarinfo, chksum=None, **kwargs): diff --git a/Misc/NEWS.d/next/Library/2023-07-31-07-36-24.gh-issue-107396.3_Kh6D.rst b/Misc/NEWS.d/next/Library/2023-07-31-07-36-24.gh-issue-107396.3_Kh6D.rst new file mode 100644 index 00000000000000..73bff4bdbe024d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-07-31-07-36-24.gh-issue-107396.3_Kh6D.rst @@ -0,0 +1 @@ +tarfiles; Fixed use before assignment of self.exception for gzip decompression From 145d9252b70dff1afd798a1cde7bb5bdf2b103b0 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 21 Aug 2023 10:35:36 -0700 Subject: [PATCH 179/632] [3.11] gh-107298: Fix references to deprecated and removed PyUnicode C API (GH-108077) (GH-108226) (cherry picked from commit db55383829ccd5ce80c551d60f26851346741fdf) Co-authored-by: Serhiy Storchaka --- Doc/whatsnew/3.11.rst | 26 ++++++++-------- Doc/whatsnew/3.3.rst | 66 ++++++++++++++++++++-------------------- Doc/whatsnew/3.6.rst | 6 ++-- Doc/whatsnew/3.9.rst | 4 +-- Misc/NEWS.d/3.11.0b1.rst | 6 ++-- 5 files changed, 54 insertions(+), 54 deletions(-) diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index f102aa599069c2..9054452fe12d51 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -2591,22 +2591,22 @@ Pending Removal in Python 3.12 The following C APIs have been deprecated in earlier Python releases, and will be removed in Python 3.12. -* :c:func:`PyUnicode_AS_DATA` -* :c:func:`PyUnicode_AS_UNICODE` -* :c:func:`PyUnicode_AsUnicodeAndSize` -* :c:func:`PyUnicode_AsUnicode` -* :c:func:`PyUnicode_FromUnicode` -* :c:func:`PyUnicode_GET_DATA_SIZE` -* :c:func:`PyUnicode_GET_SIZE` -* :c:func:`PyUnicode_GetSize` +* :c:func:`!PyUnicode_AS_DATA` +* :c:func:`!PyUnicode_AS_UNICODE` +* :c:func:`!PyUnicode_AsUnicodeAndSize` +* :c:func:`!PyUnicode_AsUnicode` +* :c:func:`!PyUnicode_FromUnicode` +* :c:func:`!PyUnicode_GET_DATA_SIZE` +* :c:func:`!PyUnicode_GET_SIZE` +* :c:func:`!PyUnicode_GetSize` * :c:func:`PyUnicode_IS_COMPACT` * :c:func:`PyUnicode_IS_READY` * :c:func:`PyUnicode_READY` -* :c:func:`Py_UNICODE_WSTR_LENGTH` -* :c:func:`_PyUnicode_AsUnicode` -* :c:macro:`PyUnicode_WCHAR_KIND` +* :c:func:`!PyUnicode_WSTR_LENGTH` +* :c:func:`!_PyUnicode_AsUnicode` +* :c:macro:`!PyUnicode_WCHAR_KIND` * :c:type:`PyUnicodeObject` -* :c:func:`PyUnicode_InternImmortal()` +* :c:func:`!PyUnicode_InternImmortal` .. _whatsnew311-c-api-removed: @@ -2614,7 +2614,7 @@ and will be removed in Python 3.12. Removed ------- -* :c:func:`PyFrame_BlockSetup` and :c:func:`PyFrame_BlockPop` have been +* :c:func:`!PyFrame_BlockSetup` and :c:func:`!PyFrame_BlockPop` have been removed. (Contributed by Mark Shannon in :issue:`40222`.) diff --git a/Doc/whatsnew/3.3.rst b/Doc/whatsnew/3.3.rst index 9fb06c1d1f807b..91ffc99325a014 100644 --- a/Doc/whatsnew/3.3.rst +++ b/Doc/whatsnew/3.3.rst @@ -249,7 +249,7 @@ Changes introduced by :pep:`393` are the following: non-BMP code points. * The value of :data:`sys.maxunicode` is now always ``1114111`` (``0x10FFFF`` - in hexadecimal). The :c:func:`PyUnicode_GetMax` function still returns + in hexadecimal). The :c:func:`!PyUnicode_GetMax` function still returns either ``0xFFFF`` or ``0x10FFFF`` for backward compatibility, and it should not be used with the new Unicode API (see :issue:`13054`). @@ -2195,7 +2195,7 @@ Changes to Python's build process and to the C API include: * :c:macro:`PyUnicode_DATA`, :c:macro:`PyUnicode_1BYTE_DATA`, :c:macro:`PyUnicode_2BYTE_DATA`, :c:macro:`PyUnicode_4BYTE_DATA` * :c:macro:`PyUnicode_KIND` with :c:enum:`PyUnicode_Kind` enum: - :c:data:`PyUnicode_WCHAR_KIND`, :c:data:`PyUnicode_1BYTE_KIND`, + :c:data:`!PyUnicode_WCHAR_KIND`, :c:data:`PyUnicode_1BYTE_KIND`, :c:data:`PyUnicode_2BYTE_KIND`, :c:data:`PyUnicode_4BYTE_KIND` * :c:macro:`PyUnicode_READ`, :c:macro:`PyUnicode_READ_CHAR`, :c:macro:`PyUnicode_WRITE` * :c:macro:`PyUnicode_MAX_CHAR_VALUE` @@ -2269,58 +2269,58 @@ removed in Python 4. All functions using this type are deprecated: Unicode functions and methods using :c:type:`Py_UNICODE` and :c:expr:`Py_UNICODE*` types: -* :c:macro:`PyUnicode_FromUnicode`: use :c:func:`PyUnicode_FromWideChar` or +* :c:macro:`!PyUnicode_FromUnicode`: use :c:func:`PyUnicode_FromWideChar` or :c:func:`PyUnicode_FromKindAndData` -* :c:macro:`PyUnicode_AS_UNICODE`, :c:func:`PyUnicode_AsUnicode`, - :c:func:`PyUnicode_AsUnicodeAndSize`: use :c:func:`PyUnicode_AsWideCharString` -* :c:macro:`PyUnicode_AS_DATA`: use :c:macro:`PyUnicode_DATA` with +* :c:macro:`!PyUnicode_AS_UNICODE`, :c:func:`!PyUnicode_AsUnicode`, + :c:func:`!PyUnicode_AsUnicodeAndSize`: use :c:func:`PyUnicode_AsWideCharString` +* :c:macro:`!PyUnicode_AS_DATA`: use :c:macro:`PyUnicode_DATA` with :c:macro:`PyUnicode_READ` and :c:macro:`PyUnicode_WRITE` -* :c:macro:`PyUnicode_GET_SIZE`, :c:func:`PyUnicode_GetSize`: use +* :c:macro:`!PyUnicode_GET_SIZE`, :c:func:`!PyUnicode_GetSize`: use :c:macro:`PyUnicode_GET_LENGTH` or :c:func:`PyUnicode_GetLength` -* :c:macro:`PyUnicode_GET_DATA_SIZE`: use +* :c:macro:`!PyUnicode_GET_DATA_SIZE`: use ``PyUnicode_GET_LENGTH(str) * PyUnicode_KIND(str)`` (only work on ready strings) -* :c:func:`PyUnicode_AsUnicodeCopy`: use :c:func:`PyUnicode_AsUCS4Copy` or +* :c:func:`!PyUnicode_AsUnicodeCopy`: use :c:func:`PyUnicode_AsUCS4Copy` or :c:func:`PyUnicode_AsWideCharString` -* :c:func:`PyUnicode_GetMax` +* :c:func:`!PyUnicode_GetMax` Functions and macros manipulating Py_UNICODE* strings: -* :c:macro:`Py_UNICODE_strlen`: use :c:func:`PyUnicode_GetLength` or +* :c:macro:`!Py_UNICODE_strlen()`: use :c:func:`PyUnicode_GetLength` or :c:macro:`PyUnicode_GET_LENGTH` -* :c:macro:`Py_UNICODE_strcat`: use :c:func:`PyUnicode_CopyCharacters` or +* :c:macro:`!Py_UNICODE_strcat()`: use :c:func:`PyUnicode_CopyCharacters` or :c:func:`PyUnicode_FromFormat` -* :c:macro:`Py_UNICODE_strcpy`, :c:macro:`Py_UNICODE_strncpy`, - :c:macro:`Py_UNICODE_COPY`: use :c:func:`PyUnicode_CopyCharacters` or +* :c:macro:`!Py_UNICODE_strcpy()`, :c:macro:`!Py_UNICODE_strncpy()`, + :c:macro:`!Py_UNICODE_COPY()`: use :c:func:`PyUnicode_CopyCharacters` or :c:func:`PyUnicode_Substring` -* :c:macro:`Py_UNICODE_strcmp`: use :c:func:`PyUnicode_Compare` -* :c:macro:`Py_UNICODE_strncmp`: use :c:func:`PyUnicode_Tailmatch` -* :c:macro:`Py_UNICODE_strchr`, :c:macro:`Py_UNICODE_strrchr`: use +* :c:macro:`!Py_UNICODE_strcmp()`: use :c:func:`PyUnicode_Compare` +* :c:macro:`!Py_UNICODE_strncmp()`: use :c:func:`PyUnicode_Tailmatch` +* :c:macro:`!Py_UNICODE_strchr()`, :c:macro:`!Py_UNICODE_strrchr()`: use :c:func:`PyUnicode_FindChar` -* :c:macro:`Py_UNICODE_FILL`: use :c:func:`PyUnicode_Fill` -* :c:macro:`Py_UNICODE_MATCH` +* :c:macro:`!Py_UNICODE_FILL()`: use :c:func:`PyUnicode_Fill` +* :c:macro:`!Py_UNICODE_MATCH` Encoders: -* :c:func:`PyUnicode_Encode`: use :c:func:`PyUnicode_AsEncodedObject` -* :c:func:`PyUnicode_EncodeUTF7` -* :c:func:`PyUnicode_EncodeUTF8`: use :c:func:`PyUnicode_AsUTF8` or +* :c:func:`!PyUnicode_Encode`: use :c:func:`PyUnicode_AsEncodedObject` +* :c:func:`!PyUnicode_EncodeUTF7` +* :c:func:`!PyUnicode_EncodeUTF8`: use :c:func:`PyUnicode_AsUTF8` or :c:func:`PyUnicode_AsUTF8String` -* :c:func:`PyUnicode_EncodeUTF32` -* :c:func:`PyUnicode_EncodeUTF16` -* :c:func:`PyUnicode_EncodeUnicodeEscape` use +* :c:func:`!PyUnicode_EncodeUTF32` +* :c:func:`!PyUnicode_EncodeUTF16` +* :c:func:`!PyUnicode_EncodeUnicodeEscape` use :c:func:`PyUnicode_AsUnicodeEscapeString` -* :c:func:`PyUnicode_EncodeRawUnicodeEscape` use +* :c:func:`!PyUnicode_EncodeRawUnicodeEscape` use :c:func:`PyUnicode_AsRawUnicodeEscapeString` -* :c:func:`PyUnicode_EncodeLatin1`: use :c:func:`PyUnicode_AsLatin1String` -* :c:func:`PyUnicode_EncodeASCII`: use :c:func:`PyUnicode_AsASCIIString` -* :c:func:`PyUnicode_EncodeCharmap` -* :c:func:`PyUnicode_TranslateCharmap` -* :c:func:`PyUnicode_EncodeMBCS`: use :c:func:`PyUnicode_AsMBCSString` or +* :c:func:`!PyUnicode_EncodeLatin1`: use :c:func:`PyUnicode_AsLatin1String` +* :c:func:`!PyUnicode_EncodeASCII`: use :c:func:`PyUnicode_AsASCIIString` +* :c:func:`!PyUnicode_EncodeCharmap` +* :c:func:`!PyUnicode_TranslateCharmap` +* :c:func:`!PyUnicode_EncodeMBCS`: use :c:func:`PyUnicode_AsMBCSString` or :c:func:`PyUnicode_EncodeCodePage` (with ``CP_ACP`` code_page) -* :c:func:`PyUnicode_EncodeDecimal`, - :c:func:`PyUnicode_TransformDecimalToASCII` +* :c:func:`!PyUnicode_EncodeDecimal`, + :c:func:`!PyUnicode_TransformDecimalToASCII` Deprecated features diff --git a/Doc/whatsnew/3.6.rst b/Doc/whatsnew/3.6.rst index 1b7cef14d912e8..e6e813e49ceb8f 100644 --- a/Doc/whatsnew/3.6.rst +++ b/Doc/whatsnew/3.6.rst @@ -2066,9 +2066,9 @@ environment. (Contributed by Brett Cannon in :issue:`25154`.) Deprecated functions and types of the C API ------------------------------------------- -Undocumented functions :c:func:`PyUnicode_AsEncodedObject`, -:c:func:`PyUnicode_AsDecodedObject`, :c:func:`PyUnicode_AsEncodedUnicode` -and :c:func:`PyUnicode_AsDecodedUnicode` are deprecated now. +Undocumented functions :c:func:`!PyUnicode_AsEncodedObject`, +:c:func:`!PyUnicode_AsDecodedObject`, :c:func:`!PyUnicode_AsEncodedUnicode` +and :c:func:`!PyUnicode_AsDecodedUnicode` are deprecated now. Use the :ref:`generic codec based API ` instead. diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index 666d53ed6da7ba..664c8d8a545db0 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -1370,8 +1370,8 @@ Porting to Python 3.9 (Contributed by Victor Stinner in :issue:`40241`.) * The ``Py_UNICODE_COPY``, ``Py_UNICODE_FILL``, ``PyUnicode_WSTR_LENGTH``, - :c:func:`PyUnicode_FromUnicode`, :c:func:`PyUnicode_AsUnicode`, - ``_PyUnicode_AsUnicode``, and :c:func:`PyUnicode_AsUnicodeAndSize` are + :c:func:`!PyUnicode_FromUnicode`, :c:func:`!PyUnicode_AsUnicode`, + ``_PyUnicode_AsUnicode``, and :c:func:`!PyUnicode_AsUnicodeAndSize` are marked as deprecated in C. They have been deprecated by :pep:`393` since Python 3.3. (Contributed by Inada Naoki in :issue:`36346`.) diff --git a/Misc/NEWS.d/3.11.0b1.rst b/Misc/NEWS.d/3.11.0b1.rst index 6b601489a77285..1da722b21680ee 100644 --- a/Misc/NEWS.d/3.11.0b1.rst +++ b/Misc/NEWS.d/3.11.0b1.rst @@ -2068,9 +2068,9 @@ casts when the Python C API is used in C++. Patch by Victor Stinner. .. nonce: Cx-95G .. section: C API -Mark functions as deprecated by :pep:`623`: :c:func:`PyUnicode_AS_DATA`, -:c:func:`PyUnicode_AS_UNICODE`, :c:func:`PyUnicode_GET_DATA_SIZE`, -:c:func:`PyUnicode_GET_SIZE`. Patch by Victor Stinner. +Mark functions as deprecated by :pep:`623`: :c:func:`!PyUnicode_AS_DATA`, +:c:func:`!PyUnicode_AS_UNICODE`, :c:func:`!PyUnicode_GET_DATA_SIZE`, +:c:func:`!PyUnicode_GET_SIZE`. Patch by Victor Stinner. .. From 4d4871e715737bf699cf9773e275c7993ae13779 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 21 Aug 2023 14:21:26 -0700 Subject: [PATCH 180/632] [3.11] gh-108224: Fix asyncio doc inconsistency (GH-108230) (#108232) (Spawning subprocesses does not require the event loop to run in the main thread -- only signal handling does.) (cherry picked from commit 1cc391d9e2ea24ca750005335507b52933fc0b52) Co-authored-by: temach --- Doc/library/asyncio-dev.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/asyncio-dev.rst b/Doc/library/asyncio-dev.rst index c7d97008fb490e..a9c3a0183bb72d 100644 --- a/Doc/library/asyncio-dev.rst +++ b/Doc/library/asyncio-dev.rst @@ -99,7 +99,7 @@ To schedule a coroutine object from a different OS thread, the # Wait for the result: result = future.result() -To handle signals and to execute subprocesses, the event loop must be +To handle signals the event loop must be run in the main thread. The :meth:`loop.run_in_executor` method can be used with a From cc42182c978cf9e304c1b03f94486c0a0785a477 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Tue, 22 Aug 2023 10:19:56 +0200 Subject: [PATCH 181/632] [3.11] gh-107801: Improve the accuracy of io.TextIOWrapper.seek docs (#107933) (#108264) (cherry picked from commit 7f87ebbc3f52680c939791f397b9a478edf0c8d4) Clearly document the supported seek() operations: - Rewind to the start of the stream - Restore a previous stream position (given by tell()) - Fast-forward to the end of the stream --- Doc/library/io.rst | 16 ++++++++++++++++ Modules/_io/clinic/textio.c.h | 24 +++++++++++++++++++++--- Modules/_io/textio.c | 20 ++++++++++++++++++-- 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/Doc/library/io.rst b/Doc/library/io.rst index 66273d9ed1ff0a..792bf43d9811bb 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -1044,6 +1044,22 @@ Text I/O .. versionchanged:: 3.11 The method supports ``encoding="locale"`` option. + .. method:: seek(cookie, whence=os.SEEK_SET, /) + + Set the stream position. + Return the new stream position as an :class:`int`. + + Four operations are supported, + given by the following argument combinations: + + * ``seek(0, SEEK_SET)``: Rewind to the start of the stream. + * ``seek(cookie, SEEK_SET)``: Restore a previous position; + *cookie* **must be** a number returned by :meth:`!tell`. + * ``seek(0, SEEK_END)``: Fast-forward to the end of the stream. + * ``seek(0, SEEK_CUR)``: Leave the current stream position unchanged. + + Any other argument combinations are invalid, + and may raise exceptions. .. class:: StringIO(initial_value='', newline='\n') diff --git a/Modules/_io/clinic/textio.c.h b/Modules/_io/clinic/textio.c.h index 907785b2beaf9b..91755dc3ed568a 100644 --- a/Modules/_io/clinic/textio.c.h +++ b/Modules/_io/clinic/textio.c.h @@ -470,9 +470,27 @@ _io_TextIOWrapper_readline(textio *self, PyObject *const *args, Py_ssize_t nargs } PyDoc_STRVAR(_io_TextIOWrapper_seek__doc__, -"seek($self, cookie, whence=0, /)\n" +"seek($self, cookie, whence=os.SEEK_SET, /)\n" "--\n" -"\n"); +"\n" +"Set the stream position, and return the new stream position.\n" +"\n" +" cookie\n" +" Zero or an opaque number returned by tell().\n" +" whence\n" +" The relative position to seek from.\n" +"\n" +"Four operations are supported, given by the following argument\n" +"combinations:\n" +"\n" +"- seek(0, SEEK_SET): Rewind to the start of the stream.\n" +"- seek(cookie, SEEK_SET): Restore a previous position;\n" +" \'cookie\' must be a number returned by tell().\n" +"- seek(0, SEEK_END): Fast-forward to the end of the stream.\n" +"- seek(0, SEEK_CUR): Leave the current stream position unchanged.\n" +"\n" +"Any other argument combinations are invalid,\n" +"and may raise exceptions."); #define _IO_TEXTIOWRAPPER_SEEK_METHODDEF \ {"seek", _PyCFunction_CAST(_io_TextIOWrapper_seek), METH_FASTCALL, _io_TextIOWrapper_seek__doc__}, @@ -671,4 +689,4 @@ _io_TextIOWrapper_close(textio *self, PyObject *Py_UNUSED(ignored)) { return _io_TextIOWrapper_close_impl(self); } -/*[clinic end generated code: output=bb78b568b24759d6 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=f9bda53adf576a8e input=a9049054013a1b77]*/ diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 6cb5a6861a158c..403687dac03362 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -2415,13 +2415,29 @@ _textiowrapper_encoder_setstate(textio *self, cookie_type *cookie) /*[clinic input] _io.TextIOWrapper.seek cookie as cookieObj: object - whence: int = 0 + Zero or an opaque number returned by tell(). + whence: int(c_default='0') = os.SEEK_SET + The relative position to seek from. / + +Set the stream position, and return the new stream position. + +Four operations are supported, given by the following argument +combinations: + +- seek(0, SEEK_SET): Rewind to the start of the stream. +- seek(cookie, SEEK_SET): Restore a previous position; + 'cookie' must be a number returned by tell(). +- seek(0, SEEK_END): Fast-forward to the end of the stream. +- seek(0, SEEK_CUR): Leave the current stream position unchanged. + +Any other argument combinations are invalid, +and may raise exceptions. [clinic start generated code]*/ static PyObject * _io_TextIOWrapper_seek_impl(textio *self, PyObject *cookieObj, int whence) -/*[clinic end generated code: output=0a15679764e2d04d input=0458abeb3d7842be]*/ +/*[clinic end generated code: output=0a15679764e2d04d input=0f68adcb02cf2823]*/ { PyObject *posobj; cookie_type cookie; From dd0a1f9da283bd784e2c88efec0a45cef978516a Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 22 Aug 2023 01:49:35 -0700 Subject: [PATCH 182/632] [3.11] gh-102507 Remove invisible pagebreak characters (GH-102531) (#108266) gh-102507 Remove invisible pagebreak characters (GH-102531) (cherry picked from commit b097925858c6975c73e989226cf278cc382c0416) Co-authored-by: JosephSBoyle <48555120+JosephSBoyle@users.noreply.github.com> Co-authored-by: AlexWaygood --- Lib/email/__init__.py | 1 - Lib/email/base64mime.py | 4 ---- Lib/email/charset.py | 5 ----- Lib/email/encoders.py | 4 ---- Lib/email/feedparser.py | 2 -- Lib/email/generator.py | 5 +---- Lib/email/header.py | 5 ----- Lib/email/iterators.py | 3 --- Lib/email/mime/base.py | 1 - Lib/email/mime/message.py | 1 - Lib/email/mime/multipart.py | 1 - Lib/email/mime/nonmultipart.py | 1 - Lib/email/mime/text.py | 1 - Lib/email/parser.py | 3 +-- Modules/_io/bufferedio.c | 3 --- Tools/i18n/pygettext.py | 11 ++++------- 16 files changed, 6 insertions(+), 45 deletions(-) diff --git a/Lib/email/__init__.py b/Lib/email/__init__.py index fae872439edc66..9fa47783004185 100644 --- a/Lib/email/__init__.py +++ b/Lib/email/__init__.py @@ -25,7 +25,6 @@ ] - # Some convenience routines. Don't import Parser and Message as side-effects # of importing email since those cascadingly import most of the rest of the # email package. diff --git a/Lib/email/base64mime.py b/Lib/email/base64mime.py index a7cc37365c6f9a..4cdf22666e3016 100644 --- a/Lib/email/base64mime.py +++ b/Lib/email/base64mime.py @@ -45,7 +45,6 @@ MISC_LEN = 7 - # Helpers def header_length(bytearray): """Return the length of s when it is encoded with base64.""" @@ -57,7 +56,6 @@ def header_length(bytearray): return n - def header_encode(header_bytes, charset='iso-8859-1'): """Encode a single header line with Base64 encoding in a given charset. @@ -72,7 +70,6 @@ def header_encode(header_bytes, charset='iso-8859-1'): return '=?%s?b?%s?=' % (charset, encoded) - def body_encode(s, maxlinelen=76, eol=NL): r"""Encode a string with base64. @@ -98,7 +95,6 @@ def body_encode(s, maxlinelen=76, eol=NL): return EMPTYSTRING.join(encvec) - def decode(string): """Decode a raw base64 string, returning a bytes object. diff --git a/Lib/email/charset.py b/Lib/email/charset.py index d3d759ad9115f0..1d4db7cd227ee4 100644 --- a/Lib/email/charset.py +++ b/Lib/email/charset.py @@ -18,7 +18,6 @@ from email.encoders import encode_7or8bit - # Flags for types of header encodings QP = 1 # Quoted-Printable BASE64 = 2 # Base64 @@ -32,7 +31,6 @@ EMPTYSTRING = '' - # Defaults CHARSETS = { # input header enc body enc output conv @@ -104,7 +102,6 @@ } - # Convenience functions for extending the above mappings def add_charset(charset, header_enc=None, body_enc=None, output_charset=None): """Add character set properties to the global registry. @@ -153,7 +150,6 @@ def add_codec(charset, codecname): CODEC_MAP[charset] = codecname - # Convenience function for encoding strings, taking into account # that they might be unknown-8bit (ie: have surrogate-escaped bytes) def _encode(string, codec): @@ -163,7 +159,6 @@ def _encode(string, codec): return string.encode(codec) - class Charset: """Map character sets to their email properties. diff --git a/Lib/email/encoders.py b/Lib/email/encoders.py index 0a66acb6240bd7..17bd1ab7b19f32 100644 --- a/Lib/email/encoders.py +++ b/Lib/email/encoders.py @@ -16,7 +16,6 @@ from quopri import encodestring as _encodestring - def _qencode(s): enc = _encodestring(s, quotetabs=True) # Must encode spaces, which quopri.encodestring() doesn't do @@ -34,7 +33,6 @@ def encode_base64(msg): msg['Content-Transfer-Encoding'] = 'base64' - def encode_quopri(msg): """Encode the message's payload in quoted-printable. @@ -46,7 +44,6 @@ def encode_quopri(msg): msg['Content-Transfer-Encoding'] = 'quoted-printable' - def encode_7or8bit(msg): """Set the Content-Transfer-Encoding header to 7bit or 8bit.""" orig = msg.get_payload(decode=True) @@ -64,6 +61,5 @@ def encode_7or8bit(msg): msg['Content-Transfer-Encoding'] = '7bit' - def encode_noop(msg): """Do nothing.""" diff --git a/Lib/email/feedparser.py b/Lib/email/feedparser.py index e400dc7fb89b05..89c739183c1d00 100644 --- a/Lib/email/feedparser.py +++ b/Lib/email/feedparser.py @@ -41,7 +41,6 @@ NeedMoreData = object() - class BufferedSubFile(object): """A file-ish object that can have new data loaded into it. @@ -132,7 +131,6 @@ def __next__(self): return line - class FeedParser: """A feed-style parser of email.""" diff --git a/Lib/email/generator.py b/Lib/email/generator.py index c9b121624e08d5..b8c10917a5d98c 100644 --- a/Lib/email/generator.py +++ b/Lib/email/generator.py @@ -22,7 +22,6 @@ fcre = re.compile(r'^From ', re.MULTILINE) - class Generator: """Generates output from a Message object tree. @@ -392,7 +391,7 @@ def _make_boundary(cls, text=None): def _compile_re(cls, s, flags): return re.compile(s, flags) - + class BytesGenerator(Generator): """Generates a bytes version of a Message object tree. @@ -443,7 +442,6 @@ def _compile_re(cls, s, flags): return re.compile(s.encode('ascii'), flags) - _FMT = '[Non-text (%(type)s) part of message omitted, filename %(filename)s]' class DecodedGenerator(Generator): @@ -503,7 +501,6 @@ def _dispatch(self, msg): }, file=self) - # Helper used by Generator._make_boundary _width = len(repr(sys.maxsize-1)) _fmt = '%%0%dd' % _width diff --git a/Lib/email/header.py b/Lib/email/header.py index 4ab0032bc66123..984851a7d9a679 100644 --- a/Lib/email/header.py +++ b/Lib/email/header.py @@ -52,12 +52,10 @@ _embedded_header = re.compile(r'\n[^ \t]+:') - # Helpers _max_append = email.quoprimime._max_append - def decode_header(header): """Decode a message header value without converting charset. @@ -152,7 +150,6 @@ def decode_header(header): return collapsed - def make_header(decoded_seq, maxlinelen=None, header_name=None, continuation_ws=' '): """Create a Header from a sequence of pairs as returned by decode_header() @@ -175,7 +172,6 @@ def make_header(decoded_seq, maxlinelen=None, header_name=None, return h - class Header: def __init__(self, s=None, charset=None, maxlinelen=None, header_name=None, @@ -409,7 +405,6 @@ def _normalize(self): self._chunks = chunks - class _ValueFormatter: def __init__(self, headerlen, maxlen, continuation_ws, splitchars): self._maxlen = maxlen diff --git a/Lib/email/iterators.py b/Lib/email/iterators.py index b5502ee975266b..3410935e38f476 100644 --- a/Lib/email/iterators.py +++ b/Lib/email/iterators.py @@ -15,7 +15,6 @@ from io import StringIO - # This function will become a method of the Message class def walk(self): """Walk over the message tree, yielding each subpart. @@ -29,7 +28,6 @@ def walk(self): yield from subpart.walk() - # These two functions are imported into the Iterators.py interface module. def body_line_iterator(msg, decode=False): """Iterate over the parts, returning string payloads line-by-line. @@ -55,7 +53,6 @@ def typed_subpart_iterator(msg, maintype='text', subtype=None): yield subpart - def _structure(msg, fp=None, level=0, include_default=False): """A handy debugging aid""" if fp is None: diff --git a/Lib/email/mime/base.py b/Lib/email/mime/base.py index 1a3f9b51f6c045..f601f621cec393 100644 --- a/Lib/email/mime/base.py +++ b/Lib/email/mime/base.py @@ -11,7 +11,6 @@ from email import message - class MIMEBase(message.Message): """Base class for MIME specializations.""" diff --git a/Lib/email/mime/message.py b/Lib/email/mime/message.py index 07e4f2d1196151..61836b5a7861fc 100644 --- a/Lib/email/mime/message.py +++ b/Lib/email/mime/message.py @@ -10,7 +10,6 @@ from email.mime.nonmultipart import MIMENonMultipart - class MIMEMessage(MIMENonMultipart): """Class representing message/* MIME documents.""" diff --git a/Lib/email/mime/multipart.py b/Lib/email/mime/multipart.py index 2d3f288810dd91..94d81c771a474e 100644 --- a/Lib/email/mime/multipart.py +++ b/Lib/email/mime/multipart.py @@ -9,7 +9,6 @@ from email.mime.base import MIMEBase - class MIMEMultipart(MIMEBase): """Base class for MIME multipart/* type messages.""" diff --git a/Lib/email/mime/nonmultipart.py b/Lib/email/mime/nonmultipart.py index e1f51968b59eb1..a41386eb148c0c 100644 --- a/Lib/email/mime/nonmultipart.py +++ b/Lib/email/mime/nonmultipart.py @@ -10,7 +10,6 @@ from email.mime.base import MIMEBase - class MIMENonMultipart(MIMEBase): """Base class for MIME non-multipart type messages.""" diff --git a/Lib/email/mime/text.py b/Lib/email/mime/text.py index 35b442383002b2..dfe53c426b2ac4 100644 --- a/Lib/email/mime/text.py +++ b/Lib/email/mime/text.py @@ -10,7 +10,6 @@ from email.mime.nonmultipart import MIMENonMultipart - class MIMEText(MIMENonMultipart): """Class for generating text/* type MIME documents.""" diff --git a/Lib/email/parser.py b/Lib/email/parser.py index 7db4da1ff081c1..b1ca08d8f6cb2a 100644 --- a/Lib/email/parser.py +++ b/Lib/email/parser.py @@ -67,7 +67,6 @@ def parsestr(self, text, headersonly=False): return self.parse(StringIO(text), headersonly=headersonly) - class HeaderParser(Parser): def parse(self, fp, headersonly=True): return Parser.parse(self, fp, True) @@ -75,7 +74,7 @@ def parse(self, fp, headersonly=True): def parsestr(self, text, headersonly=True): return Parser.parsestr(self, text, True) - + class BytesParser: def __init__(self, *args, **kw): diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index 4a4a1992dbbb7a..fd1e6389906581 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -1740,7 +1740,6 @@ _bufferedreader_peek_unlocked(buffered *self) self->pos = 0; return PyBytes_FromStringAndSize(self->buffer, r); } - /* @@ -2047,7 +2046,6 @@ _io_BufferedWriter_write_impl(buffered *self, Py_buffer *buffer) LEAVE_BUFFERED(self) return res; } - /* @@ -2253,7 +2251,6 @@ bufferedrwpair_closed_get(rwpair *self, void *context) } return PyObject_GetAttr((PyObject *) self->writer, &_Py_ID(closed)); } - /* diff --git a/Tools/i18n/pygettext.py b/Tools/i18n/pygettext.py index 7ada79105db1ca..3a0b27ba420e7a 100755 --- a/Tools/i18n/pygettext.py +++ b/Tools/i18n/pygettext.py @@ -174,7 +174,6 @@ EMPTYSTRING = '' - # The normal pot-file header. msgmerge and Emacs's po-mode work better if it's # there. pot_header = _('''\ @@ -196,7 +195,7 @@ ''') - + def usage(code, msg=''): print(__doc__ % globals(), file=sys.stderr) if msg: @@ -204,7 +203,6 @@ def usage(code, msg=''): sys.exit(code) - def make_escapes(pass_nonascii): global escapes, escape if pass_nonascii: @@ -258,7 +256,7 @@ def normalize(s, encoding): s = '""\n"' + lineterm.join(lines) + '"' return s - + def containsAny(str, set): """Check whether 'str' contains ANY of the chars in 'set'""" return 1 in [c in str for c in set] @@ -307,7 +305,7 @@ def getFilesForName(name): return [] - + class TokenEater: def __init__(self, options): self.__options = options @@ -515,7 +513,6 @@ def write(self, fp): print('msgstr ""\n', file=fp) - def main(): global default_keywords try: @@ -675,7 +672,7 @@ class Options: if closep: fp.close() - + if __name__ == '__main__': main() # some more test strings From 8e837373edc7607d404f66df735da4e97e2bc4c5 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 22 Aug 2023 01:51:58 -0700 Subject: [PATCH 183/632] [3.11] gh-107845: Fix symlink handling for tarfile.data_filter (GH-107846) (GH-108209) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gh-107845: Fix symlink handling for tarfile.data_filter (GH-107846) (cherry picked from commit acbd3f9c5c5f23e95267714e41236140d84fe962) Co-authored-by: Petr Viktorin Co-authored-by: Victor Stinner Co-authored-by: Lumír 'Frenzy' Balhar --- Doc/library/tarfile.rst | 5 + Lib/tarfile.py | 11 +- Lib/test/test_tarfile.py | 146 +++++++++++++++++- ...-08-10-17-36-22.gh-issue-107845.dABiMJ.rst | 3 + 4 files changed, 156 insertions(+), 9 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-08-10-17-36-22.gh-issue-107845.dABiMJ.rst diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst index b7b089a73e6483..61a450cf88b0ac 100644 --- a/Doc/library/tarfile.rst +++ b/Doc/library/tarfile.rst @@ -732,6 +732,11 @@ A ``TarInfo`` object has the following public data attributes: Name of the target file name, which is only present in :class:`TarInfo` objects of type :const:`LNKTYPE` and :const:`SYMTYPE`. + For symbolic links (``SYMTYPE``), the *linkname* is relative to the directory + that contains the link. + For hard links (``LNKTYPE``), the *linkname* is relative to the root of + the archive. + .. attribute:: TarInfo.uid :type: int diff --git a/Lib/tarfile.py b/Lib/tarfile.py index b7adff6e1723bc..2808e7efc553a9 100755 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -741,7 +741,7 @@ def __init__(self, tarinfo): class AbsoluteLinkError(FilterError): def __init__(self, tarinfo): self.tarinfo = tarinfo - super().__init__(f'{tarinfo.name!r} is a symlink to an absolute path') + super().__init__(f'{tarinfo.name!r} is a link to an absolute path') class LinkOutsideDestinationError(FilterError): def __init__(self, tarinfo, path): @@ -801,7 +801,14 @@ def _get_filtered_attrs(member, dest_path, for_data=True): if member.islnk() or member.issym(): if os.path.isabs(member.linkname): raise AbsoluteLinkError(member) - target_path = os.path.realpath(os.path.join(dest_path, member.linkname)) + if member.issym(): + target_path = os.path.join(dest_path, + os.path.dirname(name), + member.linkname) + else: + target_path = os.path.join(dest_path, + member.linkname) + target_path = os.path.realpath(target_path) if os.path.commonpath([target_path, dest_path]) != dest_path: raise LinkOutsideDestinationError(member, target_path) return new_attrs diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index dc7ff852363cf5..13a75f39f9ba52 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -3256,10 +3256,12 @@ def __exit__(self, *exc): self.bio = None def add(self, name, *, type=None, symlink_to=None, hardlink_to=None, - mode=None, **kwargs): + mode=None, size=None, **kwargs): """Add a member to the test archive. Call within `with`.""" name = str(name) tarinfo = tarfile.TarInfo(name).replace(**kwargs) + if size is not None: + tarinfo.size = size if mode: tarinfo.mode = _filemode_to_int(mode) if symlink_to is not None: @@ -3335,7 +3337,8 @@ def check_context(self, tar, filter): raise self.raised_exception self.assertEqual(self.expected_paths, set()) - def expect_file(self, name, type=None, symlink_to=None, mode=None): + def expect_file(self, name, type=None, symlink_to=None, mode=None, + size=None): """Check a single file. See check_context.""" if self.raised_exception: raise self.raised_exception @@ -3364,6 +3367,8 @@ def expect_file(self, name, type=None, symlink_to=None, mode=None): self.assertTrue(path.is_fifo()) else: raise NotImplementedError(type) + if size is not None: + self.assertEqual(path.stat().st_size, size) for parent in path.parents: self.expected_paths.discard(parent) @@ -3410,8 +3415,15 @@ def test_parent_symlink(self): # Test interplaying symlinks # Inspired by 'dirsymlink2a' in jwilk/traversal-archives with ArchiveMaker() as arc: + + # `current` links to `.` which is both: + # - the destination directory + # - `current` itself arc.add('current', symlink_to='.') + + # effectively points to ./../ arc.add('parent', symlink_to='current/..') + arc.add('parent/evil') if os_helper.can_symlink(): @@ -3453,9 +3465,46 @@ def test_parent_symlink(self): def test_parent_symlink2(self): # Test interplaying symlinks # Inspired by 'dirsymlink2b' in jwilk/traversal-archives + + # Posix and Windows have different pathname resolution: + # either symlink or a '..' component resolve first. + # Let's see which we are on. + if os_helper.can_symlink(): + testpath = os.path.join(TEMPDIR, 'resolution_test') + os.mkdir(testpath) + + # testpath/current links to `.` which is all of: + # - `testpath` + # - `testpath/current` + # - `testpath/current/current` + # - etc. + os.symlink('.', os.path.join(testpath, 'current')) + + # we'll test where `testpath/current/../file` ends up + with open(os.path.join(testpath, 'current', '..', 'file'), 'w'): + pass + + if os.path.exists(os.path.join(testpath, 'file')): + # Windows collapses 'current\..' to '.' first, leaving + # 'testpath\file' + dotdot_resolves_early = True + elif os.path.exists(os.path.join(testpath, '..', 'file')): + # Posix resolves 'current' to '.' first, leaving + # 'testpath/../file' + dotdot_resolves_early = False + else: + raise AssertionError('Could not determine link resolution') + with ArchiveMaker() as arc: + + # `current` links to `.` which is both the destination directory + # and `current` itself arc.add('current', symlink_to='.') + + # `current/parent` is also available as `./parent`, + # and effectively points to `./../` arc.add('current/parent', symlink_to='..') + arc.add('parent/evil') with self.check_context(arc.open(), 'fully_trusted'): @@ -3469,6 +3518,7 @@ def test_parent_symlink2(self): with self.check_context(arc.open(), 'tar'): if os_helper.can_symlink(): + # Fail when extracting a file outside destination self.expect_exception( tarfile.OutsideDestinationError, "'parent/evil' would be extracted to " @@ -3479,10 +3529,24 @@ def test_parent_symlink2(self): self.expect_file('parent/evil') with self.check_context(arc.open(), 'data'): - self.expect_exception( - tarfile.LinkOutsideDestinationError, - """'current/parent' would link to ['"].*['"], """ - + "which is outside the destination") + if os_helper.can_symlink(): + if dotdot_resolves_early: + # Fail when extracting a file outside destination + self.expect_exception( + tarfile.OutsideDestinationError, + "'parent/evil' would be extracted to " + + """['"].*evil['"], which is outside """ + + "the destination") + else: + # Fail as soon as we have a symlink outside the destination + self.expect_exception( + tarfile.LinkOutsideDestinationError, + "'current/parent' would link to " + + """['"].*outerdir['"], which is outside """ + + "the destination") + else: + self.expect_file('current/') + self.expect_file('parent/evil') @symlink_test def test_absolute_symlink(self): @@ -3512,12 +3576,30 @@ def test_absolute_symlink(self): with self.check_context(arc.open(), 'data'): self.expect_exception( tarfile.AbsoluteLinkError, - "'parent' is a symlink to an absolute path") + "'parent' is a link to an absolute path") + + def test_absolute_hardlink(self): + # Test hardlink to an absolute path + # Inspired by 'dirsymlink' in https://github.com/jwilk/traversal-archives + with ArchiveMaker() as arc: + arc.add('parent', hardlink_to=self.outerdir / 'foo') + + with self.check_context(arc.open(), 'fully_trusted'): + self.expect_exception(KeyError, ".*foo. not found") + + with self.check_context(arc.open(), 'tar'): + self.expect_exception(KeyError, ".*foo. not found") + + with self.check_context(arc.open(), 'data'): + self.expect_exception( + tarfile.AbsoluteLinkError, + "'parent' is a link to an absolute path") @symlink_test def test_sly_relative0(self): # Inspired by 'relative0' in jwilk/traversal-archives with ArchiveMaker() as arc: + # points to `../../tmp/moo` arc.add('../moo', symlink_to='..//tmp/moo') try: @@ -3568,6 +3650,56 @@ def test_sly_relative2(self): + """['"].*moo['"], which is outside the """ + "destination") + @symlink_test + def test_deep_symlink(self): + # Test that symlinks and hardlinks inside a directory + # point to the correct file (`target` of size 3). + # If links aren't supported we get a copy of the file. + with ArchiveMaker() as arc: + arc.add('targetdir/target', size=3) + # a hardlink's linkname is relative to the archive + arc.add('linkdir/hardlink', hardlink_to=os.path.join( + 'targetdir', 'target')) + # a symlink's linkname is relative to the link's directory + arc.add('linkdir/symlink', symlink_to=os.path.join( + '..', 'targetdir', 'target')) + + for filter in 'tar', 'data', 'fully_trusted': + with self.check_context(arc.open(), filter): + self.expect_file('targetdir/target', size=3) + self.expect_file('linkdir/hardlink', size=3) + if os_helper.can_symlink(): + self.expect_file('linkdir/symlink', size=3, + symlink_to='../targetdir/target') + else: + self.expect_file('linkdir/symlink', size=3) + + @symlink_test + def test_chains(self): + # Test chaining of symlinks/hardlinks. + # Symlinks are created before the files they point to. + with ArchiveMaker() as arc: + arc.add('linkdir/symlink', symlink_to='hardlink') + arc.add('symlink2', symlink_to=os.path.join( + 'linkdir', 'hardlink2')) + arc.add('targetdir/target', size=3) + arc.add('linkdir/hardlink', hardlink_to='targetdir/target') + arc.add('linkdir/hardlink2', hardlink_to='linkdir/symlink') + + for filter in 'tar', 'data', 'fully_trusted': + with self.check_context(arc.open(), filter): + self.expect_file('targetdir/target', size=3) + self.expect_file('linkdir/hardlink', size=3) + self.expect_file('linkdir/hardlink2', size=3) + if os_helper.can_symlink(): + self.expect_file('linkdir/symlink', size=3, + symlink_to='hardlink') + self.expect_file('symlink2', size=3, + symlink_to='linkdir/hardlink2') + else: + self.expect_file('linkdir/symlink', size=3) + self.expect_file('symlink2', size=3) + def test_modes(self): # Test how file modes are extracted # (Note that the modes are ignored on platforms without working chmod) diff --git a/Misc/NEWS.d/next/Library/2023-08-10-17-36-22.gh-issue-107845.dABiMJ.rst b/Misc/NEWS.d/next/Library/2023-08-10-17-36-22.gh-issue-107845.dABiMJ.rst new file mode 100644 index 00000000000000..32c1fb93f4ab2c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-08-10-17-36-22.gh-issue-107845.dABiMJ.rst @@ -0,0 +1,3 @@ +:func:`tarfile.data_filter` now takes the location of symlinks into account +when determining their target, so it will no longer reject some valid +tarballs with ``LinkOutsideDestinationError``. From d678ee771931a391212ef41e61e9717b43867e35 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 22 Aug 2023 12:57:31 +0300 Subject: [PATCH 184/632] [3.11] Trim trailing whitespace and test on CI (GH-104275) (#108215) --- .github/CODEOWNERS | 5 +- .github/workflows/lint.yml | 22 +++ .pre-commit-config.yaml | 7 + Lib/test/test_asyncio/test_runners.py | 2 +- Lib/test/test_isinstance.py | 8 +- Modules/_blake2/blake2module.h | 2 +- Modules/_blake2/impl/blake2b-round.h | 4 +- Modules/_blake2/impl/blake2s-load-xop.h | 2 +- Modules/_blake2/impl/blake2s-round.h | 2 +- Modules/_ctypes/darwin/dlfcn.h | 2 +- Modules/_ctypes/libffi_osx/ffi.c | 12 +- Modules/_ctypes/libffi_osx/include/ffi.h | 28 ++-- .../_ctypes/libffi_osx/include/ffi_common.h | 4 +- .../_ctypes/libffi_osx/include/fficonfig.h | 6 +- .../libffi_osx/include/x86-ffitarget.h | 2 +- .../libffi_osx/powerpc/ppc-ffi_darwin.c | 6 +- Modules/_ctypes/libffi_osx/types.c | 4 +- Modules/_ctypes/libffi_osx/x86/x86-ffi64.c | 6 +- .../_ctypes/libffi_osx/x86/x86-ffi_darwin.c | 130 +++++++++--------- Modules/_io/_iomodule.c | 4 +- Modules/termios.c | 4 +- PC/winreg.c | 4 +- Tools/msi/bundle/bootstrap/pch.h | 2 +- Tools/msi/bundle/bootstrap/resource.h | 2 +- 24 files changed, 150 insertions(+), 120 deletions(-) create mode 100644 .github/workflows/lint.yml create mode 100644 .pre-commit-config.yaml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 3d39e0c4ef0417..99d701daa1d49a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -5,7 +5,10 @@ # https://git-scm.com/docs/gitignore#_pattern_format # GitHub -.github/** @ezio-melotti +.github/** @ezio-melotti @hugovk + +# pre-commit +.pre-commit-config.yaml @hugovk @AlexWaygood # asyncio **/*asyncio* @1st1 @asvetlov diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000000000..4481ea80bfd936 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,22 @@ +name: Lint + +on: [push, pull_request, workflow_dispatch] + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + lint: + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: "3.x" + - uses: pre-commit/action@v3.0.0 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000000000..808622f19a3dbf --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,7 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: check-yaml + - id: trailing-whitespace + types_or: [c, python, rst] diff --git a/Lib/test/test_asyncio/test_runners.py b/Lib/test/test_asyncio/test_runners.py index 8a4aba6d470bac..ae823cc7bec33e 100644 --- a/Lib/test/test_asyncio/test_runners.py +++ b/Lib/test/test_asyncio/test_runners.py @@ -441,7 +441,7 @@ async def coro(): with asyncio.Runner() as runner: with self.assertRaises(asyncio.CancelledError): runner.run(coro()) - + def test_signal_install_not_supported_ok(self): # signal.signal() can throw if the "main thread" doensn't have signals enabled assert threading.current_thread() is threading.main_thread() diff --git a/Lib/test/test_isinstance.py b/Lib/test/test_isinstance.py index a0974640bc1146..a8315a4a9123d9 100644 --- a/Lib/test/test_isinstance.py +++ b/Lib/test/test_isinstance.py @@ -8,7 +8,6 @@ from test import support - class TestIsInstanceExceptions(unittest.TestCase): # Test to make sure that an AttributeError when accessing the instance's # class's bases is masked. This was actually a bug in Python 2.2 and @@ -97,7 +96,7 @@ def getclass(self): class D: pass self.assertRaises(RuntimeError, isinstance, c, D) - + # These tests are similar to above, but tickle certain code paths in # issubclass() instead of isinstance() -- really PyObject_IsSubclass() # vs. PyObject_IsInstance(). @@ -147,7 +146,6 @@ def getbases(self): self.assertRaises(TypeError, issubclass, B, C()) - # meta classes for creating abstract classes and instances class AbstractClass(object): def __init__(self, bases): @@ -179,7 +177,7 @@ class Super: class Child(Super): pass - + class TestIsInstanceIsSubclass(unittest.TestCase): # Tests to ensure that isinstance and issubclass work on abstract # classes and instances. Before the 2.2 release, TypeErrors were @@ -357,6 +355,6 @@ def blowstack(fxn, arg, compare_to): tuple_arg = (tuple_arg,) fxn(arg, tuple_arg) - + if __name__ == '__main__': unittest.main() diff --git a/Modules/_blake2/blake2module.h b/Modules/_blake2/blake2module.h index aa8f281178eadc..c8144ec9d48d29 100644 --- a/Modules/_blake2/blake2module.h +++ b/Modules/_blake2/blake2module.h @@ -38,6 +38,6 @@ #endif // HAVE_LIBB2 // for secure_zero_memory(), store32(), store48(), and store64() -#include "impl/blake2-impl.h" +#include "impl/blake2-impl.h" #endif // Py_BLAKE2MODULE_H diff --git a/Modules/_blake2/impl/blake2b-round.h b/Modules/_blake2/impl/blake2b-round.h index cebc22550da4cd..5b452c4d63babe 100644 --- a/Modules/_blake2/impl/blake2b-round.h +++ b/Modules/_blake2/impl/blake2b-round.h @@ -62,7 +62,7 @@ \ row2l = _mm_roti_epi64(row2l, -24); \ row2h = _mm_roti_epi64(row2h, -24); \ - + #define G2(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h,b0,b1) \ row1l = _mm_add_epi64(_mm_add_epi64(row1l, b0), row2l); \ row1h = _mm_add_epi64(_mm_add_epi64(row1h, b1), row2h); \ @@ -81,7 +81,7 @@ \ row2l = _mm_roti_epi64(row2l, -63); \ row2h = _mm_roti_epi64(row2h, -63); \ - + #if defined(HAVE_SSSE3) #define DIAGONALIZE(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h) \ t0 = _mm_alignr_epi8(row2h, row2l, 8); \ diff --git a/Modules/_blake2/impl/blake2s-load-xop.h b/Modules/_blake2/impl/blake2s-load-xop.h index ac591a77d191a7..14d9e7f7640672 100644 --- a/Modules/_blake2/impl/blake2s-load-xop.h +++ b/Modules/_blake2/impl/blake2s-load-xop.h @@ -166,7 +166,7 @@ buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(1),TOB(7)) ); #define LOAD_MSG_8_3(buf) \ t0 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(6),TOB(1),TOB(0),TOB(0)) ); \ buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(5),TOB(4)) ); \ - + #define LOAD_MSG_8_4(buf) \ buf = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(5),TOB(4),TOB(7),TOB(2)) ); diff --git a/Modules/_blake2/impl/blake2s-round.h b/Modules/_blake2/impl/blake2s-round.h index 1e2f2b7f59bd6c..3af4be35bee5d4 100644 --- a/Modules/_blake2/impl/blake2s-round.h +++ b/Modules/_blake2/impl/blake2s-round.h @@ -86,6 +86,6 @@ LOAD_MSG_ ##r ##_4(buf4); \ G2(row1,row2,row3,row4,buf4); \ UNDIAGONALIZE(row1,row2,row3,row4); \ - + #endif diff --git a/Modules/_ctypes/darwin/dlfcn.h b/Modules/_ctypes/darwin/dlfcn.h index a2afc3eeb84794..a9915c3115ceb6 100644 --- a/Modules/_ctypes/darwin/dlfcn.h +++ b/Modules/_ctypes/darwin/dlfcn.h @@ -1,7 +1,7 @@ /* Copyright (c) 2002 Jorge Acereda & Peter O'Gorman - + Portions may be copyright others, see the AUTHORS file included with this distribution. diff --git a/Modules/_ctypes/libffi_osx/ffi.c b/Modules/_ctypes/libffi_osx/ffi.c index 1776b795e2f83d..e16423aef19fc0 100644 --- a/Modules/_ctypes/libffi_osx/ffi.c +++ b/Modules/_ctypes/libffi_osx/ffi.c @@ -65,12 +65,12 @@ initialize_aggregate( arg->size = ALIGN(arg->size, curalign); arg->size += (*ptr)->size; - arg->alignment = (arg->alignment > curalign) ? + arg->alignment = (arg->alignment > curalign) ? arg->alignment : curalign; #else arg->size = ALIGN(arg->size, (*ptr)->alignment); arg->size += (*ptr)->size; - arg->alignment = (arg->alignment > (*ptr)->alignment) ? + arg->alignment = (arg->alignment > (*ptr)->alignment) ? arg->alignment : (*ptr)->alignment; #endif @@ -130,10 +130,10 @@ struct_on_stack( // Arguments' ffi_type->alignment must be nonzero. ffi_status ffi_prep_cif( -/*@out@*/ /*@partial@*/ ffi_cif* cif, +/*@out@*/ /*@partial@*/ ffi_cif* cif, ffi_abi abi, - unsigned int nargs, -/*@dependent@*/ /*@out@*/ /*@partial@*/ ffi_type* rtype, + unsigned int nargs, +/*@dependent@*/ /*@out@*/ /*@partial@*/ ffi_type* rtype, /*@dependent@*/ ffi_type** atypes) { unsigned int bytes = 0; @@ -184,7 +184,7 @@ ffi_prep_cif( if ((*ptr)->alignment == 0) return FFI_BAD_TYPEDEF; - /* Perform a sanity check on the argument type, do this + /* Perform a sanity check on the argument type, do this check after the initialization. */ FFI_ASSERT_VALID_TYPE(*ptr); diff --git a/Modules/_ctypes/libffi_osx/include/ffi.h b/Modules/_ctypes/libffi_osx/include/ffi.h index c104a5c89350b6..88c58fc43deb6b 100644 --- a/Modules/_ctypes/libffi_osx/include/ffi.h +++ b/Modules/_ctypes/libffi_osx/include/ffi.h @@ -199,9 +199,9 @@ typedef union { void ffi_raw_call( -/*@dependent@*/ ffi_cif* cif, - void (*fn)(void), -/*@out@*/ void* rvalue, +/*@dependent@*/ ffi_cif* cif, + void (*fn)(void), +/*@out@*/ void* rvalue, /*@dependent@*/ ffi_raw* avalue); void @@ -225,9 +225,9 @@ ffi_raw_size( longs and doubles are followed by an empty 64-bit word. */ void ffi_java_raw_call( -/*@dependent@*/ ffi_cif* cif, - void (*fn)(void), -/*@out@*/ void* rvalue, +/*@dependent@*/ ffi_cif* cif, + void (*fn)(void), +/*@out@*/ void* rvalue, /*@dependent@*/ ffi_raw* avalue); void @@ -272,8 +272,8 @@ typedef struct ffi_raw_closure { ffi_cif* cif; #if !FFI_NATIVE_RAW_API - /* if this is enabled, then a raw closure has the same layout - as a regular closure. We use this to install an intermediate + /* if this is enabled, then a raw closure has the same layout + as a regular closure. We use this to install an intermediate handler to do the transaltion, void** -> ffi_raw*. */ void (*translate_args)(ffi_cif*,void*,void**,void*); void* this_closure; @@ -303,17 +303,17 @@ ffi_prep_java_raw_closure( ffi_status ffi_prep_cif( -/*@out@*/ /*@partial@*/ ffi_cif* cif, +/*@out@*/ /*@partial@*/ ffi_cif* cif, ffi_abi abi, - unsigned int nargs, -/*@dependent@*/ /*@out@*/ /*@partial@*/ ffi_type* rtype, + unsigned int nargs, +/*@dependent@*/ /*@out@*/ /*@partial@*/ ffi_type* rtype, /*@dependent@*/ ffi_type** atypes); void ffi_call( -/*@dependent@*/ ffi_cif* cif, - void (*fn)(void), -/*@out@*/ void* rvalue, +/*@dependent@*/ ffi_cif* cif, + void (*fn)(void), +/*@out@*/ void* rvalue, /*@dependent@*/ void** avalue); /* Useful for eliminating compiler warnings */ diff --git a/Modules/_ctypes/libffi_osx/include/ffi_common.h b/Modules/_ctypes/libffi_osx/include/ffi_common.h index 685a3580f4fe05..02b53c8600e012 100644 --- a/Modules/_ctypes/libffi_osx/include/ffi_common.h +++ b/Modules/_ctypes/libffi_osx/include/ffi_common.h @@ -41,7 +41,7 @@ char* alloca(); # endif #endif -/*#if defined(FFI_DEBUG) +/*#if defined(FFI_DEBUG) #include #endif*/ @@ -65,7 +65,7 @@ ffi_type_test( # define FFI_ASSERT_AT(x, f, l) ((x) ? 0 : ffi_assert(#x, (f), (l))) # define FFI_ASSERT_VALID_TYPE(x) ffi_type_test(x, __FILE__, __LINE__) #else -# define FFI_ASSERT(x) +# define FFI_ASSERT(x) # define FFI_ASSERT_AT(x, f, l) # define FFI_ASSERT_VALID_TYPE(x) #endif // #ifdef FFI_DEBUG diff --git a/Modules/_ctypes/libffi_osx/include/fficonfig.h b/Modules/_ctypes/libffi_osx/include/fficonfig.h index 217249071dcf48..dc0f4ecb26ee0b 100644 --- a/Modules/_ctypes/libffi_osx/include/fficonfig.h +++ b/Modules/_ctypes/libffi_osx/include/fficonfig.h @@ -1,4 +1,4 @@ -/* Manually created fficonfig.h for Darwin on PowerPC or Intel +/* Manually created fficonfig.h for Darwin on PowerPC or Intel This file is manually generated to do away with the need for autoconf and therefore make it easier to cross-compile and build fat binaries. @@ -33,10 +33,10 @@ # define SIZEOF_DOUBLE 8 # if __GNUC__ >= 4 # define HAVE_LONG_DOUBLE 1 -# define SIZEOF_LONG_DOUBLE 16 +# define SIZEOF_LONG_DOUBLE 16 # else # undef HAVE_LONG_DOUBLE -# define SIZEOF_LONG_DOUBLE 8 +# define SIZEOF_LONG_DOUBLE 8 # endif #elif defined(__ppc64__) diff --git a/Modules/_ctypes/libffi_osx/include/x86-ffitarget.h b/Modules/_ctypes/libffi_osx/include/x86-ffitarget.h index 55c2b6c50cd90c..df149eb14d78c7 100644 --- a/Modules/_ctypes/libffi_osx/include/x86-ffitarget.h +++ b/Modules/_ctypes/libffi_osx/include/x86-ffitarget.h @@ -33,7 +33,7 @@ # define X86 #endif -#if defined(__x86_64__) +#if defined(__x86_64__) # ifndef X86_64 # define X86_64 # endif diff --git a/Modules/_ctypes/libffi_osx/powerpc/ppc-ffi_darwin.c b/Modules/_ctypes/libffi_osx/powerpc/ppc-ffi_darwin.c index 8953d5fda35818..875412a6766957 100644 --- a/Modules/_ctypes/libffi_osx/powerpc/ppc-ffi_darwin.c +++ b/Modules/_ctypes/libffi_osx/powerpc/ppc-ffi_darwin.c @@ -891,7 +891,7 @@ ffi_closure_helper_DARWIN( avalue[i] = alloca(arg_types[i]->size); ffi64_struct_to_ram_form(arg_types[i], (const char*)pgr, &gprSize, (const char*)pfr, &fprSize, &nf, avalue[i], NULL); - + ng += gprSize / sizeof(long); pgr += gprSize / sizeof(long); pfr += (fprSize - savedFPRSize) / sizeof(double); @@ -1479,7 +1479,7 @@ ffi64_struct_to_reg_form( memcpy(&outGPRs[destGMarker], &inStruct[srcMarker], inType->size); } - + srcMarker += inType->size; destGMarker += inType->size; i += inType->size - 1; @@ -1561,7 +1561,7 @@ ffi64_struct_to_reg_form( case FFI_TYPE_STRUCT: recurseCount++; ffi64_struct_to_reg_form(inType->elements[i], - inStruct, &srcMarker, &fprsUsed, outGPRs, + inStruct, &srcMarker, &fprsUsed, outGPRs, &destGMarker, outFPRs, &destFMarker); recurseCount--; break; diff --git a/Modules/_ctypes/libffi_osx/types.c b/Modules/_ctypes/libffi_osx/types.c index 44806aeeb75d37..761d223ff1498c 100644 --- a/Modules/_ctypes/libffi_osx/types.c +++ b/Modules/_ctypes/libffi_osx/types.c @@ -1,6 +1,6 @@ /* ----------------------------------------------------------------------- types.c - Copyright (c) 1996, 1998 Red Hat, Inc. - + Predefined ffi_types needed by libffi. Permission is hereby granted, free of charge, to any person obtaining @@ -85,7 +85,7 @@ FFI_INTEGRAL_TYPEDEF(sint64, 8, 8, FFI_TYPE_SINT64); FFI_INTEGRAL_TYPEDEF(longdouble, 12, 4, FFI_TYPE_LONGDOUBLE); # endif -#elif defined ARM || defined SH || defined POWERPC_AIX +#elif defined ARM || defined SH || defined POWERPC_AIX FFI_INTEGRAL_TYPEDEF(double, 8, 4, FFI_TYPE_DOUBLE); FFI_INTEGRAL_TYPEDEF(longdouble, 8, 4, FFI_TYPE_LONGDOUBLE); #elif defined POWERPC_DARWIN diff --git a/Modules/_ctypes/libffi_osx/x86/x86-ffi64.c b/Modules/_ctypes/libffi_osx/x86/x86-ffi64.c index 8e7d016488029f..b8ae6e80231ece 100644 --- a/Modules/_ctypes/libffi_osx/x86/x86-ffi64.c +++ b/Modules/_ctypes/libffi_osx/x86/x86-ffi64.c @@ -2,8 +2,8 @@ /* ----------------------------------------------------------------------- x86-ffi64.c - Copyright (c) 2002 Bo Thorsen - - x86-64 Foreign Function Interface + + x86-64 Foreign Function Interface Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -208,7 +208,7 @@ classify_argument( case FFI_TYPE_STRUCT: { - ffi_type** ptr; + ffi_type** ptr; int i; enum x86_64_reg_class subclasses[MAX_CLASSES]; const int UNITS_PER_WORD = 8; diff --git a/Modules/_ctypes/libffi_osx/x86/x86-ffi_darwin.c b/Modules/_ctypes/libffi_osx/x86/x86-ffi_darwin.c index 706ea0f51206dc..59e36158067394 100644 --- a/Modules/_ctypes/libffi_osx/x86/x86-ffi_darwin.c +++ b/Modules/_ctypes/libffi_osx/x86/x86-ffi_darwin.c @@ -4,8 +4,8 @@ Copyright (c) 2002 Ranjit Mathew Copyright (c) 2002 Bo Thorsen Copyright (c) 2002 Roger Sayle - - x86 Foreign Function Interface + + x86 Foreign Function Interface Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -43,27 +43,27 @@ void ffi_prep_args(char *stack, extended_cif *ecif) register void **p_argv; register char *argp; register ffi_type **p_arg; - + argp = stack; - + if (ecif->cif->flags == FFI_TYPE_STRUCT) { *(void **) argp = ecif->rvalue; argp += 4; } - + p_argv = ecif->avalue; - + for (i = ecif->cif->nargs, p_arg = ecif->cif->arg_types; i != 0; i--, p_arg++) { size_t z; - + /* Align if necessary */ if ((sizeof(int) - 1) & (unsigned) argp) argp = (char *) ALIGN(argp, sizeof(int)); - + z = (*p_arg)->size; if (z < sizeof(int)) { @@ -73,31 +73,31 @@ void ffi_prep_args(char *stack, extended_cif *ecif) case FFI_TYPE_SINT8: *(signed int *) argp = (signed int)*(SINT8 *)(* p_argv); break; - + case FFI_TYPE_UINT8: *(unsigned int *) argp = (unsigned int)*(UINT8 *)(* p_argv); break; - + case FFI_TYPE_SINT16: *(signed int *) argp = (signed int)*(SINT16 *)(* p_argv); break; - + case FFI_TYPE_UINT16: *(unsigned int *) argp = (unsigned int)*(UINT16 *)(* p_argv); break; - + case FFI_TYPE_SINT32: *(signed int *) argp = (signed int)*(SINT32 *)(* p_argv); break; - + case FFI_TYPE_UINT32: *(unsigned int *) argp = (unsigned int)*(UINT32 *)(* p_argv); break; - + case FFI_TYPE_STRUCT: *(unsigned int *) argp = (unsigned int)*(UINT32 *)(* p_argv); break; - + default: FFI_ASSERT(0); } @@ -109,7 +109,7 @@ void ffi_prep_args(char *stack, extended_cif *ecif) p_argv++; argp += z; } - + return; } @@ -127,18 +127,18 @@ ffi_status ffi_prep_cif_machdep(ffi_cif *cif) case FFI_TYPE_SINT8: case FFI_TYPE_SINT16: #endif - + case FFI_TYPE_SINT64: case FFI_TYPE_FLOAT: case FFI_TYPE_DOUBLE: case FFI_TYPE_LONGDOUBLE: cif->flags = (unsigned) cif->rtype->type; break; - + case FFI_TYPE_UINT64: cif->flags = FFI_TYPE_SINT64; break; - + #ifndef X86 case FFI_TYPE_STRUCT: if (cif->rtype->size == 1) @@ -163,16 +163,16 @@ ffi_status ffi_prep_cif_machdep(ffi_cif *cif) } break; #endif - + default: cif->flags = FFI_TYPE_INT; break; } - + #ifdef X86_DARWIN cif->bytes = (cif->bytes + 15) & ~0xF; #endif - + return FFI_OK; } @@ -188,23 +188,23 @@ extern void ffi_call_STDCALL(void (*)(char *, extended_cif *), extended_cif *, void ffi_call(ffi_cif *cif, void (*fn)(), void *rvalue, void **avalue) { extended_cif ecif; - + ecif.cif = cif; ecif.avalue = avalue; - + /* If the return value is a struct and we don't have a return */ /* value address then we need to make one */ - - if ((rvalue == NULL) && + + if ((rvalue == NULL) && (cif->flags == FFI_TYPE_STRUCT)) { ecif.rvalue = alloca(cif->rtype->size); } else ecif.rvalue = rvalue; - - - switch (cif->abi) + + + switch (cif->abi) { case FFI_SYSV: ffi_call_SYSV(ffi_prep_args, &ecif, cif->bytes, cif->flags, ecif.rvalue, @@ -245,20 +245,20 @@ void *args; // our various things... ffi_cif *cif; void **arg_area; - + cif = closure->cif; - arg_area = (void**) alloca (cif->nargs * sizeof (void*)); - + arg_area = (void**) alloca (cif->nargs * sizeof (void*)); + /* this call will initialize ARG_AREA, such that each - * element in that array points to the corresponding + * element in that array points to the corresponding * value on the stack; and if the function returns * a structure, it will re-set RESP to point to the * structure return address. */ - + ffi_prep_incoming_args_SYSV(args, respp, arg_area, cif); - + (closure->fun) (cif, *respp, arg_area, closure->user_data); - + return cif->flags; } @@ -270,35 +270,35 @@ ffi_prep_incoming_args_SYSV(char *stack, void **rvalue, void **avalue, register void **p_argv; register char *argp; register ffi_type **p_arg; - + argp = stack; - + if ( cif->flags == FFI_TYPE_STRUCT ) { *rvalue = *(void **) argp; argp += 4; } - + p_argv = avalue; - + for (i = cif->nargs, p_arg = cif->arg_types; (i != 0); i--, p_arg++) { size_t z; - + /* Align if necessary */ if ((sizeof(int) - 1) & (unsigned) argp) { argp = (char *) ALIGN(argp, sizeof(int)); } - + z = (*p_arg)->size; - + /* because we're little endian, this is what it turns into. */ - + *p_argv = (void*) argp; - + p_argv++; argp += z; } - + return; } @@ -325,15 +325,15 @@ ffi_prep_closure (ffi_closure* closure, { if (cif->abi != FFI_SYSV) return FFI_BAD_ABI; - + FFI_INIT_TRAMPOLINE (&closure->tramp[0], \ &ffi_closure_SYSV, \ (void*)closure); - + closure->cif = cif; closure->user_data = user_data; closure->fun = fun; - + return FFI_OK; } @@ -349,32 +349,32 @@ ffi_prep_raw_closure_loc (ffi_raw_closure* closure, void *codeloc) { int i; - + FFI_ASSERT (cif->abi == FFI_SYSV); - + // we currently don't support certain kinds of arguments for raw // closures. This should be implemented by a separate assembly language // routine, since it would require argument processing, something we // don't do now for performance. - + for (i = cif->nargs-1; i >= 0; i--) { FFI_ASSERT (cif->arg_types[i]->type != FFI_TYPE_STRUCT); FFI_ASSERT (cif->arg_types[i]->type != FFI_TYPE_LONGDOUBLE); } - - + + FFI_INIT_TRAMPOLINE (&closure->tramp[0], &ffi_closure_raw_SYSV, codeloc); - + closure->cif = cif; closure->user_data = user_data; closure->fun = fun; - + return FFI_OK; } -static void +static void ffi_prep_args_raw(char *stack, extended_cif *ecif) { memcpy (stack, ecif->avalue, ecif->cif->bytes); @@ -386,7 +386,7 @@ ffi_prep_args_raw(char *stack, extended_cif *ecif) */ extern void -ffi_call_SYSV(void (*)(char *, extended_cif *), extended_cif *, unsigned, +ffi_call_SYSV(void (*)(char *, extended_cif *), extended_cif *, unsigned, unsigned, unsigned *, void (*fn)()); #ifdef X86_WIN32 @@ -400,23 +400,23 @@ ffi_raw_call(ffi_cif *cif, void (*fn)(), void *rvalue, ffi_raw *fake_avalue) { extended_cif ecif; void **avalue = (void **)fake_avalue; - + ecif.cif = cif; ecif.avalue = avalue; - + /* If the return value is a struct and we don't have a return */ /* value address then we need to make one */ - - if ((rvalue == NULL) && + + if ((rvalue == NULL) && (cif->rtype->type == FFI_TYPE_STRUCT)) { ecif.rvalue = alloca(cif->rtype->size); } else ecif.rvalue = rvalue; - - - switch (cif->abi) + + + switch (cif->abi) { case FFI_SYSV: ffi_call_SYSV(ffi_prep_args_raw, &ecif, cif->bytes, cif->flags, diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c index a7b2e984310d18..aa877174417cdd 100644 --- a/Modules/_io/_iomodule.c +++ b/Modules/_io/_iomodule.c @@ -59,7 +59,7 @@ PyDoc_STRVAR(module_doc, " I/O classes. open() uses the file's blksize (as obtained by os.stat) if\n" " possible.\n" ); - + /* * The main open() function @@ -512,7 +512,7 @@ _io_open_code_impl(PyObject *module, PyObject *path) { return PyFile_OpenCodeObject(path); } - + /* * Private helpers for the io module. */ diff --git a/Modules/termios.c b/Modules/termios.c index fcc8f042679870..3900a6f0b89860 100644 --- a/Modules/termios.c +++ b/Modules/termios.c @@ -85,7 +85,7 @@ termios_tcgetattr_impl(PyObject *module, int fd) int r; Py_BEGIN_ALLOW_THREADS - r = tcgetattr(fd, &mode); + r = tcgetattr(fd, &mode); Py_END_ALLOW_THREADS if (r == -1) { return PyErr_SetFromErrno(state->TermiosError); @@ -372,7 +372,7 @@ termios_tcgetwinsize_impl(PyObject *module, int fd) #if defined(TIOCGWINSZ) termiosmodulestate *state = PyModule_GetState(module); struct winsize w; - int r; + int r; Py_BEGIN_ALLOW_THREADS r = ioctl(fd, TIOCGWINSZ, &w); diff --git a/PC/winreg.c b/PC/winreg.c index f668cf3c19cab5..940278194f4cf5 100644 --- a/PC/winreg.c +++ b/PC/winreg.c @@ -564,7 +564,7 @@ Py2Reg(PyObject *value, DWORD typ, BYTE **retDataBuf, DWORD *retDataSize) { Py_ssize_t i,j; switch (typ) { - case REG_DWORD: + case REG_DWORD: { if (value != Py_None && !PyLong_Check(value)) { return FALSE; @@ -588,7 +588,7 @@ Py2Reg(PyObject *value, DWORD typ, BYTE **retDataBuf, DWORD *retDataSize) *retDataSize = sizeof(DWORD); break; } - case REG_QWORD: + case REG_QWORD: { if (value != Py_None && !PyLong_Check(value)) { return FALSE; diff --git a/Tools/msi/bundle/bootstrap/pch.h b/Tools/msi/bundle/bootstrap/pch.h index b0aa5111dabd0d..6d0974b34c61e7 100644 --- a/Tools/msi/bundle/bootstrap/pch.h +++ b/Tools/msi/bundle/bootstrap/pch.h @@ -5,7 +5,7 @@ // The license and further copyright text can be found in the file // LICENSE.TXT at the root directory of the distribution. // -// +// // // Precompiled header for standard bootstrapper application. // diff --git a/Tools/msi/bundle/bootstrap/resource.h b/Tools/msi/bundle/bootstrap/resource.h index 53c03c319f091f..d951e651f6d20d 100644 --- a/Tools/msi/bundle/bootstrap/resource.h +++ b/Tools/msi/bundle/bootstrap/resource.h @@ -14,7 +14,7 @@ // Next default values for new objects -// +// #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 102 From 9d00379e2c8dbf527cc113054b82f2fbde3e731c Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 22 Aug 2023 15:18:39 +0300 Subject: [PATCH 185/632] [3.11] Docs: move sphinx-lint to pre-commit (GH-105750) (#108276) --- .github/workflows/reusable-docs.yml | 4 ---- .pre-commit-config.yaml | 8 ++++++++ Doc/Makefile | 8 +++----- Doc/constraints.txt | 4 ---- Doc/requirements.txt | 1 - 5 files changed, 11 insertions(+), 14 deletions(-) diff --git a/.github/workflows/reusable-docs.yml b/.github/workflows/reusable-docs.yml index 9c1ed2788080dc..f8e166b780b3ed 100644 --- a/.github/workflows/reusable-docs.yml +++ b/.github/workflows/reusable-docs.yml @@ -26,8 +26,6 @@ jobs: cache-dependency-path: 'Doc/requirements.txt' - name: 'Install build dependencies' run: make -C Doc/ venv - - name: 'Check documentation' - run: make -C Doc/ check - name: 'Build HTML documentation' run: make -C Doc/ SPHINXOPTS="-q" SPHINXERRORHANDLING="-W --keep-going" html - name: 'Upload' @@ -37,8 +35,6 @@ jobs: path: Doc/build/html # This build doesn't use problem matchers or check annotations - # It also does not run 'make check', as sphinx-lint is not installed into the - # environment. build_doc_oldest_supported_sphinx: name: 'Docs (Oldest Sphinx)' runs-on: ubuntu-latest diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 808622f19a3dbf..889bd8502a10ea 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,3 +5,11 @@ repos: - id: check-yaml - id: trailing-whitespace types_or: [c, python, rst] + + - repo: https://github.com/sphinx-contrib/sphinx-lint + rev: v0.6.8 + hooks: + - id: sphinx-lint + args: [--enable=default-role] + files: ^Doc/|^Misc/NEWS.d/next/ + types: [rst] diff --git a/Doc/Makefile b/Doc/Makefile index 918814140f45cb..55be2b2dc5c1e6 100644 --- a/Doc/Makefile +++ b/Doc/Makefile @@ -214,11 +214,9 @@ dist: rm -r dist/python-$(DISTVERSION)-docs-texinfo rm dist/python-$(DISTVERSION)-docs-texinfo.tar -check: - # Check the docs and NEWS files with sphinx-lint. - # Ignore the tools and venv dirs and check that the default role is not used. - $(SPHINXLINT) -i tools -i $(VENVDIR) --enable default-role - $(SPHINXLINT) --enable default-role ../Misc/NEWS.d/next/ +check: venv + $(VENVDIR)/bin/python3 -m pre_commit --version > /dev/null || $(VENVDIR)/bin/python3 -m pip install pre-commit + $(VENVDIR)/bin/python3 -m pre_commit run --all-files serve: @echo "The serve target was removed, use htmlview instead (see bpo-36329)" diff --git a/Doc/constraints.txt b/Doc/constraints.txt index 66c748eb092d83..54888eaab242ee 100644 --- a/Doc/constraints.txt +++ b/Doc/constraints.txt @@ -23,7 +23,3 @@ sphinxcontrib-serializinghtml<1.2 # Direct dependencies of Jinja2 (Jinja is a dependency of Sphinx, see above) MarkupSafe<2.2 - -# Direct dependencies of sphinx-lint -polib<1.3 -regex<2024 diff --git a/Doc/requirements.txt b/Doc/requirements.txt index 3c0e0a7733673e..df79ae63840471 100644 --- a/Doc/requirements.txt +++ b/Doc/requirements.txt @@ -10,7 +10,6 @@ sphinx==4.5.0 blurb -sphinx-lint==0.6.7 sphinxext-opengraph>=0.7.1 # The theme used by the documentation is stored separately, so we need From d22ac0c6052c4e568c316ce3ce194fdf86d9af87 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 22 Aug 2023 06:28:38 -0700 Subject: [PATCH 186/632] [3.11] Docs: align the param spec of sqlite3.Connection methods with the implementation (GH-108285) (#108288) - no parameters of create_aggregate() are positional-only - all parameters of create_collation() are positional-only (cherry picked from commit 893215a4e7f59eabb8ccdf188c4b9b1de5bd8966) Co-authored-by: Erlend E. Aasland --- Doc/library/sqlite3.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 13e61b3d68006b..53c21aed2d6098 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -679,7 +679,7 @@ Connection objects ('acbd18db4cc2f85cedef654fccc4a4d8',) - .. method:: create_aggregate(name, /, n_arg, aggregate_class) + .. method:: create_aggregate(name, n_arg, aggregate_class) Create or remove a user-defined SQL aggregate function. @@ -819,7 +819,7 @@ Connection objects [('a', 9), ('b', 12), ('c', 16), ('d', 12), ('e', 9)] - .. method:: create_collation(name, callable) + .. method:: create_collation(name, callable, /) Create a collation named *name* using the collating function *callable*. *callable* is passed two :class:`string ` arguments, From 8927cf0200e56ebf74ed43f8c3fd724cd699ec22 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 22 Aug 2023 07:35:16 -0700 Subject: [PATCH 187/632] [3.11] gh-106242: Make ntpath.realpath errors consistent with abspath when there are embedded nulls (GH-108248) gh-106242: Make ntpath.realpath errors consistent with abspath when there are embedded nulls (GH-108248) --------- (cherry picked from commit de33b5c662ea8d35d81ed857c6a39e34ab94c510) Co-authored-by: Steve Dower Co-authored-by: Gregory P. Smith --- Lib/ntpath.py | 12 ++++++++++++ Lib/test/test_ntpath.py | 6 ++++++ .../2023-08-22-00-36-57.gh-issue-106242.q24ITw.rst | 4 ++++ 3 files changed, 22 insertions(+) create mode 100644 Misc/NEWS.d/next/Windows/2023-08-22-00-36-57.gh-issue-106242.q24ITw.rst diff --git a/Lib/ntpath.py b/Lib/ntpath.py index 0444b0f65d10ba..0246419485da0d 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -695,6 +695,14 @@ def realpath(path, *, strict=False): try: path = _getfinalpathname(path) initial_winerror = 0 + except ValueError as ex: + # gh-106242: Raised for embedded null characters + # In strict mode, we convert into an OSError. + # Non-strict mode returns the path as-is, since we've already + # made it absolute. + if strict: + raise OSError(str(ex)) from None + path = normpath(path) except OSError as ex: if strict: raise @@ -714,6 +722,10 @@ def realpath(path, *, strict=False): try: if _getfinalpathname(spath) == path: path = spath + except ValueError as ex: + # Unexpected, as an invalid path should not have gained a prefix + # at any point, but we ignore this error just in case. + pass except OSError as ex: # If the path does not exist and originally did not exist, then # strip the prefix anyway. diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py index 75e50d92ed1eb1..88660fc05a1b10 100644 --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -332,6 +332,10 @@ def test_realpath_basic(self): raise OSError("No free drive letters available") self.assertEqual(ntpath.realpath(d), d) + # gh-106242: Embedded nulls and non-strict fallback to abspath + self.assertEqual(ABSTFN + "\0spam", + ntpath.realpath(os_helper.TESTFN + "\0spam", strict=False)) + @os_helper.skip_unless_symlink @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') def test_realpath_strict(self): @@ -342,6 +346,8 @@ def test_realpath_strict(self): self.addCleanup(os_helper.unlink, ABSTFN) self.assertRaises(FileNotFoundError, ntpath.realpath, ABSTFN, strict=True) self.assertRaises(FileNotFoundError, ntpath.realpath, ABSTFN + "2", strict=True) + # gh-106242: Embedded nulls should raise OSError (not ValueError) + self.assertRaises(OSError, ntpath.realpath, ABSTFN + "\0spam", strict=True) @os_helper.skip_unless_symlink @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') diff --git a/Misc/NEWS.d/next/Windows/2023-08-22-00-36-57.gh-issue-106242.q24ITw.rst b/Misc/NEWS.d/next/Windows/2023-08-22-00-36-57.gh-issue-106242.q24ITw.rst new file mode 100644 index 00000000000000..ffe42ec5dc3faf --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-08-22-00-36-57.gh-issue-106242.q24ITw.rst @@ -0,0 +1,4 @@ +Fixes :func:`~os.path.realpath` to behave consistently when passed a path +containing an embedded null character on Windows. In strict mode, it now +raises :exc:`OSError` instead of the unexpected :exc:`ValueError`, and in +non-strict mode will make the path absolute. From 5be32d88182f956f423a56c6250ce4020e8cd53c Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 22 Aug 2023 08:42:00 -0700 Subject: [PATCH 188/632] [3.11] gh-106016: Add Lib/test/test_module/ directory (GH-108293) (#108304) gh-106016: Add Lib/test/test_module/ directory (GH-108293) * Move Python scripts related to test_module to this new directory: good_getattr.py and bad_getattrX.py scripts. * Move Lib/test/test_module.py to Lib/test/test_module/__init__.py. (cherry picked from commit adfc118fdab66882599e01a84c22bd897055f3f1) Co-authored-by: Victor Stinner --- .../__init__.py} | 36 +++++++++---------- Lib/test/{ => test_module}/bad_getattr.py | 0 Lib/test/{ => test_module}/bad_getattr2.py | 0 Lib/test/{ => test_module}/bad_getattr3.py | 0 Lib/test/{ => test_module}/good_getattr.py | 0 Makefile.pre.in | 1 + 6 files changed, 19 insertions(+), 18 deletions(-) rename Lib/test/{test_module.py => test_module/__init__.py} (92%) rename Lib/test/{ => test_module}/bad_getattr.py (100%) rename Lib/test/{ => test_module}/bad_getattr2.py (100%) rename Lib/test/{ => test_module}/bad_getattr3.py (100%) rename Lib/test/{ => test_module}/good_getattr.py (100%) diff --git a/Lib/test/test_module.py b/Lib/test/test_module/__init__.py similarity index 92% rename from Lib/test/test_module.py rename to Lib/test/test_module/__init__.py index f72177dda3702a..87d1a8e3d6c978 100644 --- a/Lib/test/test_module.py +++ b/Lib/test/test_module/__init__.py @@ -126,8 +126,8 @@ def test_weakref(self): self.assertIs(wr(), None) def test_module_getattr(self): - import test.good_getattr as gga - from test.good_getattr import test + import test.test_module.good_getattr as gga + from test.test_module.good_getattr import test self.assertEqual(test, "There is test") self.assertEqual(gga.x, 1) self.assertEqual(gga.y, 2) @@ -135,46 +135,46 @@ def test_module_getattr(self): "Deprecated, use whatever instead"): gga.yolo self.assertEqual(gga.whatever, "There is whatever") - del sys.modules['test.good_getattr'] + del sys.modules['test.test_module.good_getattr'] def test_module_getattr_errors(self): - import test.bad_getattr as bga - from test import bad_getattr2 + import test.test_module.bad_getattr as bga + from test.test_module import bad_getattr2 self.assertEqual(bga.x, 1) self.assertEqual(bad_getattr2.x, 1) with self.assertRaises(TypeError): bga.nope with self.assertRaises(TypeError): bad_getattr2.nope - del sys.modules['test.bad_getattr'] - if 'test.bad_getattr2' in sys.modules: - del sys.modules['test.bad_getattr2'] + del sys.modules['test.test_module.bad_getattr'] + if 'test.test_module.bad_getattr2' in sys.modules: + del sys.modules['test.test_module.bad_getattr2'] def test_module_dir(self): - import test.good_getattr as gga + import test.test_module.good_getattr as gga self.assertEqual(dir(gga), ['a', 'b', 'c']) - del sys.modules['test.good_getattr'] + del sys.modules['test.test_module.good_getattr'] def test_module_dir_errors(self): - import test.bad_getattr as bga - from test import bad_getattr2 + import test.test_module.bad_getattr as bga + from test.test_module import bad_getattr2 with self.assertRaises(TypeError): dir(bga) with self.assertRaises(TypeError): dir(bad_getattr2) - del sys.modules['test.bad_getattr'] - if 'test.bad_getattr2' in sys.modules: - del sys.modules['test.bad_getattr2'] + del sys.modules['test.test_module.bad_getattr'] + if 'test.test_module.bad_getattr2' in sys.modules: + del sys.modules['test.test_module.bad_getattr2'] def test_module_getattr_tricky(self): - from test import bad_getattr3 + from test.test_module import bad_getattr3 # these lookups should not crash with self.assertRaises(AttributeError): bad_getattr3.one with self.assertRaises(AttributeError): bad_getattr3.delgetattr - if 'test.bad_getattr3' in sys.modules: - del sys.modules['test.bad_getattr3'] + if 'test.test_module.bad_getattr3' in sys.modules: + del sys.modules['test.test_module.bad_getattr3'] def test_module_repr_minimal(self): # reprs when modules have no __file__, __name__, or __loader__ diff --git a/Lib/test/bad_getattr.py b/Lib/test/test_module/bad_getattr.py similarity index 100% rename from Lib/test/bad_getattr.py rename to Lib/test/test_module/bad_getattr.py diff --git a/Lib/test/bad_getattr2.py b/Lib/test/test_module/bad_getattr2.py similarity index 100% rename from Lib/test/bad_getattr2.py rename to Lib/test/test_module/bad_getattr2.py diff --git a/Lib/test/bad_getattr3.py b/Lib/test/test_module/bad_getattr3.py similarity index 100% rename from Lib/test/bad_getattr3.py rename to Lib/test/test_module/bad_getattr3.py diff --git a/Lib/test/good_getattr.py b/Lib/test/test_module/good_getattr.py similarity index 100% rename from Lib/test/good_getattr.py rename to Lib/test/test_module/good_getattr.py diff --git a/Makefile.pre.in b/Makefile.pre.in index 2a407a7df697e0..6405d06afe14e1 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -2012,6 +2012,7 @@ TESTSUBDIRS= ctypes/test \ test/test_importlib/zipdata01 \ test/test_importlib/zipdata02 \ test/test_json \ + test/test_module \ test/test_peg_generator \ test/test_sqlite3 \ test/test_tomllib \ From 75a875e0df0530b75b1470d797942f90f4a718d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Tue, 22 Aug 2023 19:53:19 +0200 Subject: [PATCH 189/632] [3.11] gh-108310: Fix CVE-2023-40217: Check for & avoid the ssl pre-close flaw (#108317) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gh-108310: Fix CVE-2023-40217: Check for & avoid the ssl pre-close flaw Instances of `ssl.SSLSocket` were vulnerable to a bypass of the TLS handshake and included protections (like certificate verification) and treating sent unencrypted data as if it were post-handshake TLS encrypted data. The vulnerability is caused when a socket is connected, data is sent by the malicious peer and stored in a buffer, and then the malicious peer closes the socket within a small timing window before the other peers’ TLS handshake can begin. After this sequence of events the closed socket will not immediately attempt a TLS handshake due to not being connected but will also allow the buffered data to be read as if a successful TLS handshake had occurred. Co-authored-by: Gregory P. Smith [Google LLC] --- Lib/ssl.py | 31 ++- Lib/test/test_ssl.py | 211 ++++++++++++++++++ ...-08-22-17-39-12.gh-issue-108310.fVM3sg.rst | 7 + 3 files changed, 248 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Security/2023-08-22-17-39-12.gh-issue-108310.fVM3sg.rst diff --git a/Lib/ssl.py b/Lib/ssl.py index ebac1d60d52de7..ced87d406995c1 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -1037,7 +1037,7 @@ def _create(cls, sock, server_side=False, do_handshake_on_connect=True, ) self = cls.__new__(cls, **kwargs) super(SSLSocket, self).__init__(**kwargs) - self.settimeout(sock.gettimeout()) + sock_timeout = sock.gettimeout() sock.detach() self._context = context @@ -1056,9 +1056,38 @@ def _create(cls, sock, server_side=False, do_handshake_on_connect=True, if e.errno != errno.ENOTCONN: raise connected = False + blocking = self.getblocking() + self.setblocking(False) + try: + # We are not connected so this is not supposed to block, but + # testing revealed otherwise on macOS and Windows so we do + # the non-blocking dance regardless. Our raise when any data + # is found means consuming the data is harmless. + notconn_pre_handshake_data = self.recv(1) + except OSError as e: + # EINVAL occurs for recv(1) on non-connected on unix sockets. + if e.errno not in (errno.ENOTCONN, errno.EINVAL): + raise + notconn_pre_handshake_data = b'' + self.setblocking(blocking) + if notconn_pre_handshake_data: + # This prevents pending data sent to the socket before it was + # closed from escaping to the caller who could otherwise + # presume it came through a successful TLS connection. + reason = "Closed before TLS handshake with data in recv buffer." + notconn_pre_handshake_data_error = SSLError(e.errno, reason) + # Add the SSLError attributes that _ssl.c always adds. + notconn_pre_handshake_data_error.reason = reason + notconn_pre_handshake_data_error.library = None + try: + self.close() + except OSError: + pass + raise notconn_pre_handshake_data_error else: connected = True + self.settimeout(sock_timeout) # Must come after setblocking() calls. self._connected = connected if connected: # create the SSL object diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 85a561f6b74919..4a2508988c0f76 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -9,11 +9,14 @@ from test.support import socket_helper from test.support import threading_helper from test.support import warnings_helper +import re import socket import select +import struct import time import enum import gc +import http.client import os import errno import pprint @@ -4896,6 +4899,214 @@ def sni_cb(sock, servername, ctx): s.connect((HOST, server.port)) +def set_socket_so_linger_on_with_zero_timeout(sock): + sock.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 0)) + + +class TestPreHandshakeClose(unittest.TestCase): + """Verify behavior of close sockets with received data before to the handshake. + """ + + class SingleConnectionTestServerThread(threading.Thread): + + def __init__(self, *, name, call_after_accept): + self.call_after_accept = call_after_accept + self.received_data = b'' # set by .run() + self.wrap_error = None # set by .run() + self.listener = None # set by .start() + self.port = None # set by .start() + super().__init__(name=name) + + def __enter__(self): + self.start() + return self + + def __exit__(self, *args): + try: + if self.listener: + self.listener.close() + except OSError: + pass + self.join() + self.wrap_error = None # avoid dangling references + + def start(self): + self.ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) + self.ssl_ctx.verify_mode = ssl.CERT_REQUIRED + self.ssl_ctx.load_verify_locations(cafile=ONLYCERT) + self.ssl_ctx.load_cert_chain(certfile=ONLYCERT, keyfile=ONLYKEY) + self.listener = socket.socket() + self.port = socket_helper.bind_port(self.listener) + self.listener.settimeout(2.0) + self.listener.listen(1) + super().start() + + def run(self): + conn, address = self.listener.accept() + self.listener.close() + with conn: + if self.call_after_accept(conn): + return + try: + tls_socket = self.ssl_ctx.wrap_socket(conn, server_side=True) + except OSError as err: # ssl.SSLError inherits from OSError + self.wrap_error = err + else: + try: + self.received_data = tls_socket.recv(400) + except OSError: + pass # closed, protocol error, etc. + + def non_linux_skip_if_other_okay_error(self, err): + if sys.platform == "linux": + return # Expect the full test setup to always work on Linux. + if (isinstance(err, ConnectionResetError) or + (isinstance(err, OSError) and err.errno == errno.EINVAL) or + re.search('wrong.version.number', getattr(err, "reason", ""), re.I)): + # On Windows the TCP RST leads to a ConnectionResetError + # (ECONNRESET) which Linux doesn't appear to surface to userspace. + # If wrap_socket() winds up on the "if connected:" path and doing + # the actual wrapping... we get an SSLError from OpenSSL. Typically + # WRONG_VERSION_NUMBER. While appropriate, neither is the scenario + # we're specifically trying to test. The way this test is written + # is known to work on Linux. We'll skip it anywhere else that it + # does not present as doing so. + self.skipTest(f"Could not recreate conditions on {sys.platform}:" + f" {err=}") + # If maintaining this conditional winds up being a problem. + # just turn this into an unconditional skip anything but Linux. + # The important thing is that our CI has the logic covered. + + def test_preauth_data_to_tls_server(self): + server_accept_called = threading.Event() + ready_for_server_wrap_socket = threading.Event() + + def call_after_accept(unused): + server_accept_called.set() + if not ready_for_server_wrap_socket.wait(2.0): + raise RuntimeError("wrap_socket event never set, test may fail.") + return False # Tell the server thread to continue. + + server = self.SingleConnectionTestServerThread( + call_after_accept=call_after_accept, + name="preauth_data_to_tls_server") + self.enterContext(server) # starts it & unittest.TestCase stops it. + + with socket.socket() as client: + client.connect(server.listener.getsockname()) + # This forces an immediate connection close via RST on .close(). + set_socket_so_linger_on_with_zero_timeout(client) + client.setblocking(False) + + server_accept_called.wait() + client.send(b"DELETE /data HTTP/1.0\r\n\r\n") + client.close() # RST + + ready_for_server_wrap_socket.set() + server.join() + wrap_error = server.wrap_error + self.assertEqual(b"", server.received_data) + self.assertIsInstance(wrap_error, OSError) # All platforms. + self.non_linux_skip_if_other_okay_error(wrap_error) + self.assertIsInstance(wrap_error, ssl.SSLError) + self.assertIn("before TLS handshake with data", wrap_error.args[1]) + self.assertIn("before TLS handshake with data", wrap_error.reason) + self.assertNotEqual(0, wrap_error.args[0]) + self.assertIsNone(wrap_error.library, msg="attr must exist") + + def test_preauth_data_to_tls_client(self): + client_can_continue_with_wrap_socket = threading.Event() + + def call_after_accept(conn_to_client): + # This forces an immediate connection close via RST on .close(). + set_socket_so_linger_on_with_zero_timeout(conn_to_client) + conn_to_client.send( + b"HTTP/1.0 307 Temporary Redirect\r\n" + b"Location: https://example.com/someone-elses-server\r\n" + b"\r\n") + conn_to_client.close() # RST + client_can_continue_with_wrap_socket.set() + return True # Tell the server to stop. + + server = self.SingleConnectionTestServerThread( + call_after_accept=call_after_accept, + name="preauth_data_to_tls_client") + self.enterContext(server) # starts it & unittest.TestCase stops it. + # Redundant; call_after_accept sets SO_LINGER on the accepted conn. + set_socket_so_linger_on_with_zero_timeout(server.listener) + + with socket.socket() as client: + client.connect(server.listener.getsockname()) + if not client_can_continue_with_wrap_socket.wait(2.0): + self.fail("test server took too long.") + ssl_ctx = ssl.create_default_context() + try: + tls_client = ssl_ctx.wrap_socket( + client, server_hostname="localhost") + except OSError as err: # SSLError inherits from OSError + wrap_error = err + received_data = b"" + else: + wrap_error = None + received_data = tls_client.recv(400) + tls_client.close() + + server.join() + self.assertEqual(b"", received_data) + self.assertIsInstance(wrap_error, OSError) # All platforms. + self.non_linux_skip_if_other_okay_error(wrap_error) + self.assertIsInstance(wrap_error, ssl.SSLError) + self.assertIn("before TLS handshake with data", wrap_error.args[1]) + self.assertIn("before TLS handshake with data", wrap_error.reason) + self.assertNotEqual(0, wrap_error.args[0]) + self.assertIsNone(wrap_error.library, msg="attr must exist") + + def test_https_client_non_tls_response_ignored(self): + + server_responding = threading.Event() + + class SynchronizedHTTPSConnection(http.client.HTTPSConnection): + def connect(self): + http.client.HTTPConnection.connect(self) + # Wait for our fault injection server to have done its thing. + if not server_responding.wait(1.0) and support.verbose: + sys.stdout.write("server_responding event never set.") + self.sock = self._context.wrap_socket( + self.sock, server_hostname=self.host) + + def call_after_accept(conn_to_client): + # This forces an immediate connection close via RST on .close(). + set_socket_so_linger_on_with_zero_timeout(conn_to_client) + conn_to_client.send( + b"HTTP/1.0 402 Payment Required\r\n" + b"\r\n") + conn_to_client.close() # RST + server_responding.set() + return True # Tell the server to stop. + + server = self.SingleConnectionTestServerThread( + call_after_accept=call_after_accept, + name="non_tls_http_RST_responder") + self.enterContext(server) # starts it & unittest.TestCase stops it. + # Redundant; call_after_accept sets SO_LINGER on the accepted conn. + set_socket_so_linger_on_with_zero_timeout(server.listener) + + connection = SynchronizedHTTPSConnection( + f"localhost", + port=server.port, + context=ssl.create_default_context(), + timeout=2.0, + ) + # There are lots of reasons this raises as desired, long before this + # test was added. Sending the request requires a successful TLS wrapped + # socket; that fails if the connection is broken. It may seem pointless + # to test this. It serves as an illustration of something that we never + # want to happen... properly not happening. + with self.assertRaises(OSError) as err_ctx: + connection.request("HEAD", "/test", headers={"Host": "localhost"}) + response = connection.getresponse() + + class TestEnumerations(unittest.TestCase): def test_tlsversion(self): diff --git a/Misc/NEWS.d/next/Security/2023-08-22-17-39-12.gh-issue-108310.fVM3sg.rst b/Misc/NEWS.d/next/Security/2023-08-22-17-39-12.gh-issue-108310.fVM3sg.rst new file mode 100644 index 00000000000000..403c77a9d480ee --- /dev/null +++ b/Misc/NEWS.d/next/Security/2023-08-22-17-39-12.gh-issue-108310.fVM3sg.rst @@ -0,0 +1,7 @@ +Fixed an issue where instances of :class:`ssl.SSLSocket` were vulnerable to +a bypass of the TLS handshake and included protections (like certificate +verification) and treating sent unencrypted data as if it were +post-handshake TLS encrypted data. Security issue reported as +`CVE-2023-40217 +`_ by +Aapo Oksman. Patch by Gregory P. Smith. From 3a39e151799d4882bb73ef216705c3e71b67ca00 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 22 Aug 2023 11:32:16 -0700 Subject: [PATCH 190/632] [3.11] Resolve reference warnings in faq/gui.rst (GH-108147) (#108194) (cherry picked from commit 8f3d09bf5d16b508fece5420a22abe6f0c1f00b7) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Co-authored-by: Hugo van Kemenade --- Doc/faq/gui.rst | 9 +++++---- Doc/tools/.nitignore | 1 - 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Doc/faq/gui.rst b/Doc/faq/gui.rst index 023ffdf0db510a..77c0a27a10c9a3 100644 --- a/Doc/faq/gui.rst +++ b/Doc/faq/gui.rst @@ -43,7 +43,7 @@ applications, the applications will not be truly stand-alone, as the application will still need the Tcl and Tk libraries. One solution is to ship the application with the Tcl and Tk libraries, and point -to them at run-time using the :envvar:`TCL_LIBRARY` and :envvar:`TK_LIBRARY` +to them at run-time using the :envvar:`!TCL_LIBRARY` and :envvar:`!TK_LIBRARY` environment variables. To get truly stand-alone applications, the Tcl scripts that form the library @@ -62,7 +62,7 @@ Can I have Tk events handled while waiting for I/O? On platforms other than Windows, yes, and you don't even need threads! But you'll have to restructure your I/O -code a bit. Tk has the equivalent of Xt's :c:func:`XtAddInput()` call, which allows you +code a bit. Tk has the equivalent of Xt's :c:func:`!XtAddInput` call, which allows you to register a callback function which will be called from the Tk mainloop when I/O is possible on a file descriptor. See :ref:`tkinter-file-handlers`. @@ -70,8 +70,9 @@ I/O is possible on a file descriptor. See :ref:`tkinter-file-handlers`. I can't get key bindings to work in Tkinter: why? ------------------------------------------------- -An often-heard complaint is that event handlers bound to events with the -:meth:`bind` method don't get handled even when the appropriate key is pressed. +An often-heard complaint is that event handlers :ref:`bound ` +to events with the :meth:`!bind` method +don't get handled even when the appropriate key is pressed. The most common cause is that the widget to which the binding applies doesn't have "keyboard focus". Check out the Tk documentation for the focus command. diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 8c44199cb861d3..c8f3080cb6eddf 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -24,7 +24,6 @@ Doc/c-api/typeobj.rst Doc/c-api/unicode.rst Doc/extending/extending.rst Doc/extending/newtypes.rst -Doc/faq/gui.rst Doc/glossary.rst Doc/howto/descriptor.rst Doc/howto/enum.rst From 80c7ae00f10a374e1d79d1c2719056951fdd3421 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 22 Aug 2023 11:32:48 -0700 Subject: [PATCH 191/632] [3.11] gh-107298: Fix C API datetime documentation (GH-108034) (#108233) (cherry picked from commit d63972e289e05b0d82e59f32f107312a8b3de7b5) Co-authored-by: Serhiy Storchaka --- Doc/c-api/datetime.rst | 71 +++++++++++++++++++++++++++++++++--------- Doc/tools/.nitignore | 1 - 2 files changed, 57 insertions(+), 15 deletions(-) diff --git a/Doc/c-api/datetime.rst b/Doc/c-api/datetime.rst index 72fc07afbf1f4d..97522da773477e 100644 --- a/Doc/c-api/datetime.rst +++ b/Doc/c-api/datetime.rst @@ -8,11 +8,54 @@ DateTime Objects Various date and time objects are supplied by the :mod:`datetime` module. Before using any of these functions, the header file :file:`datetime.h` must be included in your source (note that this is not included by :file:`Python.h`), -and the macro :c:macro:`PyDateTime_IMPORT` must be invoked, usually as part of +and the macro :c:macro:`!PyDateTime_IMPORT` must be invoked, usually as part of the module initialisation function. The macro puts a pointer to a C structure -into a static variable, :c:data:`PyDateTimeAPI`, that is used by the following +into a static variable, :c:data:`!PyDateTimeAPI`, that is used by the following macros. +.. c:type:: PyDateTime_Date + + This subtype of :c:type:`PyObject` represents a Python date object. + +.. c:type:: PyDateTime_DateTime + + This subtype of :c:type:`PyObject` represents a Python datetime object. + +.. c:type:: PyDateTime_Time + + This subtype of :c:type:`PyObject` represents a Python time object. + +.. c:type:: PyDateTime_Delta + + This subtype of :c:type:`PyObject` represents the difference between two datetime values. + +.. c:var:: PyTypeObject PyDateTime_DateType + + This instance of :c:type:`PyTypeObject` represents the Python date type; + it is the same object as :class:`datetime.date` in the Python layer. + +.. c:var:: PyTypeObject PyDateTime_DateTimeType + + This instance of :c:type:`PyTypeObject` represents the Python datetime type; + it is the same object as :class:`datetime.datetime` in the Python layer. + +.. c:var:: PyTypeObject PyDateTime_TimeType + + This instance of :c:type:`PyTypeObject` represents the Python time type; + it is the same object as :class:`datetime.time` in the Python layer. + +.. c:var:: PyTypeObject PyDateTime_DeltaType + + This instance of :c:type:`PyTypeObject` represents Python type for + the difference between two datetime values; + it is the same object as :class:`datetime.timedelta` in the Python layer. + +.. c:var:: PyTypeObject PyDateTime_TZInfoType + + This instance of :c:type:`PyTypeObject` represents the Python time zone info type; + it is the same object as :class:`datetime.tzinfo` in the Python layer. + + Macro for access to the UTC singleton: .. c:var:: PyObject* PyDateTime_TimeZone_UTC @@ -28,7 +71,7 @@ Type-check macros: .. c:function:: int PyDate_Check(PyObject *ob) Return true if *ob* is of type :c:data:`PyDateTime_DateType` or a subtype of - :c:data:`PyDateTime_DateType`. *ob* must not be ``NULL``. This function always + :c:data:`!PyDateTime_DateType`. *ob* must not be ``NULL``. This function always succeeds. @@ -41,7 +84,7 @@ Type-check macros: .. c:function:: int PyDateTime_Check(PyObject *ob) Return true if *ob* is of type :c:data:`PyDateTime_DateTimeType` or a subtype of - :c:data:`PyDateTime_DateTimeType`. *ob* must not be ``NULL``. This function always + :c:data:`!PyDateTime_DateTimeType`. *ob* must not be ``NULL``. This function always succeeds. @@ -54,7 +97,7 @@ Type-check macros: .. c:function:: int PyTime_Check(PyObject *ob) Return true if *ob* is of type :c:data:`PyDateTime_TimeType` or a subtype of - :c:data:`PyDateTime_TimeType`. *ob* must not be ``NULL``. This function always + :c:data:`!PyDateTime_TimeType`. *ob* must not be ``NULL``. This function always succeeds. @@ -67,7 +110,7 @@ Type-check macros: .. c:function:: int PyDelta_Check(PyObject *ob) Return true if *ob* is of type :c:data:`PyDateTime_DeltaType` or a subtype of - :c:data:`PyDateTime_DeltaType`. *ob* must not be ``NULL``. This function always + :c:data:`!PyDateTime_DeltaType`. *ob* must not be ``NULL``. This function always succeeds. @@ -80,7 +123,7 @@ Type-check macros: .. c:function:: int PyTZInfo_Check(PyObject *ob) Return true if *ob* is of type :c:data:`PyDateTime_TZInfoType` or a subtype of - :c:data:`PyDateTime_TZInfoType`. *ob* must not be ``NULL``. This function always + :c:data:`!PyDateTime_TZInfoType`. *ob* must not be ``NULL``. This function always succeeds. @@ -133,7 +176,7 @@ Macros to create objects: :class:`datetime.timedelta` objects. -.. c:function:: PyObject* PyTimeZone_FromOffset(PyDateTime_DeltaType* offset) +.. c:function:: PyObject* PyTimeZone_FromOffset(PyObject *offset) Return a :class:`datetime.timezone` object with an unnamed fixed offset represented by the *offset* argument. @@ -141,7 +184,7 @@ Macros to create objects: .. versionadded:: 3.7 -.. c:function:: PyObject* PyTimeZone_FromOffsetAndName(PyDateTime_DeltaType* offset, PyUnicode* name) +.. c:function:: PyObject* PyTimeZone_FromOffsetAndName(PyObject *offset, PyObject *name) Return a :class:`datetime.timezone` object with a fixed offset represented by the *offset* argument and with tzname *name*. @@ -150,8 +193,8 @@ Macros to create objects: Macros to extract fields from date objects. The argument must be an instance of -:c:data:`PyDateTime_Date`, including subclasses (such as -:c:data:`PyDateTime_DateTime`). The argument must not be ``NULL``, and the type is +:c:type:`PyDateTime_Date`, including subclasses (such as +:c:type:`PyDateTime_DateTime`). The argument must not be ``NULL``, and the type is not checked: .. c:function:: int PyDateTime_GET_YEAR(PyDateTime_Date *o) @@ -170,7 +213,7 @@ not checked: Macros to extract fields from datetime objects. The argument must be an -instance of :c:data:`PyDateTime_DateTime`, including subclasses. The argument +instance of :c:type:`PyDateTime_DateTime`, including subclasses. The argument must not be ``NULL``, and the type is not checked: .. c:function:: int PyDateTime_DATE_GET_HOUR(PyDateTime_DateTime *o) @@ -208,7 +251,7 @@ must not be ``NULL``, and the type is not checked: Macros to extract fields from time objects. The argument must be an instance of -:c:data:`PyDateTime_Time`, including subclasses. The argument must not be ``NULL``, +:c:type:`PyDateTime_Time`, including subclasses. The argument must not be ``NULL``, and the type is not checked: .. c:function:: int PyDateTime_TIME_GET_HOUR(PyDateTime_Time *o) @@ -246,7 +289,7 @@ and the type is not checked: Macros to extract fields from time delta objects. The argument must be an -instance of :c:data:`PyDateTime_Delta`, including subclasses. The argument must +instance of :c:type:`PyDateTime_Delta`, including subclasses. The argument must not be ``NULL``, and the type is not checked: .. c:function:: int PyDateTime_DELTA_GET_DAYS(PyDateTime_Delta *o) diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index c8f3080cb6eddf..4021f97e34f7fb 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -2,7 +2,6 @@ # as tested on the CI via check-warnings.py in reusable-docs.yml. # Keep lines sorted lexicographically to help avoid merge conflicts. -Doc/c-api/datetime.rst Doc/c-api/descriptor.rst Doc/c-api/exceptions.rst Doc/c-api/file.rst From 3ca9264aba054672e8a33c2e0d9cf32cb4e17a86 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 22 Aug 2023 21:33:07 +0300 Subject: [PATCH 192/632] [3.11] gh-107298: Fix numerous ref errors and typos in the C API docs (GH-108258) (#108290) (cherry picked from commit d7202e4879bf4e7e00a69500ddcb3143864139b4) --- Doc/c-api/exceptions.rst | 14 +++++++++----- Doc/c-api/init_config.rst | 4 ++++ Doc/c-api/module.rst | 2 ++ Doc/c-api/unicode.rst | 2 +- Doc/extending/newtypes.rst | 2 +- Doc/tools/.nitignore | 1 - Doc/whatsnew/2.2.rst | 12 ++++++------ Doc/whatsnew/2.3.rst | 2 +- Doc/whatsnew/2.4.rst | 2 +- Doc/whatsnew/2.5.rst | 10 +++++----- Doc/whatsnew/2.6.rst | 8 ++++---- Doc/whatsnew/2.7.rst | 10 +++++----- Doc/whatsnew/3.0.rst | 4 ++-- Doc/whatsnew/3.1.rst | 4 ++-- Doc/whatsnew/3.10.rst | 4 ++-- Doc/whatsnew/3.11.rst | 26 +++++++++++++------------- Doc/whatsnew/3.2.rst | 14 +++++++------- Doc/whatsnew/3.3.rst | 4 ++-- Doc/whatsnew/3.5.rst | 4 ++-- Doc/whatsnew/3.8.rst | 16 ++++++++-------- Doc/whatsnew/3.9.rst | 12 ++++++------ Misc/NEWS.d/3.11.0a1.rst | 18 +++++++++--------- Misc/NEWS.d/3.8.0a1.rst | 4 ++-- Misc/NEWS.d/3.8.0a4.rst | 2 +- Misc/NEWS.d/3.8.0b1.rst | 2 +- Misc/NEWS.d/3.9.0a1.rst | 6 +++--- Misc/NEWS.d/3.9.0a5.rst | 4 ++-- 27 files changed, 101 insertions(+), 92 deletions(-) diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst index ae45aaef238fec..82050845237bbd 100644 --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -211,17 +211,21 @@ For convenience, some of these functions will always return a .. c:function:: PyObject* PyErr_SetFromWindowsErrWithFilename(int ierr, const char *filename) - Similar to :c:func:`PyErr_SetFromWindowsErrWithFilenameObject`, but the - filename is given as a C string. *filename* is decoded from the filesystem - encoding (:func:`os.fsdecode`). + Similar to :c:func:`PyErr_SetFromWindowsErr`, with the additional behavior + that if *filename* is not ``NULL``, it is decoded from the filesystem + encoding (:func:`os.fsdecode`) and passed to the constructor of + :exc:`OSError` as a third parameter to be used to define the + :attr:`!filename` attribute of the exception instance. .. availability:: Windows. .. c:function:: PyObject* PyErr_SetExcFromWindowsErrWithFilenameObject(PyObject *type, int ierr, PyObject *filename) - Similar to :c:func:`PyErr_SetFromWindowsErrWithFilenameObject`, with an - additional parameter specifying the exception type to be raised. + Similar to :c:func:`PyErr_SetExcFromWindowsErr`, with the additional behavior + that if *filename* is not ``NULL``, it is passed to the constructor of + :exc:`OSError` as a third parameter to be used to define the + :attr:`!filename` attribute of the exception instance. .. availability:: Windows. diff --git a/Doc/c-api/init_config.rst b/Doc/c-api/init_config.rst index c6798d68b0e8a7..ad1cd3f4f06681 100644 --- a/Doc/c-api/init_config.rst +++ b/Doc/c-api/init_config.rst @@ -82,6 +82,8 @@ PyWideStringList If *length* is non-zero, *items* must be non-``NULL`` and all strings must be non-``NULL``. + .. c:namespace:: NULL + Methods: .. c:function:: PyStatus PyWideStringList_Append(PyWideStringList *list, const wchar_t *item) @@ -101,6 +103,8 @@ PyWideStringList Python must be preinitialized to call this function. + .. c:namespace:: PyWideStringList + Structure fields: .. c:member:: Py_ssize_t length diff --git a/Doc/c-api/module.rst b/Doc/c-api/module.rst index 6ef4eea6a07f73..71a079a8e67f4f 100644 --- a/Doc/c-api/module.rst +++ b/Doc/c-api/module.rst @@ -338,6 +338,7 @@ The available slot types are: The *value* pointer of this slot must point to a function of the signature: .. c:function:: PyObject* create_module(PyObject *spec, PyModuleDef *def) + :noindex: The function receives a :py:class:`~importlib.machinery.ModuleSpec` instance, as defined in :PEP:`451`, and the module definition. @@ -372,6 +373,7 @@ The available slot types are: The signature of the function is: .. c:function:: int exec_module(PyObject* module) + :noindex: If multiple ``Py_mod_exec`` slots are specified, they are processed in the order they appear in the *m_slots* array. diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst index 3d6b4c49fa9c83..146511b9e3658a 100644 --- a/Doc/c-api/unicode.rst +++ b/Doc/c-api/unicode.rst @@ -1391,7 +1391,7 @@ the user settings on the machine running the codec. Encode the Unicode object using the specified code page and return a Python bytes object. Return ``NULL`` if an exception was raised by the codec. Use - :c:macro:`CP_ACP` code page to get the MBCS encoder. + :c:macro:`!CP_ACP` code page to get the MBCS encoder. .. versionadded:: 3.3 diff --git a/Doc/extending/newtypes.rst b/Doc/extending/newtypes.rst index c9d2301d1470c1..8ea76aec8290db 100644 --- a/Doc/extending/newtypes.rst +++ b/Doc/extending/newtypes.rst @@ -323,7 +323,7 @@ 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. -As with the :c:member:`~PyTypeObject.tp_methods` table, a sentinel entry with a :c:member:`~PyMethodDef.name` value +As with the :c:member:`~PyTypeObject.tp_methods` table, a sentinel entry with a :c:member:`~PyMethodDef.ml_name` value of ``NULL`` is required. .. XXX Descriptors need to be explained in more detail somewhere, but not here. diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 4021f97e34f7fb..42f1cc052d68af 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -20,7 +20,6 @@ Doc/c-api/structures.rst Doc/c-api/sys.rst Doc/c-api/type.rst Doc/c-api/typeobj.rst -Doc/c-api/unicode.rst Doc/extending/extending.rst Doc/extending/newtypes.rst Doc/glossary.rst diff --git a/Doc/whatsnew/2.2.rst b/Doc/whatsnew/2.2.rst index 7de48a40263034..d9ead57413cbbf 100644 --- a/Doc/whatsnew/2.2.rst +++ b/Doc/whatsnew/2.2.rst @@ -1078,17 +1078,17 @@ code, none of the changes described here will affect you very much. To upgrade an extension module to the new API, perform the following steps: -* Rename :c:func:`Py_TPFLAGS_GC` to :c:func:`PyTPFLAGS_HAVE_GC`. +* Rename :c:macro:`!Py_TPFLAGS_GC` to :c:macro:`Py_TPFLAGS_HAVE_GC`. * Use :c:func:`PyObject_GC_New` or :c:func:`PyObject_GC_NewVar` to allocate objects, and :c:func:`PyObject_GC_Del` to deallocate them. -* Rename :c:func:`PyObject_GC_Init` to :c:func:`PyObject_GC_Track` and - :c:func:`PyObject_GC_Fini` to :c:func:`PyObject_GC_UnTrack`. +* Rename :c:func:`!PyObject_GC_Init` to :c:func:`PyObject_GC_Track` and + :c:func:`!PyObject_GC_Fini` to :c:func:`PyObject_GC_UnTrack`. -* Remove :c:func:`PyGC_HEAD_SIZE` from object size calculations. +* Remove :c:macro:`!PyGC_HEAD_SIZE` from object size calculations. -* Remove calls to :c:func:`PyObject_AS_GC` and :c:func:`PyObject_FROM_GC`. +* Remove calls to :c:func:`!PyObject_AS_GC` and :c:func:`!PyObject_FROM_GC`. * A new ``et`` format sequence was added to :c:func:`PyArg_ParseTuple`; ``et`` takes both a parameter and an encoding name, and converts the parameter to the @@ -1219,7 +1219,7 @@ Some of the more notable changes are: operator, but these features were rarely used and therefore buggy. The :meth:`tolist` method and the :attr:`start`, :attr:`stop`, and :attr:`step` attributes are also being deprecated. At the C level, the fourth argument to - the :c:func:`PyRange_New` function, ``repeat``, has also been deprecated. + the :c:func:`!PyRange_New` function, ``repeat``, has also been deprecated. * There were a bunch of patches to the dictionary implementation, mostly to fix potential core dumps if a dictionary contains objects that sneakily changed diff --git a/Doc/whatsnew/2.3.rst b/Doc/whatsnew/2.3.rst index 4390f245d3ab46..72aef3d269f8d3 100644 --- a/Doc/whatsnew/2.3.rst +++ b/Doc/whatsnew/2.3.rst @@ -1897,7 +1897,7 @@ Changes to Python's build process and to the C API include: but will also mean that you can't get help for Python's built-ins. (Contributed by Gustavo Niemeyer.) -* The :c:func:`PyArg_NoArgs` macro is now deprecated, and code that uses it +* The :c:func:`!PyArg_NoArgs` macro is now deprecated, and code that uses it should be changed. For Python 2.2 and later, the method definition table can specify the :c:macro:`METH_NOARGS` flag, signalling that there are no arguments, and the argument checking can then be removed. If compatibility with pre-2.2 diff --git a/Doc/whatsnew/2.4.rst b/Doc/whatsnew/2.4.rst index 4272928fb67894..ceda03afd5f4f6 100644 --- a/Doc/whatsnew/2.4.rst +++ b/Doc/whatsnew/2.4.rst @@ -1468,7 +1468,7 @@ Some of the changes to Python's build process and to the C API are: *X* is a NaN. (Contributed by Tim Peters.) * C code can avoid unnecessary locking by using the new - :c:func:`PyEval_ThreadsInitialized` function to tell if any thread operations + :c:func:`!PyEval_ThreadsInitialized` function to tell if any thread operations have been performed. If this function returns false, no lock operations are needed. (Contributed by Nick Coghlan.) diff --git a/Doc/whatsnew/2.5.rst b/Doc/whatsnew/2.5.rst index a15eefc2953f40..f58b3ede27facc 100644 --- a/Doc/whatsnew/2.5.rst +++ b/Doc/whatsnew/2.5.rst @@ -2119,9 +2119,9 @@ Changes to Python's build process and to the C API include: the various AST nodes in :file:`Parser/Python.asdl`. A Python script reads this file and generates a set of C structure definitions in :file:`Include/Python-ast.h`. The :c:func:`PyParser_ASTFromString` and - :c:func:`PyParser_ASTFromFile`, defined in :file:`Include/pythonrun.h`, take + :c:func:`!PyParser_ASTFromFile`, defined in :file:`Include/pythonrun.h`, take Python source as input and return the root of an AST representing the contents. - This AST can then be turned into a code object by :c:func:`PyAST_Compile`. For + This AST can then be turned into a code object by :c:func:`!PyAST_Compile`. For more information, read the source code, and then ask questions on python-dev. The AST code was developed under Jeremy Hylton's management, and implemented by @@ -2172,7 +2172,7 @@ Changes to Python's build process and to the C API include: ``Py_LOCAL(type)`` declares the function as returning a value of the specified *type* and uses a fast-calling qualifier. ``Py_LOCAL_INLINE(type)`` does the same thing and also requests the - function be inlined. If :c:func:`PY_LOCAL_AGGRESSIVE` is defined before + function be inlined. If macro :c:macro:`!PY_LOCAL_AGGRESSIVE` is defined before :file:`python.h` is included, a set of more aggressive optimizations are enabled for the module; you should benchmark the results to find out if these optimizations actually make the code faster. (Contributed by Fredrik Lundh at @@ -2181,7 +2181,7 @@ Changes to Python's build process and to the C API include: * ``PyErr_NewException(name, base, dict)`` can now accept a tuple of base classes as its *base* argument. (Contributed by Georg Brandl.) -* The :c:func:`PyErr_Warn` function for issuing warnings is now deprecated in +* The :c:func:`!PyErr_Warn` function for issuing warnings is now deprecated in favour of ``PyErr_WarnEx(category, message, stacklevel)`` which lets you specify the number of stack frames separating this function and the caller. A *stacklevel* of 1 is the function calling :c:func:`PyErr_WarnEx`, 2 is the @@ -2191,7 +2191,7 @@ Changes to Python's build process and to the C API include: compiled with a C++ compiler without errors. (Implemented by Anthony Baxter, Martin von Löwis, Skip Montanaro.) -* The :c:func:`PyRange_New` function was removed. It was never documented, never +* The :c:func:`!PyRange_New` function was removed. It was never documented, never used in the core code, and had dangerously lax error checking. In the unlikely case that your extensions were using it, you can replace it by something like the following:: diff --git a/Doc/whatsnew/2.6.rst b/Doc/whatsnew/2.6.rst index 563fa461d2a868..b975aea7d6894a 100644 --- a/Doc/whatsnew/2.6.rst +++ b/Doc/whatsnew/2.6.rst @@ -977,7 +977,7 @@ can be used to include Unicode characters:: print len(s) # 12 Unicode characters At the C level, Python 3.0 will rename the existing 8-bit -string type, called :c:type:`PyStringObject` in Python 2.x, +string type, called :c:type:`!PyStringObject` in Python 2.x, to :c:type:`PyBytesObject`. Python 2.6 uses ``#define`` to support using the names :c:func:`PyBytesObject`, :c:func:`PyBytes_Check`, :c:func:`PyBytes_FromStringAndSize`, @@ -3012,11 +3012,11 @@ Changes to Python's build process and to the C API include: bug occurred if one thread closed a file object while another thread was reading from or writing to the object. In 2.6 file objects have a reference count, manipulated by the - :c:func:`PyFile_IncUseCount` and :c:func:`PyFile_DecUseCount` + :c:func:`!PyFile_IncUseCount` and :c:func:`!PyFile_DecUseCount` functions. File objects can't be closed unless the reference count - is zero. :c:func:`PyFile_IncUseCount` should be called while the GIL + is zero. :c:func:`!PyFile_IncUseCount` should be called while the GIL is still held, before carrying out an I/O operation using the - ``FILE *`` pointer, and :c:func:`PyFile_DecUseCount` should be called + ``FILE *`` pointer, and :c:func:`!PyFile_DecUseCount` should be called immediately after the GIL is re-acquired. (Contributed by Antoine Pitrou and Gregory P. Smith.) diff --git a/Doc/whatsnew/2.7.rst b/Doc/whatsnew/2.7.rst index bc62a2155f1b63..787fe8468b50d1 100644 --- a/Doc/whatsnew/2.7.rst +++ b/Doc/whatsnew/2.7.rst @@ -2151,7 +2151,7 @@ Changes to Python's build process and to the C API include: * New function: stemming from the rewrite of string-to-float conversion, a new :c:func:`PyOS_string_to_double` function was added. The old - :c:func:`PyOS_ascii_strtod` and :c:func:`PyOS_ascii_atof` functions + :c:func:`!PyOS_ascii_strtod` and :c:func:`!PyOS_ascii_atof` functions are now deprecated. * New function: :c:func:`PySys_SetArgvEx` sets the value of @@ -2194,13 +2194,13 @@ Changes to Python's build process and to the C API include: .. XXX these macros don't seem to be described in the c-api docs. -* Removed function: :c:macro:`PyEval_CallObject` is now only available +* Removed function: :c:func:`!PyEval_CallObject` is now only available as a macro. A function version was being kept around to preserve ABI linking compatibility, but that was in 1997; it can certainly be deleted by now. (Removed by Antoine Pitrou; :issue:`8276`.) -* New format codes: the :c:func:`PyFormat_FromString`, - :c:func:`PyFormat_FromStringV`, and :c:func:`PyErr_Format` functions now +* New format codes: the :c:func:`!PyString_FromFormat`, + :c:func:`!PyString_FromFormatV`, and :c:func:`PyErr_Format` functions now accept ``%lld`` and ``%llu`` format codes for displaying C's :c:expr:`long long` types. (Contributed by Mark Dickinson; :issue:`7228`.) @@ -2539,7 +2539,7 @@ For C extensions: instead of triggering a :exc:`DeprecationWarning` (:issue:`5080`). * Use the new :c:func:`PyOS_string_to_double` function instead of the old - :c:func:`PyOS_ascii_strtod` and :c:func:`PyOS_ascii_atof` functions, + :c:func:`!PyOS_ascii_strtod` and :c:func:`!PyOS_ascii_atof` functions, which are now deprecated. For applications that embed Python: diff --git a/Doc/whatsnew/3.0.rst b/Doc/whatsnew/3.0.rst index 65fcc071a5cf54..4089f4cb4866b9 100644 --- a/Doc/whatsnew/3.0.rst +++ b/Doc/whatsnew/3.0.rst @@ -865,8 +865,8 @@ to the C API. * No more C API support for restricted execution. -* :c:func:`PyNumber_Coerce`, :c:func:`PyNumber_CoerceEx`, - :c:func:`PyMember_Get`, and :c:func:`PyMember_Set` C APIs are removed. +* :c:func:`!PyNumber_Coerce`, :c:func:`!PyNumber_CoerceEx`, + :c:func:`!PyMember_Get`, and :c:func:`!PyMember_Set` C APIs are removed. * New C API :c:func:`PyImport_ImportModuleNoBlock`, works like :c:func:`PyImport_ImportModule` but won't block on the import lock diff --git a/Doc/whatsnew/3.1.rst b/Doc/whatsnew/3.1.rst index ceef622f9f8daa..cc6619c53cd626 100644 --- a/Doc/whatsnew/3.1.rst +++ b/Doc/whatsnew/3.1.rst @@ -501,12 +501,12 @@ Changes to Python's build process and to the C API include: (Contributed by Mark Dickinson and Lisandro Dalcrin; :issue:`5175`.) -* Deprecated :c:func:`PyNumber_Int`. Use :c:func:`PyNumber_Long` instead. +* Deprecated :c:func:`!PyNumber_Int`. Use :c:func:`PyNumber_Long` instead. (Contributed by Mark Dickinson; :issue:`4910`.) * Added a new :c:func:`PyOS_string_to_double` function to replace the - deprecated functions :c:func:`PyOS_ascii_strtod` and :c:func:`PyOS_ascii_atof`. + deprecated functions :c:func:`!PyOS_ascii_strtod` and :c:func:`!PyOS_ascii_atof`. (Contributed by Mark Dickinson; :issue:`5914`.) diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 50b85174ffdef7..9213c9c3e45e1d 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -1818,8 +1818,8 @@ Removed into their code. (Contributed by Dong-hee Na and Terry J. Reedy in :issue:`42299`.) -* Removed the :c:func:`PyModule_GetWarningsModule` function that was useless - now due to the _warnings module was converted to a builtin module in 2.6. +* Removed the :c:func:`!PyModule_GetWarningsModule` function that was useless + now due to the :mod:`!_warnings` module was converted to a builtin module in 2.6. (Contributed by Hai Shi in :issue:`42599`.) * Remove deprecated aliases to :ref:`collections-abstract-base-classes` from diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 9054452fe12d51..5de165a566547a 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -2216,7 +2216,7 @@ New Features * :c:func:`PyBuffer_SizeFromFormat` * :c:func:`PyBuffer_ToContiguous` * :c:func:`PyBuffer_FromContiguous` - * :c:func:`PyBuffer_CopyData` + * :c:func:`PyObject_CopyData` * :c:func:`PyBuffer_IsContiguous` * :c:func:`PyBuffer_FillContiguousStrides` * :c:func:`PyBuffer_FillInfo` @@ -2562,18 +2562,18 @@ Deprecated * Deprecate the following functions to configure the Python initialization: - * :c:func:`PySys_AddWarnOptionUnicode` - * :c:func:`PySys_AddWarnOption` - * :c:func:`PySys_AddXOption` - * :c:func:`PySys_HasWarnOptions` - * :c:func:`PySys_SetArgvEx` - * :c:func:`PySys_SetArgv` - * :c:func:`PySys_SetPath` - * :c:func:`Py_SetPath` - * :c:func:`Py_SetProgramName` - * :c:func:`Py_SetPythonHome` - * :c:func:`Py_SetStandardStreamEncoding` - * :c:func:`_Py_SetProgramFullPath` + * :c:func:`!PySys_AddWarnOptionUnicode` + * :c:func:`!PySys_AddWarnOption` + * :c:func:`!PySys_AddXOption` + * :c:func:`!PySys_HasWarnOptions` + * :c:func:`!PySys_SetArgvEx` + * :c:func:`!PySys_SetArgv` + * :c:func:`!PySys_SetPath` + * :c:func:`!Py_SetPath` + * :c:func:`!Py_SetProgramName` + * :c:func:`!Py_SetPythonHome` + * :c:func:`!Py_SetStandardStreamEncoding` + * :c:func:`!_Py_SetProgramFullPath` Use the new :c:type:`PyConfig` API of the :ref:`Python Initialization Configuration ` instead (:pep:`587`). diff --git a/Doc/whatsnew/3.2.rst b/Doc/whatsnew/3.2.rst index de355c19ac0e48..f372b6be05fcad 100644 --- a/Doc/whatsnew/3.2.rst +++ b/Doc/whatsnew/3.2.rst @@ -2567,7 +2567,7 @@ Changes to Python's build process and to the C API include: to set :data:`sys.argv` without also modifying :data:`sys.path` (:issue:`5753`). -* :c:macro:`PyEval_CallObject` is now only available in macro form. The +* :c:func:`!PyEval_CallObject` is now only available in macro form. The function declaration, which was kept for backwards compatibility reasons, is now removed -- the macro was introduced in 1997 (:issue:`8276`). @@ -2729,15 +2729,15 @@ require changes to your code: (Contributed by Antoine Pitrou, :issue:`10272`.) -* The misleading functions :c:func:`PyEval_AcquireLock()` and - :c:func:`PyEval_ReleaseLock()` have been officially deprecated. The - thread-state aware APIs (such as :c:func:`PyEval_SaveThread()` - and :c:func:`PyEval_RestoreThread()`) should be used instead. +* The misleading functions :c:func:`!PyEval_AcquireLock` and + :c:func:`!PyEval_ReleaseLock` have been officially deprecated. The + thread-state aware APIs (such as :c:func:`PyEval_SaveThread` + and :c:func:`PyEval_RestoreThread`) should be used instead. * Due to security risks, :func:`asyncore.handle_accept` has been deprecated, and a new function, :func:`asyncore.handle_accepted`, was added to replace it. (Contributed by Giampaolo Rodola in :issue:`6706`.) -* Due to the new :term:`GIL` implementation, :c:func:`PyEval_InitThreads()` - cannot be called before :c:func:`Py_Initialize()` anymore. +* Due to the new :term:`GIL` implementation, :c:func:`!PyEval_InitThreads` + cannot be called before :c:func:`Py_Initialize` anymore. diff --git a/Doc/whatsnew/3.3.rst b/Doc/whatsnew/3.3.rst index 91ffc99325a014..e0aa82dcdb8dff 100644 --- a/Doc/whatsnew/3.3.rst +++ b/Doc/whatsnew/3.3.rst @@ -2303,7 +2303,7 @@ Functions and macros manipulating Py_UNICODE* strings: Encoders: -* :c:func:`!PyUnicode_Encode`: use :c:func:`PyUnicode_AsEncodedObject` +* :c:func:`!PyUnicode_Encode`: use :c:func:`!PyUnicode_AsEncodedObject` * :c:func:`!PyUnicode_EncodeUTF7` * :c:func:`!PyUnicode_EncodeUTF8`: use :c:func:`PyUnicode_AsUTF8` or :c:func:`PyUnicode_AsUTF8String` @@ -2461,7 +2461,7 @@ Porting C code -------------- * In the course of changes to the buffer API the undocumented - :c:member:`~Py_buffer.smalltable` member of the + :c:member:`!smalltable` member of the :c:type:`Py_buffer` structure has been removed and the layout of the :c:type:`PyMemoryViewObject` has changed. diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst index 9bd97e4726ca24..6d911096c17398 100644 --- a/Doc/whatsnew/3.5.rst +++ b/Doc/whatsnew/3.5.rst @@ -2512,7 +2512,7 @@ Changes in the Python API Changes in the C API -------------------- -* The undocumented :c:member:`~PyMemoryViewObject.format` member of the +* The undocumented :c:member:`!format` member of the (non-public) :c:type:`PyMemoryViewObject` structure has been removed. All extensions relying on the relevant parts in ``memoryobject.h`` must be rebuilt. @@ -2520,7 +2520,7 @@ Changes in the C API * The :c:type:`PyMemAllocator` structure was renamed to :c:type:`PyMemAllocatorEx` and a new ``calloc`` field was added. -* Removed non-documented macro :c:macro:`PyObject_REPR` which leaked references. +* Removed non-documented macro :c:macro:`!PyObject_REPR()` which leaked references. Use format character ``%R`` in :c:func:`PyUnicode_FromFormat`-like functions to format the :func:`repr` of the object. (Contributed by Serhiy Storchaka in :issue:`22453`.) diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index b3a6f114dd4445..1d2f0a6ff3ca5e 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -1571,12 +1571,12 @@ Build and C API Changes * :c:func:`Py_INCREF`, :c:func:`Py_DECREF` * :c:func:`Py_XINCREF`, :c:func:`Py_XDECREF` * :c:func:`PyObject_INIT`, :c:func:`PyObject_INIT_VAR` - * Private functions: :c:func:`_PyObject_GC_TRACK`, - :c:func:`_PyObject_GC_UNTRACK`, :c:func:`_Py_Dealloc` + * Private functions: :c:func:`!_PyObject_GC_TRACK`, + :c:func:`!_PyObject_GC_UNTRACK`, :c:func:`!_Py_Dealloc` (Contributed by Victor Stinner in :issue:`35059`.) -* The :c:func:`PyByteArray_Init` and :c:func:`PyByteArray_Fini` functions have +* The :c:func:`!PyByteArray_Init` and :c:func:`!PyByteArray_Fini` functions have been removed. They did nothing since Python 2.7.4 and Python 3.2.0, were excluded from the limited API (stable ABI), and were not documented. (Contributed by Victor Stinner in :issue:`35713`.) @@ -1625,7 +1625,7 @@ Build and C API Changes parameter for indicating the number of positional-only arguments. (Contributed by Pablo Galindo in :issue:`37221`.) -* :c:func:`Py_SetPath` now sets :data:`sys.executable` to the program full +* :c:func:`!Py_SetPath` now sets :data:`sys.executable` to the program full path (:c:func:`Py_GetProgramFullPath`) rather than to the program name (:c:func:`Py_GetProgramName`). (Contributed by Victor Stinner in :issue:`38234`.) @@ -1842,11 +1842,11 @@ Changes in Python behavior always use ``sys.platform.startswith('aix')``. (Contributed by M. Felt in :issue:`36588`.) -* :c:func:`PyEval_AcquireLock` and :c:func:`PyEval_AcquireThread` now +* :c:func:`!PyEval_AcquireLock` and :c:func:`!PyEval_AcquireThread` now terminate the current thread if called while the interpreter is finalizing, making them consistent with :c:func:`PyEval_RestoreThread`, :c:func:`Py_END_ALLOW_THREADS`, and :c:func:`PyGILState_Ensure`. If this - behavior is not desired, guard the call by checking :c:func:`_Py_IsFinalizing` + behavior is not desired, guard the call by checking :c:func:`!_Py_IsFinalizing` or :func:`sys.is_finalizing`. (Contributed by Joannah Nanjekye in :issue:`36475`.) @@ -2018,7 +2018,7 @@ Changes in the C API *cf_flags*. (Contributed by Guido van Rossum in :issue:`35766`.) -* The :c:func:`PyEval_ReInitThreads` function has been removed from the C API. +* The :c:func:`!PyEval_ReInitThreads` function has been removed from the C API. It should not be called explicitly: use :c:func:`PyOS_AfterFork_Child` instead. (Contributed by Victor Stinner in :issue:`36728`.) @@ -2118,7 +2118,7 @@ Changes in the C API (Contributed by Antoine Pitrou in :issue:`32388`.) -* The functions :c:func:`PyNode_AddChild` and :c:func:`PyParser_AddToken` now accept +* The functions :c:func:`!PyNode_AddChild` and :c:func:`!PyParser_AddToken` now accept two additional ``int`` arguments *end_lineno* and *end_col_offset*. * The :file:`libpython38.a` file to allow MinGW tools to link directly against diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index 664c8d8a545db0..6829970900930a 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -870,9 +870,9 @@ Deprecated users can leverage the Abstract Syntax Tree (AST) generation and compilation stage, using the :mod:`ast` module. -* The Public C API functions :c:func:`PyParser_SimpleParseStringFlags`, - :c:func:`PyParser_SimpleParseStringFlagsFilename`, - :c:func:`PyParser_SimpleParseFileFlags` and :c:func:`PyNode_Compile` +* The Public C API functions :c:func:`!PyParser_SimpleParseStringFlags`, + :c:func:`!PyParser_SimpleParseStringFlagsFilename`, + :c:func:`!PyParser_SimpleParseFileFlags` and :c:func:`!PyNode_Compile` are deprecated and will be removed in Python 3.10 together with the old parser. * Using :data:`NotImplemented` in a boolean context has been deprecated, @@ -923,10 +923,10 @@ Deprecated (Contributed by Batuhan Taskaya in :issue:`39639` and :issue:`39969` and Serhiy Storchaka in :issue:`39988`.) -* The :c:func:`PyEval_InitThreads` and :c:func:`PyEval_ThreadsInitialized` +* The :c:func:`!PyEval_InitThreads` and :c:func:`!PyEval_ThreadsInitialized` functions are now deprecated and will be removed in Python 3.11. Calling - :c:func:`PyEval_InitThreads` now does nothing. The :term:`GIL` is initialized - by :c:func:`Py_Initialize()` since Python 3.7. + :c:func:`!PyEval_InitThreads` now does nothing. The :term:`GIL` is initialized + by :c:func:`Py_Initialize` since Python 3.7. (Contributed by Victor Stinner in :issue:`39877`.) * Passing ``None`` as the first argument to the :func:`shlex.split` function diff --git a/Misc/NEWS.d/3.11.0a1.rst b/Misc/NEWS.d/3.11.0a1.rst index 17cc1fa7cb7ac1..1fea35c42f26c6 100644 --- a/Misc/NEWS.d/3.11.0a1.rst +++ b/Misc/NEWS.d/3.11.0a1.rst @@ -5036,15 +5036,15 @@ Limited API. Deprecate the following functions to configure the Python initialization: -* :c:func:`PySys_AddWarnOptionUnicode` -* :c:func:`PySys_AddWarnOption` -* :c:func:`PySys_AddXOption` -* :c:func:`PySys_HasWarnOptions` -* :c:func:`Py_SetPath` -* :c:func:`Py_SetProgramName` -* :c:func:`Py_SetPythonHome` -* :c:func:`Py_SetStandardStreamEncoding` -* :c:func:`_Py_SetProgramFullPath` +* :c:func:`!PySys_AddWarnOptionUnicode` +* :c:func:`!PySys_AddWarnOption` +* :c:func:`!PySys_AddXOption` +* :c:func:`!PySys_HasWarnOptions` +* :c:func:`!Py_SetPath` +* :c:func:`!Py_SetProgramName` +* :c:func:`!Py_SetPythonHome` +* :c:func:`!Py_SetStandardStreamEncoding` +* :c:func:`!_Py_SetProgramFullPath` Use the new :c:type:`PyConfig` API of the :ref:`Python Initialization Configuration ` instead (:pep:`587`). diff --git a/Misc/NEWS.d/3.8.0a1.rst b/Misc/NEWS.d/3.8.0a1.rst index 5f1a0142ae7781..85a5447ceb5f28 100644 --- a/Misc/NEWS.d/3.8.0a1.rst +++ b/Misc/NEWS.d/3.8.0a1.rst @@ -8765,7 +8765,7 @@ for relative path to files in current directory. .. nonce: fmehdG .. section: C API -The :c:func:`PyByteArray_Init` and :c:func:`PyByteArray_Fini` functions have +The :c:func:`!PyByteArray_Init` and :c:func:`!PyByteArray_Fini` functions have been removed. They did nothing since Python 2.7.4 and Python 3.2.0, were excluded from the limited API (stable ABI), and were not documented. @@ -8836,7 +8836,7 @@ Py_LIMITED_API. Patch by Arthur Neufeld. .. nonce: gFd85N .. section: C API -The :c:func:`_PyObject_GC_TRACK` and :c:func:`_PyObject_GC_UNTRACK` macros +The :c:func:`!_PyObject_GC_TRACK` and :c:func:`!_PyObject_GC_UNTRACK` macros have been removed from the public C API. .. diff --git a/Misc/NEWS.d/3.8.0a4.rst b/Misc/NEWS.d/3.8.0a4.rst index 17bbac6238913c..00079c8491fe6a 100644 --- a/Misc/NEWS.d/3.8.0a4.rst +++ b/Misc/NEWS.d/3.8.0a4.rst @@ -124,7 +124,7 @@ Galindo. .. nonce: CjRps3 .. section: Core and Builtins -:c:func:`PyEval_AcquireLock` and :c:func:`PyEval_AcquireThread` now +:c:func:`!PyEval_AcquireLock` and :c:func:`!PyEval_AcquireThread` now terminate the current thread if called while the interpreter is finalizing, making them consistent with :c:func:`PyEval_RestoreThread`, :c:func:`Py_END_ALLOW_THREADS`, and :c:func:`PyGILState_Ensure`. diff --git a/Misc/NEWS.d/3.8.0b1.rst b/Misc/NEWS.d/3.8.0b1.rst index 5285770de13186..f8f180bc731fd5 100644 --- a/Misc/NEWS.d/3.8.0b1.rst +++ b/Misc/NEWS.d/3.8.0b1.rst @@ -2047,6 +2047,6 @@ unbound methods. These are objects supporting the optimization given by the .. nonce: FR-dMP .. section: C API -The :c:func:`PyEval_ReInitThreads` function has been removed from the C API. +The :c:func:`!PyEval_ReInitThreads` function has been removed from the C API. It should not be called explicitly: use :c:func:`PyOS_AfterFork_Child` instead. diff --git a/Misc/NEWS.d/3.9.0a1.rst b/Misc/NEWS.d/3.9.0a1.rst index 64410a1d34dfeb..41ab33fac3322d 100644 --- a/Misc/NEWS.d/3.9.0a1.rst +++ b/Misc/NEWS.d/3.9.0a1.rst @@ -5535,7 +5535,7 @@ Tyler Kieft. .. nonce: d0bhEA .. section: C API -:c:func:`Py_SetPath` now sets :data:`sys.executable` to the program full +:c:func:`!Py_SetPath` now sets :data:`sys.executable` to the program full path (:c:func:`Py_GetProgramFullPath`) rather than to the program name (:c:func:`Py_GetProgramName`). @@ -5546,8 +5546,8 @@ path (:c:func:`Py_GetProgramFullPath`) rather than to the program name .. nonce: ZbquVK .. section: C API -Python ignored arguments passed to :c:func:`Py_SetPath`, -:c:func:`Py_SetPythonHome` and :c:func:`Py_SetProgramName`: fix Python +Python ignored arguments passed to :c:func:`!Py_SetPath`, +:c:func:`!Py_SetPythonHome` and :c:func:`!Py_SetProgramName`: fix Python initialization to use specified arguments. .. diff --git a/Misc/NEWS.d/3.9.0a5.rst b/Misc/NEWS.d/3.9.0a5.rst index a129e721a4cf90..8c6057aa3c8a4b 100644 --- a/Misc/NEWS.d/3.9.0a5.rst +++ b/Misc/NEWS.d/3.9.0a5.rst @@ -1234,8 +1234,8 @@ method name in the SystemError "bad call flags" error message to ease debug. .. nonce: GOYtIm .. section: C API -Deprecated :c:func:`PyEval_InitThreads` and -:c:func:`PyEval_ThreadsInitialized`. Calling :c:func:`PyEval_InitThreads` +Deprecated :c:func:`!PyEval_InitThreads` and +:c:func:`!PyEval_ThreadsInitialized`. Calling :c:func:`!PyEval_InitThreads` now does nothing. .. From 9d7f2705b51eb7a4c48799ea16786f4a69f57dc5 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 22 Aug 2023 11:33:26 -0700 Subject: [PATCH 193/632] [3.11] gh-107700: [Enum] Document that `EnumType` was added in 3.11 (GH-108260) (#108300) (cherry picked from commit e8ef0bdd8c613a722bf7965bf1da912882141a52) Co-authored-by: Nikita Sobolev --- Doc/library/enum.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index 15b8e2918d7544..85da33156b1e38 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -247,6 +247,10 @@ Data Types >>> list(reversed(Color)) [, , ] + .. versionadded:: 3.11 + + Before 3.11 ``enum`` used ``EnumMeta`` type, which is kept as an alias. + .. class:: Enum From fadf8a032b8b602ed2681aa912ceab01d095fd8f Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 22 Aug 2023 12:23:55 -0700 Subject: [PATCH 194/632] [3.11] Clarify how topics.py gets created. (GH-106121) (GH-106580) When changing docs, it was easy to find text in topics.py, and I wondered whether I was supposed to edit it. Thankfully, the top of the file says it's auto-generated, so I knew I didn't have to edit it. But I didn't know what started the auto-generation process. It's part of the release process, so I'll leave a note here for future editors. (cherry picked from commit dac1e364901d3668742e6eecc2ce63586330c11f) Co-authored-by: Ned Batchelder --- Doc/tools/extensions/pyspecific.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Doc/tools/extensions/pyspecific.py b/Doc/tools/extensions/pyspecific.py index d13a9dfd4c8eb8..31c42e9d9d3a63 100644 --- a/Doc/tools/extensions/pyspecific.py +++ b/Doc/tools/extensions/pyspecific.py @@ -562,6 +562,7 @@ def finish(self): try: f.write('# -*- coding: utf-8 -*-\n'.encode('utf-8')) f.write(('# Autogenerated by Sphinx on %s\n' % asctime()).encode('utf-8')) + f.write('# as part of the release process.\n'.encode('utf-8')) f.write(('topics = ' + pformat(self.topics) + '\n').encode('utf-8')) finally: f.close() From 12cad6155bb1291b4b6c111ef480bbc0e6a68941 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 22 Aug 2023 12:52:51 -0700 Subject: [PATCH 195/632] [3.11] Docs: Add link to skip to datetime's format codes (GH-108027) (#108330) Co-authored-by: Hugo van Kemenade --- Doc/library/datetime.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index 9711f944e90dc3..d7b0401b7feef8 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -19,6 +19,10 @@ The :mod:`datetime` module supplies classes for manipulating dates and times. While date and time arithmetic is supported, the focus of the implementation is on efficient attribute extraction for output formatting and manipulation. +.. tip:: + + Skip to :ref:`the format codes `. + .. seealso:: Module :mod:`calendar` @@ -2314,6 +2318,8 @@ versus :meth:`strptime`: +----------------+--------------------------------------------------------+------------------------------------------------------------------------------+ + .. _format-codes: + :meth:`strftime` and :meth:`strptime` Format Codes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From a15396146f8b4f7196e858eba577730f66ac3fb6 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 23 Aug 2023 05:11:53 +0200 Subject: [PATCH 196/632] [3.11] gh-108303: Add Lib/test/test_cppext/ sub-directory (#108325) (#108336) gh-108303: Add Lib/test/test_cppext/ sub-directory (#108325) * Move test_cppext to its own directory * Rename setup_testcppext.py to setup.py * Rename _testcppext.cpp to extension.cpp * The source (extension.cpp) is now also copied by the test. (cherry picked from commit 21dda09600848ac280481f7c64f8d9516dc69bb2) --- Lib/test/{test_cppext.py => test_cppext/__init__.py} | 8 +++----- Lib/test/{_testcppext.cpp => test_cppext/extension.cpp} | 2 ++ Lib/test/{setup_testcppext.py => test_cppext/setup.py} | 4 ++-- Makefile.pre.in | 1 + 4 files changed, 8 insertions(+), 7 deletions(-) rename Lib/test/{test_cppext.py => test_cppext/__init__.py} (95%) rename Lib/test/{_testcppext.cpp => test_cppext/extension.cpp} (99%) rename Lib/test/{setup_testcppext.py => test_cppext/setup.py} (93%) diff --git a/Lib/test/test_cppext.py b/Lib/test/test_cppext/__init__.py similarity index 95% rename from Lib/test/test_cppext.py rename to Lib/test/test_cppext/__init__.py index 465894d24e7dfc..4ce29b7ff2c23e 100644 --- a/Lib/test/test_cppext.py +++ b/Lib/test/test_cppext/__init__.py @@ -10,9 +10,7 @@ MS_WINDOWS = (sys.platform == 'win32') - - -SETUP_TESTCPPEXT = support.findfile('setup_testcppext.py') +SETUP = os.path.join(os.path.dirname(__file__), 'setup.py') @support.requires_subprocess() @@ -74,14 +72,14 @@ def run_cmd(operation, cmd): # Build the C++ extension cmd = [python, '-X', 'dev', - SETUP_TESTCPPEXT, 'build_ext', '--verbose'] + SETUP, 'build_ext', '--verbose'] if std_cpp03: cmd.append('-std=c++03') run_cmd('Build', cmd) # Install the C++ extension cmd = [python, '-X', 'dev', - SETUP_TESTCPPEXT, 'install'] + SETUP, 'install'] run_cmd('Install', cmd) # Do a reference run. Until we test that running python diff --git a/Lib/test/_testcppext.cpp b/Lib/test/test_cppext/extension.cpp similarity index 99% rename from Lib/test/_testcppext.cpp rename to Lib/test/test_cppext/extension.cpp index 0e381a78c5ceed..58d18ea6759330 100644 --- a/Lib/test/_testcppext.cpp +++ b/Lib/test/test_cppext/extension.cpp @@ -1,5 +1,7 @@ // gh-91321: Very basic C++ test extension to check that the Python C API is // compatible with C++ and does not emit C++ compiler warnings. +// +// The code is only built, not executed. // Always enable assertions #undef NDEBUG diff --git a/Lib/test/setup_testcppext.py b/Lib/test/test_cppext/setup.py similarity index 93% rename from Lib/test/setup_testcppext.py rename to Lib/test/test_cppext/setup.py index c6b68104d1333c..dac3a96609ebcf 100644 --- a/Lib/test/setup_testcppext.py +++ b/Lib/test/test_cppext/setup.py @@ -1,7 +1,7 @@ # gh-91321: Build a basic C++ test extension to check that the Python C API is # compatible with C++ and does not emit C++ compiler warnings. +import os.path import sys -from test import support from setuptools import setup, Extension @@ -9,7 +9,7 @@ MS_WINDOWS = (sys.platform == 'win32') -SOURCE = support.findfile('_testcppext.cpp') +SOURCE = os.path.join(os.path.dirname(__file__), 'extension.cpp') if not MS_WINDOWS: # C++ compiler flags for GCC and clang CPPFLAGS = [ diff --git a/Makefile.pre.in b/Makefile.pre.in index 6405d06afe14e1..884bea1841c232 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1959,6 +1959,7 @@ TESTSUBDIRS= ctypes/test \ test/support \ test/test_asyncio \ test/test_capi \ + test/test_cppext \ test/test_email \ test/test_email/data \ test/test_import \ From 1aff195903ca664b5285f7ac9e999347484d9d3b Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 23 Aug 2023 05:47:41 +0200 Subject: [PATCH 197/632] [3.11] gh-105776: Fix test_cppext when CC contains -std=c11 option (#108343) (#108347) gh-105776: Fix test_cppext when CC contains -std=c11 option (#108343) Fix test_cppext when the C compiler command has the "-std=c11" option. Remove "-std=" options from the compiler command. (cherry picked from commit 9173b2bbe13aeccc075b571da05c653a2a91de1b) --- Lib/test/test_cppext/setup.py | 13 +++++++++++++ .../2023-08-23-04-08-18.gh-issue-105776.oE6wp_.rst | 2 ++ 2 files changed, 15 insertions(+) create mode 100644 Misc/NEWS.d/next/Tests/2023-08-23-04-08-18.gh-issue-105776.oE6wp_.rst diff --git a/Lib/test/test_cppext/setup.py b/Lib/test/test_cppext/setup.py index dac3a96609ebcf..f74124775acd06 100644 --- a/Lib/test/test_cppext/setup.py +++ b/Lib/test/test_cppext/setup.py @@ -1,7 +1,9 @@ # gh-91321: Build a basic C++ test extension to check that the Python C API is # compatible with C++ and does not emit C++ compiler warnings. import os.path +import shlex import sys +import sysconfig from setuptools import setup, Extension @@ -36,6 +38,17 @@ def main(): cppflags = [*CPPFLAGS, f'-std={std}'] + # gh-105776: When "gcc -std=11" is used as the C++ compiler, -std=c11 + # option emits a C++ compiler warning. Remove "-std11" option from the + # CC command. + cmd = (sysconfig.get_config_var('CC') or '') + if cmd is not None: + cmd = shlex.split(cmd) + cmd = [arg for arg in cmd if not arg.startswith('-std=')] + cmd = shlex.join(cmd) + # CC env var overrides sysconfig CC variable in setuptools + os.environ['CC'] = cmd + cpp_ext = Extension( name, sources=[SOURCE], diff --git a/Misc/NEWS.d/next/Tests/2023-08-23-04-08-18.gh-issue-105776.oE6wp_.rst b/Misc/NEWS.d/next/Tests/2023-08-23-04-08-18.gh-issue-105776.oE6wp_.rst new file mode 100644 index 00000000000000..0e0a3aa9b11e68 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-08-23-04-08-18.gh-issue-105776.oE6wp_.rst @@ -0,0 +1,2 @@ +Fix test_cppext when the C compiler command ``-std=c11`` option: remove +``-std=`` options from the compiler command. Patch by Victor Stinner. From 1ecbe787dde59b3d021990dbf92680f0301977ce Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 23 Aug 2023 00:20:57 -0700 Subject: [PATCH 198/632] [3.11] gh-108267: Dataclasses docs: Fix object.__setattr__ typo (GH-108355) (#108357) gh-108267: Dataclasses docs: Fix object.__setattr__ typo (GH-108355) Fixed a sentence in dataclasses.rst Changed "__setattr__" to "object.__setattr__" in a section that was specifically supposed to refer to the __setattr__ method of the object class. Also suppressed the link to the data model docs for __setattr__, since we're talking about a specific __setattr__ implementation, not __setattr__ methods in general. (cherry picked from commit 79fdacc0059a3959074d2d9d054653eae1dcfe06) Co-authored-by: FrozenBob <30644137+FrozenBob@users.noreply.github.com> --- Doc/library/dataclasses.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/dataclasses.rst b/Doc/library/dataclasses.rst index aa71e5e735f2c5..09f4ce812dc83a 100644 --- a/Doc/library/dataclasses.rst +++ b/Doc/library/dataclasses.rst @@ -604,7 +604,7 @@ methods will raise a :exc:`FrozenInstanceError` when invoked. There is a tiny performance penalty when using ``frozen=True``: :meth:`~object.__init__` cannot use simple assignment to initialize fields, and -must use :meth:`~object.__setattr__`. +must use :meth:`!object.__setattr__`. Inheritance ----------- From 07c727e92a72ea8a9e7d9705b4afc261c32fa1ce Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 23 Aug 2023 02:01:30 -0700 Subject: [PATCH 199/632] [3.11] gh-107136: Remove Plausible for docs metrics (GH-107856) (#108335) (cherry picked from commit fc23f34cc9701949e6832eb32f26ea89f6622b82) Co-authored-by: Hugo van Kemenade --- Doc/tools/templates/layout.html | 3 --- 1 file changed, 3 deletions(-) diff --git a/Doc/tools/templates/layout.html b/Doc/tools/templates/layout.html index 80103158ea01e6..9498b2ccc5af92 100644 --- a/Doc/tools/templates/layout.html +++ b/Doc/tools/templates/layout.html @@ -26,9 +26,6 @@ {% endblock %} {% block extrahead %} - {% if builder == "html" %} - - {% endif %} {% if builder != "htmlhelp" %} {% if pagename == 'whatsnew/changelog' and not embedded %} From 93714b7db795b14b26adffde30753cfda0ca4867 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 23 Aug 2023 03:10:04 -0700 Subject: [PATCH 200/632] [3.11] gh-108342: Break ref cycle in SSLSocket._create() exc (GH-108344) (#108349) Explicitly break a reference cycle when SSLSocket._create() raises an exception. Clear the variable storing the exception, since the exception traceback contains the variables and so creates a reference cycle. This test leak was introduced by the test added for the fix of GH-108310. (cherry picked from commit 64f99350351bc46e016b2286f36ba7cd669b79e3) Co-authored-by: Victor Stinner --- Lib/ssl.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Lib/ssl.py b/Lib/ssl.py index ced87d406995c1..48d229f810af28 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -1083,7 +1083,11 @@ def _create(cls, sock, server_side=False, do_handshake_on_connect=True, self.close() except OSError: pass - raise notconn_pre_handshake_data_error + try: + raise notconn_pre_handshake_data_error + finally: + # Explicitly break the reference cycle. + notconn_pre_handshake_data_error = None else: connected = True From 34ef75d3ef559288900fad008f05b29155eb8b59 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 23 Aug 2023 14:11:20 -0700 Subject: [PATCH 201/632] [3.11] gh-77377: Ensure multiprocessing SemLock is valid for spawn-based Process before serializing it (GH-107275) (#108378) gh-77377: Ensure multiprocessing SemLock is valid for spawn-based Process before serializing it (GH-107275) Ensure multiprocessing SemLock is valid for spawn Process before serializing it. Creating a multiprocessing SemLock with a fork context, and then trying to pass it to a spawn-created Process, would segfault if not detected early. --------- (cherry picked from commit 1700d34d314f5304a7a75363bda295a8c15c371f) Co-authored-by: albanD Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Antoine Pitrou --- Lib/multiprocessing/synchronize.py | 9 ++++++-- Lib/test/_test_multiprocessing.py | 22 +++++++++++++++++++ ...3-07-25-22-35-35.gh-issue-77377.EHAbXx.rst | 1 + 3 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-07-25-22-35-35.gh-issue-77377.EHAbXx.rst diff --git a/Lib/multiprocessing/synchronize.py b/Lib/multiprocessing/synchronize.py index 42624b543601a1..2328d332123082 100644 --- a/Lib/multiprocessing/synchronize.py +++ b/Lib/multiprocessing/synchronize.py @@ -50,8 +50,8 @@ class SemLock(object): def __init__(self, kind, value, maxvalue, *, ctx): if ctx is None: ctx = context._default_context.get_context() - name = ctx.get_start_method() - unlink_now = sys.platform == 'win32' or name == 'fork' + self.is_fork_ctx = ctx.get_start_method() == 'fork' + unlink_now = sys.platform == 'win32' or self.is_fork_ctx for i in range(100): try: sl = self._semlock = _multiprocessing.SemLock( @@ -103,6 +103,11 @@ def __getstate__(self): if sys.platform == 'win32': h = context.get_spawning_popen().duplicate_for_child(sl.handle) else: + if self.is_fork_ctx: + raise RuntimeError('A SemLock created in a fork context is being ' + 'shared with a process in a spawn context. This is ' + 'not supported. Please use the same context to create ' + 'multiprocessing objects and Process.') h = sl.handle return (h, sl.kind, sl.maxvalue, sl.name) diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 29dc3863766497..9bc8242cbfa26a 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -5345,6 +5345,28 @@ def test_preload_resources(self): print(err) self.fail("failed spawning forkserver or grandchild") + @unittest.skipIf(sys.platform == "win32", + "Only Spawn on windows so no risk of mixing") + @only_run_in_spawn_testsuite("avoids redundant testing.") + def test_mixed_startmethod(self): + # Fork-based locks cannot be used with spawned process + for process_method in ["spawn", "forkserver"]: + queue = multiprocessing.get_context("fork").Queue() + process_ctx = multiprocessing.get_context(process_method) + p = process_ctx.Process(target=close_queue, args=(queue,)) + err_msg = "A SemLock created in a fork" + with self.assertRaisesRegex(RuntimeError, err_msg): + p.start() + + # non-fork-based locks can be used with all other start methods + for queue_method in ["spawn", "forkserver"]: + for process_method in multiprocessing.get_all_start_methods(): + queue = multiprocessing.get_context(queue_method).Queue() + process_ctx = multiprocessing.get_context(process_method) + p = process_ctx.Process(target=close_queue, args=(queue,)) + p.start() + p.join() + @unittest.skipIf(sys.platform == "win32", "test semantics don't make sense on Windows") diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-07-25-22-35-35.gh-issue-77377.EHAbXx.rst b/Misc/NEWS.d/next/Core and Builtins/2023-07-25-22-35-35.gh-issue-77377.EHAbXx.rst new file mode 100644 index 00000000000000..194851dea13352 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-07-25-22-35-35.gh-issue-77377.EHAbXx.rst @@ -0,0 +1 @@ +Ensure that multiprocessing synchronization objects created in a fork context are not sent to a different process created in a spawn context. This changes a segfault into an actionable RuntimeError in the parent process. From 869dc14a2ebabaa6dcf7c727e6346616f7798906 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 23 Aug 2023 15:10:27 -0700 Subject: [PATCH 202/632] [3.11] gh-70766: Mention the object getstate caveat in 3.11 What's new. (GH-108379) (#108385) gh-70766: Mention the object getstate caveat in 3.11 What's new. (GH-108379) (cherry picked from commit b6be18812c68fce5ab56c266dc5fc5a3cceb09c0) Co-authored-by: Gregory P. Smith --- Doc/whatsnew/3.11.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 5de165a566547a..af7162128ebbc2 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -459,6 +459,10 @@ Other Language Changes :class:`collections.OrderedDict`, :class:`collections.deque`, :class:`weakref.WeakSet`, and :class:`datetime.tzinfo` now copies and pickles instance attributes implemented as :term:`slots <__slots__>`. + This change has an unintended side effect: It trips up a small minority + of existing Python projects not expecting :meth:`object.__getstate__` to + exist. See the later comments on :gh:`70766` for discussions of what + workarounds such code may need. (Contributed by Serhiy Storchaka in :issue:`26579`.) .. _whatsnew311-pythonsafepath: From d90f2f3a6292614ce8ae22a15694dcb676bc8c36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Thu, 24 Aug 2023 12:08:52 +0200 Subject: [PATCH 203/632] [3.11] gh-108342: Make ssl TestPreHandshakeClose more reliable (GH-108370) (#108405) * In preauth tests of test_ssl, explicitly break reference cycles invoving SingleConnectionTestServerThread to make sure that the thread is deleted. Otherwise, the test marks the environment as altered because the threading module sees a "dangling thread" (SingleConnectionTestServerThread). This test leak was introduced by the test added for the fix of issue gh-108310. * Use support.SHORT_TIMEOUT instead of hardcoded 1.0 or 2.0 seconds timeout. * SingleConnectionTestServerThread.run() catchs TimeoutError * Fix a race condition (missing synchronization) in test_preauth_data_to_tls_client(): the server now waits until the client connect() completed in call_after_accept(). * test_https_client_non_tls_response_ignored() calls server.join() explicitly. * Replace "localhost" with server.listener.getsockname()[0]. (cherry picked from commit 592bacb6fc0833336c0453e818e9b95016e9fd47) Co-authored-by: Victor Stinner --- Lib/test/test_ssl.py | 102 ++++++++++++++++++++++++++++++------------- 1 file changed, 71 insertions(+), 31 deletions(-) diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 4a2508988c0f76..d6acbf36e6e605 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -4909,12 +4909,16 @@ class TestPreHandshakeClose(unittest.TestCase): class SingleConnectionTestServerThread(threading.Thread): - def __init__(self, *, name, call_after_accept): + def __init__(self, *, name, call_after_accept, timeout=None): self.call_after_accept = call_after_accept self.received_data = b'' # set by .run() self.wrap_error = None # set by .run() self.listener = None # set by .start() self.port = None # set by .start() + if timeout is None: + self.timeout = support.SHORT_TIMEOUT + else: + self.timeout = timeout super().__init__(name=name) def __enter__(self): @@ -4937,13 +4941,19 @@ def start(self): self.ssl_ctx.load_cert_chain(certfile=ONLYCERT, keyfile=ONLYKEY) self.listener = socket.socket() self.port = socket_helper.bind_port(self.listener) - self.listener.settimeout(2.0) + self.listener.settimeout(self.timeout) self.listener.listen(1) super().start() def run(self): - conn, address = self.listener.accept() - self.listener.close() + try: + conn, address = self.listener.accept() + except TimeoutError: + # on timeout, just close the listener + return + finally: + self.listener.close() + with conn: if self.call_after_accept(conn): return @@ -4971,8 +4981,13 @@ def non_linux_skip_if_other_okay_error(self, err): # we're specifically trying to test. The way this test is written # is known to work on Linux. We'll skip it anywhere else that it # does not present as doing so. - self.skipTest(f"Could not recreate conditions on {sys.platform}:" - f" {err=}") + try: + self.skipTest(f"Could not recreate conditions on {sys.platform}:" + f" {err=}") + finally: + # gh-108342: Explicitly break the reference cycle + err = None + # If maintaining this conditional winds up being a problem. # just turn this into an unconditional skip anything but Linux. # The important thing is that our CI has the logic covered. @@ -4983,7 +4998,7 @@ def test_preauth_data_to_tls_server(self): def call_after_accept(unused): server_accept_called.set() - if not ready_for_server_wrap_socket.wait(2.0): + if not ready_for_server_wrap_socket.wait(support.SHORT_TIMEOUT): raise RuntimeError("wrap_socket event never set, test may fail.") return False # Tell the server thread to continue. @@ -5004,20 +5019,31 @@ def call_after_accept(unused): ready_for_server_wrap_socket.set() server.join() + wrap_error = server.wrap_error - self.assertEqual(b"", server.received_data) - self.assertIsInstance(wrap_error, OSError) # All platforms. - self.non_linux_skip_if_other_okay_error(wrap_error) - self.assertIsInstance(wrap_error, ssl.SSLError) - self.assertIn("before TLS handshake with data", wrap_error.args[1]) - self.assertIn("before TLS handshake with data", wrap_error.reason) - self.assertNotEqual(0, wrap_error.args[0]) - self.assertIsNone(wrap_error.library, msg="attr must exist") + server.wrap_error = None + try: + self.assertEqual(b"", server.received_data) + self.assertIsInstance(wrap_error, OSError) # All platforms. + self.non_linux_skip_if_other_okay_error(wrap_error) + self.assertIsInstance(wrap_error, ssl.SSLError) + self.assertIn("before TLS handshake with data", wrap_error.args[1]) + self.assertIn("before TLS handshake with data", wrap_error.reason) + self.assertNotEqual(0, wrap_error.args[0]) + self.assertIsNone(wrap_error.library, msg="attr must exist") + finally: + # gh-108342: Explicitly break the reference cycle + wrap_error = None + server = None def test_preauth_data_to_tls_client(self): + server_can_continue_with_wrap_socket = threading.Event() client_can_continue_with_wrap_socket = threading.Event() def call_after_accept(conn_to_client): + if not server_can_continue_with_wrap_socket.wait(support.SHORT_TIMEOUT): + print("ERROR: test client took too long") + # This forces an immediate connection close via RST on .close(). set_socket_so_linger_on_with_zero_timeout(conn_to_client) conn_to_client.send( @@ -5037,8 +5063,10 @@ def call_after_accept(conn_to_client): with socket.socket() as client: client.connect(server.listener.getsockname()) - if not client_can_continue_with_wrap_socket.wait(2.0): - self.fail("test server took too long.") + server_can_continue_with_wrap_socket.set() + + if not client_can_continue_with_wrap_socket.wait(support.SHORT_TIMEOUT): + self.fail("test server took too long") ssl_ctx = ssl.create_default_context() try: tls_client = ssl_ctx.wrap_socket( @@ -5052,24 +5080,31 @@ def call_after_accept(conn_to_client): tls_client.close() server.join() - self.assertEqual(b"", received_data) - self.assertIsInstance(wrap_error, OSError) # All platforms. - self.non_linux_skip_if_other_okay_error(wrap_error) - self.assertIsInstance(wrap_error, ssl.SSLError) - self.assertIn("before TLS handshake with data", wrap_error.args[1]) - self.assertIn("before TLS handshake with data", wrap_error.reason) - self.assertNotEqual(0, wrap_error.args[0]) - self.assertIsNone(wrap_error.library, msg="attr must exist") + try: + self.assertEqual(b"", received_data) + self.assertIsInstance(wrap_error, OSError) # All platforms. + self.non_linux_skip_if_other_okay_error(wrap_error) + self.assertIsInstance(wrap_error, ssl.SSLError) + self.assertIn("before TLS handshake with data", wrap_error.args[1]) + self.assertIn("before TLS handshake with data", wrap_error.reason) + self.assertNotEqual(0, wrap_error.args[0]) + self.assertIsNone(wrap_error.library, msg="attr must exist") + finally: + # gh-108342: Explicitly break the reference cycle + wrap_error = None + server = None def test_https_client_non_tls_response_ignored(self): - server_responding = threading.Event() class SynchronizedHTTPSConnection(http.client.HTTPSConnection): def connect(self): + # Call clear text HTTP connect(), not the encrypted HTTPS (TLS) + # connect(): wrap_socket() is called manually below. http.client.HTTPConnection.connect(self) + # Wait for our fault injection server to have done its thing. - if not server_responding.wait(1.0) and support.verbose: + if not server_responding.wait(support.SHORT_TIMEOUT) and support.verbose: sys.stdout.write("server_responding event never set.") self.sock = self._context.wrap_socket( self.sock, server_hostname=self.host) @@ -5084,28 +5119,33 @@ def call_after_accept(conn_to_client): server_responding.set() return True # Tell the server to stop. + timeout = 2.0 server = self.SingleConnectionTestServerThread( call_after_accept=call_after_accept, - name="non_tls_http_RST_responder") + name="non_tls_http_RST_responder", + timeout=timeout) self.enterContext(server) # starts it & unittest.TestCase stops it. # Redundant; call_after_accept sets SO_LINGER on the accepted conn. set_socket_so_linger_on_with_zero_timeout(server.listener) connection = SynchronizedHTTPSConnection( - f"localhost", + server.listener.getsockname()[0], port=server.port, context=ssl.create_default_context(), - timeout=2.0, + timeout=timeout, ) + # There are lots of reasons this raises as desired, long before this # test was added. Sending the request requires a successful TLS wrapped # socket; that fails if the connection is broken. It may seem pointless # to test this. It serves as an illustration of something that we never # want to happen... properly not happening. - with self.assertRaises(OSError) as err_ctx: + with self.assertRaises(OSError): connection.request("HEAD", "/test", headers={"Host": "localhost"}) response = connection.getresponse() + server.join() + class TestEnumerations(unittest.TestCase): From c3d129f07eeddf3b9eaabc4510f1c62bc0e58f6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Thu, 24 Aug 2023 13:26:19 +0200 Subject: [PATCH 204/632] [3.11] Revert "gh-46376: Return existing pointer when possible in ctypes (GH-107131) (GH-107488)" (#108412) This reverts commit 57f27e444175a8a5ffcd86971e06de61c1c38628. The fix caused gh-107940. Until we have a bulletproof fix for that, the 3.11 backport needs to be reverted to make way for 3.11.5. --- Lib/ctypes/test/test_keeprefs.py | 27 ----------------- ...3-07-24-01-21-16.gh-issue-46376.w-xuDL.rst | 1 - Modules/_ctypes/_ctypes.c | 29 ------------------- 3 files changed, 57 deletions(-) delete mode 100644 Misc/NEWS.d/next/Library/2023-07-24-01-21-16.gh-issue-46376.w-xuDL.rst diff --git a/Lib/ctypes/test/test_keeprefs.py b/Lib/ctypes/test/test_keeprefs.py index 61650ad1704753..e20adc7696f501 100644 --- a/Lib/ctypes/test/test_keeprefs.py +++ b/Lib/ctypes/test/test_keeprefs.py @@ -93,33 +93,6 @@ def test_p_cint(self): x = pointer(i) self.assertEqual(x._objects, {'1': i}) - def test_pp_ownership(self): - d = c_int(123) - n = c_int(456) - - p = pointer(d) - pp = pointer(p) - - self.assertIs(pp._objects['1'], p) - self.assertIs(pp._objects['0']['1'], d) - - pp.contents.contents = n - - self.assertIs(pp._objects['1'], p) - self.assertIs(pp._objects['0']['1'], n) - - self.assertIs(p._objects['1'], n) - self.assertEqual(len(p._objects), 1) - - del d - del p - - self.assertIs(pp._objects['0']['1'], n) - self.assertEqual(len(pp._objects), 2) - - del n - - self.assertEqual(len(pp._objects), 2) class PointerToStructure(unittest.TestCase): def test(self): diff --git a/Misc/NEWS.d/next/Library/2023-07-24-01-21-16.gh-issue-46376.w-xuDL.rst b/Misc/NEWS.d/next/Library/2023-07-24-01-21-16.gh-issue-46376.w-xuDL.rst deleted file mode 100644 index 8e8f0245b4539b..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-07-24-01-21-16.gh-issue-46376.w-xuDL.rst +++ /dev/null @@ -1 +0,0 @@ -Prevent memory leak and use-after-free when using pointers to pointers with ctypes diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 9cc518132f92f5..fc73264ff5a32c 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -5158,8 +5158,6 @@ static PyObject * Pointer_get_contents(CDataObject *self, void *closure) { StgDictObject *stgdict; - PyObject *keep, *ptr_probe; - CDataObject *ptr2ptr; if (*(void **)self->b_ptr == NULL) { PyErr_SetString(PyExc_ValueError, @@ -5169,33 +5167,6 @@ Pointer_get_contents(CDataObject *self, void *closure) stgdict = PyObject_stgdict((PyObject *)self); assert(stgdict); /* Cannot be NULL for pointer instances */ - - keep = GetKeepedObjects(self); - if (keep != NULL) { - // check if it's a pointer to a pointer: - // pointers will have '0' key in the _objects - ptr_probe = PyDict_GetItemString(keep, "0"); - - if (ptr_probe != NULL) { - ptr2ptr = (CDataObject*) PyDict_GetItemString(keep, "1"); - if (ptr2ptr == NULL) { - PyErr_SetString(PyExc_ValueError, - "Unexpected NULL pointer in _objects"); - return NULL; - } - // don't construct a new object, - // return existing one instead to preserve refcount - assert( - *(void**) self->b_ptr == ptr2ptr->b_ptr || - *(void**) self->b_value.c == ptr2ptr->b_ptr || - *(void**) self->b_ptr == ptr2ptr->b_value.c || - *(void**) self->b_value.c == ptr2ptr->b_value.c - ); // double-check that we are returning the same thing - Py_INCREF(ptr2ptr); - return (PyObject *) ptr2ptr; - } - } - return PyCData_FromBaseObj(stgdict->proto, (PyObject *)self, 0, *(void **)self->b_ptr); From 42f9d6faa21bc75631eae0b587a2e76719102b47 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 24 Aug 2023 04:26:56 -0700 Subject: [PATCH 205/632] [3.11] gh-107432 Update Porting Python 2 Code to Python 3 how-to (GH-107434) (#108410) https://docs.python.org/3/howto/pyporting.html was written for another time. In this patch: - material that frames Python 3 as "new" is removed - descriptions and directions have been trimmed (cherry picked from commit 809ea7c4b6c2b818ae510f1f58e82b6b05ed4ef9) Co-authored-by: Daniele Procida --- Doc/howto/pyporting.rst | 215 ++++++++++++++++++---------------------- 1 file changed, 94 insertions(+), 121 deletions(-) diff --git a/Doc/howto/pyporting.rst b/Doc/howto/pyporting.rst index baea3e85c3b84b..6c30a0dd7d6bcc 100644 --- a/Doc/howto/pyporting.rst +++ b/Doc/howto/pyporting.rst @@ -1,49 +1,47 @@ .. _pyporting-howto: -********************************* -Porting Python 2 Code to Python 3 -********************************* +************************************* +How to port Python 2 Code to Python 3 +************************************* :author: Brett Cannon .. topic:: Abstract - With Python 3 being the future of Python while Python 2 is still in active - use, it is good to have your project available for both major releases of - Python. This guide is meant to help you figure out how best to support both - Python 2 & 3 simultaneously. + Python 2 reached its official end-of-life at the start of 2020. This means + that no new bug reports, fixes, or changes will be made to Python 2 - it's + no longer supported. + + This guide is intended to provide you with a path to Python 3 for your + code, that includes compatibility with Python 2 as a first step. If you are looking to port an extension module instead of pure Python code, please see :ref:`cporting-howto`. - If you would like to read one core Python developer's take on why Python 3 - came into existence, you can read Nick Coghlan's `Python 3 Q & A`_ or - Brett Cannon's `Why Python 3 exists`_. - + The archived python-porting_ mailing list may contain some useful guidance. - For help with porting, you can view the archived python-porting_ mailing list. The Short Explanation ===================== -To make your project be single-source Python 2/3 compatible, the basic steps +To achieve Python 2/3 compatibility in a single code base, the basic steps are: #. Only worry about supporting Python 2.7 #. Make sure you have good test coverage (coverage.py_ can help; ``python -m pip install coverage``) -#. Learn the differences between Python 2 & 3 +#. Learn the differences between Python 2 and 3 #. Use Futurize_ (or Modernize_) to update your code (e.g. ``python -m pip install future``) #. Use Pylint_ to help make sure you don't regress on your Python 3 support (``python -m pip install pylint``) #. Use caniusepython3_ to find out which of your dependencies are blocking your use of Python 3 (``python -m pip install caniusepython3``) #. Once your dependencies are no longer blocking you, use continuous integration - to make sure you stay compatible with Python 2 & 3 (tox_ can help test + to make sure you stay compatible with Python 2 and 3 (tox_ can help test against multiple versions of Python; ``python -m pip install tox``) #. Consider using optional static type checking to make sure your type usage - works in both Python 2 & 3 (e.g. use mypy_ to check your typing under both - Python 2 & Python 3; ``python -m pip install mypy``). + works in both Python 2 and 3 (e.g. use mypy_ to check your typing under both + Python 2 and Python 3; ``python -m pip install mypy``). .. note:: @@ -55,43 +53,30 @@ are: Details ======= -A key point about supporting Python 2 & 3 simultaneously is that you can start -**today**! Even if your dependencies are not supporting Python 3 yet that does -not mean you can't modernize your code **now** to support Python 3. Most changes -required to support Python 3 lead to cleaner code using newer practices even in -Python 2 code. +Even if other factors - say, dependencies over which you have no control - +still require you to support Python 2, that does not prevent you taking the +step of including Python 3 support. -Another key point is that modernizing your Python 2 code to also support -Python 3 is largely automated for you. While you might have to make some API -decisions thanks to Python 3 clarifying text data versus binary data, the -lower-level work is now mostly done for you and thus can at least benefit from -the automated changes immediately. +Most changes required to support Python 3 lead to cleaner code using newer +practices even in Python 2 code. -Keep those key points in mind while you read on about the details of porting -your code to support Python 2 & 3 simultaneously. +Different versions of Python 2 +------------------------------ -Drop support for Python 2.6 and older -------------------------------------- +Ideally, your code should be compatible with Python 2.7, which was the +last supported version of Python 2. -While you can make Python 2.5 work with Python 3, it is **much** easier if you -only have to work with Python 2.7. If dropping Python 2.5 is not an -option then the six_ project can help you support Python 2.5 & 3 simultaneously -(``python -m pip install six``). Do realize, though, that nearly all the projects listed -in this HOWTO will not be available to you. +Some of the tools mentioned in this guide will not work with Python 2.6. -If you are able to skip Python 2.5 and older, then the required changes -to your code should continue to look and feel like idiomatic Python code. At -worst you will have to use a function instead of a method in some instances or -have to import a function instead of using a built-in one, but otherwise the -overall transformation should not feel foreign to you. +If absolutely necessary, the six_ project can help you support Python 2.5 and +3 simultaneously. Do realize, though, that nearly all the projects listed in +this guide will not be available to you. -But you should aim for only supporting Python 2.7. Python 2.6 is no longer -freely supported and thus is not receiving bugfixes. This means **you** will have -to work around any issues you come across with Python 2.6. There are also some -tools mentioned in this HOWTO which do not support Python 2.6 (e.g., Pylint_), -and this will become more commonplace as time goes on. It will simply be easier -for you if you only support the versions of Python that you have to support. +If you are able to skip Python 2.5 and older, the required changes to your +code will be minimal. At worst you will have to use a function instead of a +method in some instances or have to import a function instead of using a +built-in one. Make sure you specify the proper version support in your ``setup.py`` file @@ -118,62 +103,57 @@ coverage). If you don't already have a tool to measure test coverage then coverage.py_ is recommended. -Learn the differences between Python 2 & 3 -------------------------------------------- +Be aware of the differences between Python 2 and 3 +-------------------------------------------------- Once you have your code well-tested you are ready to begin porting your code to Python 3! But to fully understand how your code is going to change and what you want to look out for while you code, you will want to learn what changes -Python 3 makes in terms of Python 2. Typically the two best ways of doing that -is reading the :ref:`"What's New" ` doc for each release of Python 3 and the -`Porting to Python 3`_ book (which is free online). There is also a handy -`cheat sheet`_ from the Python-Future project. +Python 3 makes in terms of Python 2. + +Some resources for understanding the differences and their implications for you +code: + +* the :ref:`"What's New" ` doc for each release of Python 3 +* the `Porting to Python 3`_ book (which is free online) +* the handy `cheat sheet`_ from the Python-Future project. Update your code ---------------- -Once you feel like you know what is different in Python 3 compared to Python 2, -it's time to update your code! You have a choice between two tools in porting -your code automatically: Futurize_ and Modernize_. Which tool you choose will -depend on how much like Python 3 you want your code to be. Futurize_ does its -best to make Python 3 idioms and practices exist in Python 2, e.g. backporting -the ``bytes`` type from Python 3 so that you have semantic parity between the -major versions of Python. Modernize_, -on the other hand, is more conservative and targets a Python 2/3 subset of -Python, directly relying on six_ to help provide compatibility. As Python 3 is -the future, it might be best to consider Futurize to begin adjusting to any new -practices that Python 3 introduces which you are not accustomed to yet. - -Regardless of which tool you choose, they will update your code to run under -Python 3 while staying compatible with the version of Python 2 you started with. -Depending on how conservative you want to be, you may want to run the tool over -your test suite first and visually inspect the diff to make sure the -transformation is accurate. After you have transformed your test suite and -verified that all the tests still pass as expected, then you can transform your -application code knowing that any tests which fail is a translation failure. +There are tools available that can port your code automatically. + +Futurize_ does its best to make Python 3 idioms and practices exist in Python +2, e.g. backporting the ``bytes`` type from Python 3 so that you have +semantic parity between the major versions of Python. This is the better +approach for most cases. + +Modernize_, on the other hand, is more conservative and targets a Python 2/3 +subset of Python, directly relying on six_ to help provide compatibility. + +A good approach is to run the tool over your test suite first and visually +inspect the diff to make sure the transformation is accurate. After you have +transformed your test suite and verified that all the tests still pass as +expected, then you can transform your application code knowing that any tests +which fail is a translation failure. Unfortunately the tools can't automate everything to make your code work under -Python 3 and so there are a handful of things you will need to update manually -to get full Python 3 support (which of these steps are necessary vary between -the tools). Read the documentation for the tool you choose to use to see what it -fixes by default and what it can do optionally to know what will (not) be fixed -for you and what you may have to fix on your own (e.g. using ``io.open()`` over -the built-in ``open()`` function is off by default in Modernize). Luckily, -though, there are only a couple of things to watch out for which can be -considered large issues that may be hard to debug if not watched for. +Python 3, and you will also need to read the tools' documentation in case some +options you need are turned off by default. +Key issues to be aware of and check for: Division ++++++++ -In Python 3, ``5 / 2 == 2.5`` and not ``2``; all division between ``int`` values -result in a ``float``. This change has actually been planned since Python 2.2 -which was released in 2002. Since then users have been encouraged to add -``from __future__ import division`` to any and all files which use the ``/`` and -``//`` operators or to be running the interpreter with the ``-Q`` flag. If you -have not been doing this then you will need to go through your code and do two -things: +In Python 3, ``5 / 2 == 2.5`` and not ``2`` as it was in Python 2; all +division between ``int`` values result in a ``float``. This change has +actually been planned since Python 2.2 which was released in 2002. Since then +users have been encouraged to add ``from __future__ import division`` to any +and all files which use the ``/`` and ``//`` operators or to be running the +interpreter with the ``-Q`` flag. If you have not been doing this then you +will need to go through your code and do two things: #. Add ``from __future__ import division`` to your files #. Update any division operator as necessary to either use ``//`` to use floor @@ -197,30 +177,29 @@ specific type. This complicated the situation especially for anyone supporting multiple languages as APIs wouldn't bother explicitly supporting ``unicode`` when they claimed text data support. -To make the distinction between text and binary data clearer and more -pronounced, Python 3 did what most languages created in the age of the internet -have done and made text and binary data distinct types that cannot blindly be -mixed together (Python predates widespread access to the internet). For any code -that deals only with text or only binary data, this separation doesn't pose an -issue. But for code that has to deal with both, it does mean you might have to -now care about when you are using text compared to binary data, which is why -this cannot be entirely automated. - -To start, you will need to decide which APIs take text and which take binary -(it is **highly** recommended you don't design APIs that can take both due to -the difficulty of keeping the code working; as stated earlier it is difficult to -do well). In Python 2 this means making sure the APIs that take text can work -with ``unicode`` and those that work with binary data work with the -``bytes`` type from Python 3 (which is a subset of ``str`` in Python 2 and acts -as an alias for ``bytes`` type in Python 2). Usually the biggest issue is -realizing which methods exist on which types in Python 2 & 3 simultaneously -(for text that's ``unicode`` in Python 2 and ``str`` in Python 3, for binary -that's ``str``/``bytes`` in Python 2 and ``bytes`` in Python 3). The following -table lists the **unique** methods of each data type across Python 2 & 3 -(e.g., the ``decode()`` method is usable on the equivalent binary data type in -either Python 2 or 3, but it can't be used by the textual data type consistently -between Python 2 and 3 because ``str`` in Python 3 doesn't have the method). Do -note that as of Python 3.5 the ``__mod__`` method was added to the bytes type. +Python 3 made text and binary data distinct types that cannot simply be mixed +together. For any code that deals only with text or only binary data, this +separation doesn't pose an issue. But for code that has to deal with both, it +does mean you might have to now care about when you are using text compared +to binary data, which is why this cannot be entirely automated. + +Decide which APIs take text and which take binary (it is **highly** recommended +you don't design APIs that can take both due to the difficulty of keeping the +code working; as stated earlier it is difficult to do well). In Python 2 this +means making sure the APIs that take text can work with ``unicode`` and those +that work with binary data work with the ``bytes`` type from Python 3 +(which is a subset of ``str`` in Python 2 and acts as an alias for ``bytes`` +type in Python 2). Usually the biggest issue is realizing which methods exist +on which types in Python 2 and 3 simultaneously (for text that's ``unicode`` +in Python 2 and ``str`` in Python 3, for binary that's ``str``/``bytes`` in +Python 2 and ``bytes`` in Python 3). + +The following table lists the **unique** methods of each data type across +Python 2 and 3 (e.g., the ``decode()`` method is usable on the equivalent binary +data type in either Python 2 or 3, but it can't be used by the textual data +type consistently between Python 2 and 3 because ``str`` in Python 3 doesn't +have the method). Do note that as of Python 3.5 the ``__mod__`` method was +added to the bytes type. ======================== ===================== **Text data** **Binary data** @@ -246,12 +225,11 @@ having to keep track of what type of data you are working with. The next issue is making sure you know whether the string literals in your code represent text or binary data. You should add a ``b`` prefix to any literal that presents binary data. For text you should add a ``u`` prefix to -the text literal. (there is a :mod:`__future__` import to force all unspecified +the text literal. (There is a :mod:`__future__` import to force all unspecified literals to be Unicode, but usage has shown it isn't as effective as adding a ``b`` or ``u`` prefix to all literals explicitly) -As part of this dichotomy you also need to be careful about opening files. -Unless you have been working on Windows, there is a chance you have not always +You also need to be careful about opening files. Possibly you have not always bothered to add the ``b`` mode when opening a binary file (e.g., ``rb`` for binary reading). Under Python 3, binary files and text files are clearly distinct and mutually incompatible; see the :mod:`io` module for details. @@ -265,7 +243,7 @@ outdated practice of using :func:`codecs.open` as that's only necessary for keeping compatibility with Python 2.5. The constructors of both ``str`` and ``bytes`` have different semantics for the -same arguments between Python 2 & 3. Passing an integer to ``bytes`` in Python 2 +same arguments between Python 2 and 3. Passing an integer to ``bytes`` in Python 2 will give you the string representation of the integer: ``bytes(3) == '3'``. But in Python 3, an integer argument to ``bytes`` will give you a bytes object as long as the integer specified, filled with null bytes: @@ -400,7 +378,7 @@ Use continuous integration to stay compatible --------------------------------------------- Once you are able to fully run under Python 3 you will want to make sure your -code always works under both Python 2 & 3. Probably the best tool for running +code always works under both Python 2 and 3. Probably the best tool for running your tests under multiple Python interpreters is tox_. You can then integrate tox with your continuous integration system so that you never accidentally break Python 2 or 3 support. @@ -413,11 +391,6 @@ separation of text/binary data handling or indexing on bytes you wouldn't easily find the mistake. This flag will raise an exception when these kinds of comparisons occur, making the mistake much easier to track down. -And that's mostly it! At this point your code base is compatible with both -Python 2 and 3 simultaneously. Your testing will also be set up so that you -don't accidentally break Python 2 or 3 compatibility regardless of which version -you typically run your tests under while developing. - Consider using optional static type checking -------------------------------------------- From cce6ba91b3a0111110d7e1db828bd6311d58a0a7 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Thu, 24 Aug 2023 13:05:22 +0100 Subject: [PATCH 206/632] Python 3.11.5 --- Include/patchlevel.h | 4 +- Lib/pydoc_data/topics.py | 77 +- Misc/NEWS.d/3.11.5.rst | 862 ++++++++++++++++++ ...-01-17-21-32-51.gh-issue-100340.i9zRGM.rst | 2 - ...-05-20-23-49-30.gh-issue-104692.s5UIu5.rst | 6 - ...-07-28-18-17-33.gh-issue-106881.U3Ezdq.rst | 1 - ...-08-09-17-05-33.gh-issue-107814.c0Oapq.rst | 1 - ...2-11-20-09-52-50.gh-issue-99612.eBHksg.rst | 2 - ...-06-09-23-34-25.gh-issue-105375.n7amiF.rst | 2 - ...-07-25-13-41-09.gh-issue-107226.N919zH.rst | 2 - ...-08-13-12-33-00.gh-issue-107915.jQ0wOi.rst | 4 - ...-08-14-10-59-03.gh-issue-107916.KH4Muo.rst | 4 - ...-01-13-11-37-41.gh-issue-101006.fuLvn2.rst | 1 - ...-06-02-19-37-29.gh-issue-105235.fgFGTi.rst | 1 - ...-06-08-09-25-52.gh-issue-105375.ocB7fT.rst | 2 - ...-06-08-09-54-37.gh-issue-105375.kqKT3E.rst | 1 - ...-06-08-10-10-07.gh-issue-105375.35VGDd.rst | 2 - ...-06-09-11-19-51.gh-issue-105588.Y5ovpY.rst | 2 - ...-06-15-22-11-43.gh-issue-105840.Fum_g_.rst | 2 - ...-06-22-19-16-24.gh-issue-105979.TDP2CU.rst | 1 - ...-07-13-14-55-45.gh-issue-106723.KsMufQ.rst | 1 - ...-07-13-15-59-07.gh-issue-106719.jmVrsv.rst | 2 - ...-07-18-16-13-51.gh-issue-106092.bObgRM.rst | 2 - ...3-07-25-22-35-35.gh-issue-77377.EHAbXx.rst | 1 - ...-07-27-11-47-29.gh-issue-104432.oGHF-z.rst | 4 - ...3-05-16-22-08-24.gh-issue-54738.mJvCnj.rst | 1 - ...-07-21-11-51-57.gh-issue-106948.K_JQ7j.rst | 1 - ...-07-22-15-14-13.gh-issue-107008.3JQ1Vt.rst | 2 - .../2020-05-03-00-33-15.bpo-18319.faPTlx.rst | 2 - ...2-07-12-18-45-13.gh-issue-94777.mOybx7.rst | 1 - ...2-11-26-22-05-22.gh-issue-99203.j0DUae.rst | 5 - ...-03-12-01-17-15.gh-issue-102541.LK1adc.rst | 1 - ...-03-14-01-19-57.gh-issue-100061.CiXJYn.rst | 2 - ...-06-05-14-43-56.gh-issue-104554.pwfKIo.rst | 1 - ...-06-06-11-50-33.gh-issue-105332.tmpgRA.rst | 1 - ...-06-07-00-09-52.gh-issue-105375.Y_9D4n.rst | 2 - ...-06-07-14-24-39.gh-issue-103171.b3VJMD.rst | 4 - ...-06-08-08-58-36.gh-issue-105375.bTcqS9.rst | 1 - ...-06-08-17-49-46.gh-issue-105497.K6Q8nU.rst | 1 - ...-06-09-21-04-39.gh-issue-105375.bTcqS9.rst | 1 - ...-06-09-21-11-28.gh-issue-105375.4Mxn7t.rst | 1 - ...-06-09-21-25-14.gh-issue-105375.95g1eI.rst | 1 - ...-06-09-21-30-59.gh-issue-105375.eewafp.rst | 2 - ...-06-09-21-40-45.gh-issue-105375._sZilh.rst | 1 - ...-06-09-21-46-52.gh-issue-105375.yrJelV.rst | 2 - ...-06-09-22-16-46.gh-issue-105375.EgVJOP.rst | 2 - ...-06-09-22-45-26.gh-issue-105375.9rp6tG.rst | 2 - ...-06-09-22-52-45.gh-issue-105375.6igkhn.rst | 1 - ...-06-09-23-00-13.gh-issue-105605.YuwqxY.rst | 3 - ...-06-09-23-46-23.gh-issue-105375.9KaioS.rst | 2 - ...-06-11-22-46-06.gh-issue-105375.YkhSNt.rst | 2 - ...-06-30-16-42-44.gh-issue-106263.tk-t93.rst | 2 - ...-07-01-16-40-54.gh-issue-102541.C1ahtk.rst | 1 - ...-07-03-03-46-20.gh-issue-106350.LLcTEe.rst | 2 - ...3-07-05-13-08-23.gh-issue-90876.Qvlkfl.rst | 3 - ...-07-05-14-34-10.gh-issue-105497.HU5u89.rst | 1 - ...-07-07-13-47-28.gh-issue-106510.9n5BdC.rst | 1 - ...-07-07-14-52-31.gh-issue-106052.ak8nbs.rst | 2 - ...-07-11-09-25-40.gh-issue-106530.VgXrMx.rst | 2 - ...-07-12-04-58-45.gh-issue-106602.dGCcXe.rst | 1 - ...-07-14-16-54-13.gh-issue-106752.BT1Yxw.rst | 3 - ...-07-15-10-24-56.gh-issue-106774.FJcqCj.rst | 1 - ...-07-17-21-45-15.gh-issue-106831.RqVq9X.rst | 2 - ...-07-22-13-09-28.gh-issue-106186.EIsUNG.rst | 3 - ...3-07-22-15-51-33.gh-issue-83006.21zaCz.rst | 2 - ...3-07-23-12-26-23.gh-issue-62519.w8-81X.rst | 2 - ...-07-31-07-36-24.gh-issue-107396.3_Kh6D.rst | 1 - ...-08-03-12-52-19.gh-issue-107077.-pzHD6.rst | 6 - ...-08-05-05-10-41.gh-issue-106684.P9zRXb.rst | 1 - ...-08-06-15-29-00.gh-issue-100814.h195gW.rst | 2 - ...-08-07-14-12-07.gh-issue-107715.238r2f.rst | 1 - ...-08-10-17-36-22.gh-issue-107845.dABiMJ.rst | 3 - ...-08-14-23-11-11.gh-issue-106242.71HMym.rst | 1 - ...-08-15-18-20-00.gh-issue-107963.20g5BG.rst | 2 - ...-08-17-14-45-25.gh-issue-105736.NJsH7r.rst | 3 - ...-08-22-17-39-12.gh-issue-108310.fVM3sg.rst | 7 - ...-06-28-02-51-08.gh-issue-101634.Rayczr.rst | 3 - ...-07-25-14-36-33.gh-issue-107237.y1pY79.rst | 2 - ...-08-23-04-08-18.gh-issue-105776.oE6wp_.rst | 2 - ...-07-21-23-16-05.gh-issue-106970.NLRnml.rst | 4 - ...3-08-07-16-30-48.gh-issue-95065.-im4R5.rst | 2 - ...-08-12-13-18-15.gh-issue-107565.Tv22Ne.rst | 2 - ...-06-08-11-30-17.gh-issue-105436.1qlDxw.rst | 2 - ...3-07-11-20-48-17.gh-issue-99079.CIMftz.rst | 1 - ...-07-18-13-01-26.gh-issue-106844.mci4xO.rst | 1 - ...-08-22-00-36-57.gh-issue-106242.q24ITw.rst | 4 - ...3-07-30-23-42-20.gh-issue-99079.JAtoh1.rst | 1 - ...-08-12-13-33-57.gh-issue-107565.SJwqf4.rst | 1 - README.rst | 2 +- 89 files changed, 909 insertions(+), 207 deletions(-) create mode 100644 Misc/NEWS.d/3.11.5.rst delete mode 100644 Misc/NEWS.d/next/Build/2023-01-17-21-32-51.gh-issue-100340.i9zRGM.rst delete mode 100644 Misc/NEWS.d/next/Build/2023-05-20-23-49-30.gh-issue-104692.s5UIu5.rst delete mode 100644 Misc/NEWS.d/next/Build/2023-07-28-18-17-33.gh-issue-106881.U3Ezdq.rst delete mode 100644 Misc/NEWS.d/next/Build/2023-08-09-17-05-33.gh-issue-107814.c0Oapq.rst delete mode 100644 Misc/NEWS.d/next/C API/2022-11-20-09-52-50.gh-issue-99612.eBHksg.rst delete mode 100644 Misc/NEWS.d/next/C API/2023-06-09-23-34-25.gh-issue-105375.n7amiF.rst delete mode 100644 Misc/NEWS.d/next/C API/2023-07-25-13-41-09.gh-issue-107226.N919zH.rst delete mode 100644 Misc/NEWS.d/next/C API/2023-08-13-12-33-00.gh-issue-107915.jQ0wOi.rst delete mode 100644 Misc/NEWS.d/next/C API/2023-08-14-10-59-03.gh-issue-107916.KH4Muo.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-13-11-37-41.gh-issue-101006.fuLvn2.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-06-02-19-37-29.gh-issue-105235.fgFGTi.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-06-08-09-25-52.gh-issue-105375.ocB7fT.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-06-08-09-54-37.gh-issue-105375.kqKT3E.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-06-08-10-10-07.gh-issue-105375.35VGDd.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-06-09-11-19-51.gh-issue-105588.Y5ovpY.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-06-15-22-11-43.gh-issue-105840.Fum_g_.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-06-22-19-16-24.gh-issue-105979.TDP2CU.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-07-13-14-55-45.gh-issue-106723.KsMufQ.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-07-13-15-59-07.gh-issue-106719.jmVrsv.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-07-18-16-13-51.gh-issue-106092.bObgRM.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-07-25-22-35-35.gh-issue-77377.EHAbXx.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-07-27-11-47-29.gh-issue-104432.oGHF-z.rst delete mode 100644 Misc/NEWS.d/next/Documentation/2023-05-16-22-08-24.gh-issue-54738.mJvCnj.rst delete mode 100644 Misc/NEWS.d/next/Documentation/2023-07-21-11-51-57.gh-issue-106948.K_JQ7j.rst delete mode 100644 Misc/NEWS.d/next/Documentation/2023-07-22-15-14-13.gh-issue-107008.3JQ1Vt.rst delete mode 100644 Misc/NEWS.d/next/Library/2020-05-03-00-33-15.bpo-18319.faPTlx.rst delete mode 100644 Misc/NEWS.d/next/Library/2022-07-12-18-45-13.gh-issue-94777.mOybx7.rst delete mode 100644 Misc/NEWS.d/next/Library/2022-11-26-22-05-22.gh-issue-99203.j0DUae.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-03-12-01-17-15.gh-issue-102541.LK1adc.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-03-14-01-19-57.gh-issue-100061.CiXJYn.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-06-05-14-43-56.gh-issue-104554.pwfKIo.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-06-06-11-50-33.gh-issue-105332.tmpgRA.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-06-07-00-09-52.gh-issue-105375.Y_9D4n.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-06-07-14-24-39.gh-issue-103171.b3VJMD.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-06-08-08-58-36.gh-issue-105375.bTcqS9.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-06-08-17-49-46.gh-issue-105497.K6Q8nU.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-06-09-21-04-39.gh-issue-105375.bTcqS9.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-06-09-21-11-28.gh-issue-105375.4Mxn7t.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-06-09-21-25-14.gh-issue-105375.95g1eI.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-06-09-21-30-59.gh-issue-105375.eewafp.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-06-09-21-40-45.gh-issue-105375._sZilh.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-06-09-21-46-52.gh-issue-105375.yrJelV.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-06-09-22-16-46.gh-issue-105375.EgVJOP.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-06-09-22-45-26.gh-issue-105375.9rp6tG.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-06-09-22-52-45.gh-issue-105375.6igkhn.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-06-09-23-00-13.gh-issue-105605.YuwqxY.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-06-09-23-46-23.gh-issue-105375.9KaioS.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-06-11-22-46-06.gh-issue-105375.YkhSNt.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-06-30-16-42-44.gh-issue-106263.tk-t93.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-07-01-16-40-54.gh-issue-102541.C1ahtk.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-07-03-03-46-20.gh-issue-106350.LLcTEe.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-07-05-13-08-23.gh-issue-90876.Qvlkfl.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-07-05-14-34-10.gh-issue-105497.HU5u89.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-07-07-13-47-28.gh-issue-106510.9n5BdC.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-07-07-14-52-31.gh-issue-106052.ak8nbs.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-07-11-09-25-40.gh-issue-106530.VgXrMx.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-07-12-04-58-45.gh-issue-106602.dGCcXe.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-07-14-16-54-13.gh-issue-106752.BT1Yxw.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-07-15-10-24-56.gh-issue-106774.FJcqCj.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-07-17-21-45-15.gh-issue-106831.RqVq9X.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-07-22-13-09-28.gh-issue-106186.EIsUNG.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-07-22-15-51-33.gh-issue-83006.21zaCz.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-07-23-12-26-23.gh-issue-62519.w8-81X.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-07-31-07-36-24.gh-issue-107396.3_Kh6D.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-08-03-12-52-19.gh-issue-107077.-pzHD6.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-08-05-05-10-41.gh-issue-106684.P9zRXb.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-08-06-15-29-00.gh-issue-100814.h195gW.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-08-07-14-12-07.gh-issue-107715.238r2f.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-08-10-17-36-22.gh-issue-107845.dABiMJ.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-08-14-23-11-11.gh-issue-106242.71HMym.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-08-15-18-20-00.gh-issue-107963.20g5BG.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-08-17-14-45-25.gh-issue-105736.NJsH7r.rst delete mode 100644 Misc/NEWS.d/next/Security/2023-08-22-17-39-12.gh-issue-108310.fVM3sg.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-06-28-02-51-08.gh-issue-101634.Rayczr.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-07-25-14-36-33.gh-issue-107237.y1pY79.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-08-23-04-08-18.gh-issue-105776.oE6wp_.rst delete mode 100644 Misc/NEWS.d/next/Tools-Demos/2023-07-21-23-16-05.gh-issue-106970.NLRnml.rst delete mode 100644 Misc/NEWS.d/next/Tools-Demos/2023-08-07-16-30-48.gh-issue-95065.-im4R5.rst delete mode 100644 Misc/NEWS.d/next/Tools-Demos/2023-08-12-13-18-15.gh-issue-107565.Tv22Ne.rst delete mode 100644 Misc/NEWS.d/next/Windows/2023-06-08-11-30-17.gh-issue-105436.1qlDxw.rst delete mode 100644 Misc/NEWS.d/next/Windows/2023-07-11-20-48-17.gh-issue-99079.CIMftz.rst delete mode 100644 Misc/NEWS.d/next/Windows/2023-07-18-13-01-26.gh-issue-106844.mci4xO.rst delete mode 100644 Misc/NEWS.d/next/Windows/2023-08-22-00-36-57.gh-issue-106242.q24ITw.rst delete mode 100644 Misc/NEWS.d/next/macOS/2023-07-30-23-42-20.gh-issue-99079.JAtoh1.rst delete mode 100644 Misc/NEWS.d/next/macOS/2023-08-12-13-33-57.gh-issue-107565.SJwqf4.rst diff --git a/Include/patchlevel.h b/Include/patchlevel.h index 72f5afaf375369..7023a3f6c366cd 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -18,12 +18,12 @@ /*--start constants--*/ #define PY_MAJOR_VERSION 3 #define PY_MINOR_VERSION 11 -#define PY_MICRO_VERSION 4 +#define PY_MICRO_VERSION 5 #define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_FINAL #define PY_RELEASE_SERIAL 0 /* Version as a string */ -#define PY_VERSION "3.11.4+" +#define PY_VERSION "3.11.5" /*--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 80930419b6182c..1b1251b690998e 100644 --- a/Lib/pydoc_data/topics.py +++ b/Lib/pydoc_data/topics.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- -# Autogenerated by Sphinx on Tue Jun 6 23:00:07 2023 +# Autogenerated by Sphinx on Thu Aug 24 13:07:17 2023 +# as part of the release process. topics = {'assert': 'The "assert" statement\n' '**********************\n' '\n' @@ -208,7 +209,7 @@ 'the\n' ' subscript must have a type compatible with the mapping’s key ' 'type,\n' - ' and the mapping is then asked to create a key/datum pair ' + ' and the mapping is then asked to create a key/value pair ' 'which maps\n' ' the subscript to the assigned object. This can either ' 'replace an\n' @@ -5429,30 +5430,31 @@ 'dict': 'Dictionary displays\n' '*******************\n' '\n' - 'A dictionary display is a possibly empty series of key/datum pairs\n' - 'enclosed in curly braces:\n' + 'A dictionary display is a possibly empty series of dict items\n' + '(key/value pairs) enclosed in curly braces:\n' '\n' - ' dict_display ::= "{" [key_datum_list | dict_comprehension] ' + ' dict_display ::= "{" [dict_item_list | dict_comprehension] ' '"}"\n' - ' key_datum_list ::= key_datum ("," key_datum)* [","]\n' - ' key_datum ::= expression ":" expression | "**" or_expr\n' + ' dict_item_list ::= dict_item ("," dict_item)* [","]\n' + ' dict_item ::= expression ":" expression | "**" or_expr\n' ' dict_comprehension ::= expression ":" expression comp_for\n' '\n' 'A dictionary display yields a new dictionary object.\n' '\n' - 'If a comma-separated sequence of key/datum pairs is given, they are\n' + 'If a comma-separated sequence of dict items is given, they are\n' 'evaluated from left to right to define the entries of the ' 'dictionary:\n' 'each key object is used as a key into the dictionary to store the\n' - 'corresponding datum. This means that you can specify the same key\n' - 'multiple times in the key/datum list, and the final dictionary’s ' + 'corresponding value. This means that you can specify the same key\n' + 'multiple times in the dict item list, and the final dictionary’s ' 'value\n' 'for that key will be the last one given.\n' '\n' 'A double asterisk "**" denotes *dictionary unpacking*. Its operand\n' 'must be a *mapping*. Each mapping item is added to the new\n' - 'dictionary. Later values replace values already set by earlier\n' - 'key/datum pairs and earlier dictionary unpackings.\n' + 'dictionary. Later values replace values already set by earlier ' + 'dict\n' + 'items and earlier dictionary unpackings.\n' '\n' 'New in version 3.5: Unpacking into dictionary displays, originally\n' 'proposed by **PEP 448**.\n' @@ -5468,7 +5470,7 @@ 'Restrictions on the types of the key values are listed earlier in\n' 'section The standard type hierarchy. (To summarize, the key type\n' 'should be *hashable*, which excludes all mutable objects.) Clashes\n' - 'between duplicate keys are not detected; the last datum (textually\n' + 'between duplicate keys are not detected; the last value (textually\n' 'rightmost in the display) stored for a given key value prevails.\n' '\n' 'Changed in version 3.8: Prior to Python 3.8, in dict ' @@ -6113,22 +6115,26 @@ 'positional\n' 'argument, and if it’s a keyword, it refers to a named ' 'keyword\n' - 'argument. If the numerical arg_names in a format string ' - 'are 0, 1, 2,\n' - '… in sequence, they can all be omitted (not just some) and ' - 'the numbers\n' - '0, 1, 2, … will be automatically inserted in that order. ' - 'Because\n' - '*arg_name* is not quote-delimited, it is not possible to ' - 'specify\n' - 'arbitrary dictionary keys (e.g., the strings "\'10\'" or ' - '"\':-]\'") within\n' - 'a format string. The *arg_name* can be followed by any ' - 'number of index\n' - 'or attribute expressions. An expression of the form ' - '"\'.name\'" selects\n' - 'the named attribute using "getattr()", while an expression ' - 'of the form\n' + 'argument. An *arg_name* is treated as a number if a call ' + 'to\n' + '"str.isdecimal()" on the string would return true. If the ' + 'numerical\n' + 'arg_names in a format string are 0, 1, 2, … in sequence, ' + 'they can all\n' + 'be omitted (not just some) and the numbers 0, 1, 2, … will ' + 'be\n' + 'automatically inserted in that order. Because *arg_name* is ' + 'not quote-\n' + 'delimited, it is not possible to specify arbitrary ' + 'dictionary keys\n' + '(e.g., the strings "\'10\'" or "\':-]\'") within a format ' + 'string. The\n' + '*arg_name* can be followed by any number of index or ' + 'attribute\n' + 'expressions. An expression of the form "\'.name\'" selects ' + 'the named\n' + 'attribute using "getattr()", while an expression of the ' + 'form\n' '"\'[index]\'" does an index lookup using "__getitem__()".\n' '\n' 'Changed in version 3.1: The positional argument specifiers ' @@ -9105,7 +9111,8 @@ ' still alive. The list is in definition order. Example:\n' '\n' ' >>> int.__subclasses__()\n' - " []\n", + " [, , , " + "]\n", 'specialnames': 'Special method names\n' '********************\n' '\n' @@ -12604,7 +12611,7 @@ 'are\n' 'most of the built-in objects considered false:\n' '\n' - '* constants defined to be false: "None" and "False".\n' + '* constants defined to be false: "None" and "False"\n' '\n' '* zero of any numeric type: "0", "0.0", "0j", "Decimal(0)",\n' ' "Fraction(0, 1)"\n' @@ -14517,8 +14524,12 @@ ' >>> # set operations\n' " >>> keys & {'eggs', 'bacon', 'salad'}\n" " {'bacon'}\n" - " >>> keys ^ {'sausage', 'juice'}\n" - " {'juice', 'sausage', 'bacon', 'spam'}\n" + " >>> keys ^ {'sausage', 'juice'} == {'juice', 'sausage', " + "'bacon', 'spam'}\n" + ' True\n' + " >>> keys | ['juice', 'juice', 'juice'] == {'bacon', " + "'spam', 'juice'}\n" + ' True\n' '\n' ' >>> # get back a read-only proxy for the original ' 'dictionary\n' diff --git a/Misc/NEWS.d/3.11.5.rst b/Misc/NEWS.d/3.11.5.rst new file mode 100644 index 00000000000000..765c85964a18bc --- /dev/null +++ b/Misc/NEWS.d/3.11.5.rst @@ -0,0 +1,862 @@ +.. date: 2023-08-22-17-39-12 +.. gh-issue: 108310 +.. nonce: fVM3sg +.. release date: 2023-08-24 +.. section: Security + +Fixed an issue where instances of :class:`ssl.SSLSocket` were vulnerable to +a bypass of the TLS handshake and included protections (like certificate +verification) and treating sent unencrypted data as if it were +post-handshake TLS encrypted data. Security issue reported as +`CVE-2023-40217 +`_ by Aapo +Oksman. Patch by Gregory P. Smith. + +.. + +.. date: 2023-07-27-11-47-29 +.. gh-issue: 104432 +.. nonce: oGHF-z +.. section: Core and Builtins + +Fix potential unaligned memory access on C APIs involving returned sequences +of ``char *`` pointers within the :mod:`grp` and :mod:`socket` modules. +These were revealed using a ``-fsaniziter=alignment`` build on ARM macOS. +Patch by Christopher Chavez. + +.. + +.. date: 2023-07-25-22-35-35 +.. gh-issue: 77377 +.. nonce: EHAbXx +.. section: Core and Builtins + +Ensure that multiprocessing synchronization objects created in a fork +context are not sent to a different process created in a spawn context. This +changes a segfault into an actionable RuntimeError in the parent process. + +.. + +.. date: 2023-07-18-16-13-51 +.. gh-issue: 106092 +.. nonce: bObgRM +.. section: Core and Builtins + +Fix a segmentation fault caused by a use-after-free bug in ``frame_dealloc`` +when the trashcan delays the deallocation of a ``PyFrameObject``. + +.. + +.. date: 2023-07-13-15-59-07 +.. gh-issue: 106719 +.. nonce: jmVrsv +.. section: Core and Builtins + +No longer suppress arbitrary errors in the ``__annotations__`` getter and +setter in the type and module types. + +.. + +.. date: 2023-07-13-14-55-45 +.. gh-issue: 106723 +.. nonce: KsMufQ +.. section: Core and Builtins + +Propagate ``frozen_modules`` to multiprocessing spawned process +interpreters. + +.. + +.. date: 2023-06-22-19-16-24 +.. gh-issue: 105979 +.. nonce: TDP2CU +.. section: Core and Builtins + +Fix crash in :func:`!_imp.get_frozen_object` due to improper exception +handling. + +.. + +.. date: 2023-06-15-22-11-43 +.. gh-issue: 105840 +.. nonce: Fum_g_ +.. section: Core and Builtins + +Fix possible crashes when specializing function calls with too many +``__defaults__``. + +.. + +.. date: 2023-06-09-11-19-51 +.. gh-issue: 105588 +.. nonce: Y5ovpY +.. section: Core and Builtins + +Fix an issue that could result in crashes when compiling malformed +:mod:`ast` nodes. + +.. + +.. date: 2023-06-08-10-10-07 +.. gh-issue: 105375 +.. nonce: 35VGDd +.. section: Core and Builtins + +Fix bugs in the :mod:`builtins` module where exceptions could end up being +overwritten. + +.. + +.. date: 2023-06-08-09-54-37 +.. gh-issue: 105375 +.. nonce: kqKT3E +.. section: Core and Builtins + +Fix bug in the compiler where an exception could end up being overwritten. + +.. + +.. date: 2023-06-08-09-25-52 +.. gh-issue: 105375 +.. nonce: ocB7fT +.. section: Core and Builtins + +Improve error handling in :c:func:`PyUnicode_BuildEncodingMap` where an +exception could end up being overwritten. + +.. + +.. date: 2023-06-02-19-37-29 +.. gh-issue: 105235 +.. nonce: fgFGTi +.. section: Core and Builtins + +Prevent out-of-bounds memory access during ``mmap.find()`` calls. + +.. + +.. date: 2023-01-13-11-37-41 +.. gh-issue: 101006 +.. nonce: fuLvn2 +.. section: Core and Builtins + +Improve error handling when read :mod:`marshal` data. + +.. + +.. date: 2023-08-17-14-45-25 +.. gh-issue: 105736 +.. nonce: NJsH7r +.. section: Library + +Harmonized the pure Python version of OrderedDict with the C version. Now, +both versions set up their internal state in ``__new__``. Formerly, the +pure Python version did the set up in ``__init__``. + +.. + +.. date: 2023-08-15-18-20-00 +.. gh-issue: 107963 +.. nonce: 20g5BG +.. section: Library + +Fix :func:`multiprocessing.set_forkserver_preload` to check the given list +of modules names. Patch by Dong-hee Na. + +.. + +.. date: 2023-08-14-23-11-11 +.. gh-issue: 106242 +.. nonce: 71HMym +.. section: Library + +Fixes :func:`os.path.normpath` to handle embedded null characters without +truncating the path. + +.. + +.. date: 2023-08-10-17-36-22 +.. gh-issue: 107845 +.. nonce: dABiMJ +.. section: Library + +:func:`tarfile.data_filter` now takes the location of symlinks into account +when determining their target, so it will no longer reject some valid +tarballs with ``LinkOutsideDestinationError``. + +.. + +.. date: 2023-08-07-14-12-07 +.. gh-issue: 107715 +.. nonce: 238r2f +.. section: Library + +Fix :meth:`doctest.DocTestFinder.find` in presence of class names with +special characters. Patch by Gertjan van Zwieten. + +.. + +.. date: 2023-08-06-15-29-00 +.. gh-issue: 100814 +.. nonce: h195gW +.. section: Library + +Passing a callable object as an option value to a Tkinter image now raises +the expected TclError instead of an AttributeError. + +.. + +.. date: 2023-08-05-05-10-41 +.. gh-issue: 106684 +.. nonce: P9zRXb +.. section: Library + +Close :class:`asyncio.StreamWriter` when it is not closed by application +leading to memory leaks. Patch by Kumar Aditya. + +.. + +.. date: 2023-08-03-12-52-19 +.. gh-issue: 107077 +.. nonce: -pzHD6 +.. section: Library + +Seems that in some conditions, OpenSSL will return ``SSL_ERROR_SYSCALL`` +instead of ``SSL_ERROR_SSL`` when a certification verification has failed, +but the error parameters will still contain ``ERR_LIB_SSL`` and +``SSL_R_CERTIFICATE_VERIFY_FAILED``. We are now detecting this situation and +raising the appropiate ``ssl.SSLCertVerificationError``. Patch by Pablo +Galindo + +.. + +.. date: 2023-07-31-07-36-24 +.. gh-issue: 107396 +.. nonce: 3_Kh6D +.. section: Library + +tarfiles; Fixed use before assignment of self.exception for gzip +decompression + +.. + +.. date: 2023-07-23-12-26-23 +.. gh-issue: 62519 +.. nonce: w8-81X +.. section: Library + +Make :func:`gettext.pgettext` search plural definitions when translation is +not found. + +.. + +.. date: 2023-07-22-15-51-33 +.. gh-issue: 83006 +.. nonce: 21zaCz +.. section: Library + +Document behavior of :func:`shutil.disk_usage` for non-mounted filesystems +on Unix. + +.. + +.. date: 2023-07-22-13-09-28 +.. gh-issue: 106186 +.. nonce: EIsUNG +.. section: Library + +Do not report ``MultipartInvariantViolationDefect`` defect when the +:class:`email.parser.Parser` class is used to parse emails with +``headersonly=True``. + +.. + +.. date: 2023-07-17-21-45-15 +.. gh-issue: 106831 +.. nonce: RqVq9X +.. section: Library + +Fix potential missing ``NULL`` check of ``d2i_SSL_SESSION`` result in +``_ssl.c``. + +.. + +.. date: 2023-07-15-10-24-56 +.. gh-issue: 106774 +.. nonce: FJcqCj +.. section: Library + +Update the bundled copy of pip to version 23.2.1. + +.. + +.. date: 2023-07-14-16-54-13 +.. gh-issue: 106752 +.. nonce: BT1Yxw +.. section: Library + +Fixed several bug in zipfile.Path in +``name``/``suffix``/``suffixes``/``stem`` operations when no filename is +present and the Path is not at the root of the zipfile. + +.. + +.. date: 2023-07-12-04-58-45 +.. gh-issue: 106602 +.. nonce: dGCcXe +.. section: Library + +Add __copy__ and __deepcopy__ in :mod:`enum` + +.. + +.. date: 2023-07-11-09-25-40 +.. gh-issue: 106530 +.. nonce: VgXrMx +.. section: Library + +Revert a change to :func:`colorsys.rgb_to_hls` that caused division by zero +for certain almost-white inputs. Patch by Terry Jan Reedy. + +.. + +.. date: 2023-07-07-14-52-31 +.. gh-issue: 106052 +.. nonce: ak8nbs +.. section: Library + +:mod:`re` module: fix the matching of possessive quantifiers in the case of +a subpattern containing backtracking. + +.. + +.. date: 2023-07-07-13-47-28 +.. gh-issue: 106510 +.. nonce: 9n5BdC +.. section: Library + +Improve debug output for atomic groups in regular expressions. + +.. + +.. date: 2023-07-05-14-34-10 +.. gh-issue: 105497 +.. nonce: HU5u89 +.. section: Library + +Fix flag mask inversion when unnamed flags exist. + +.. + +.. date: 2023-07-05-13-08-23 +.. gh-issue: 90876 +.. nonce: Qvlkfl +.. section: Library + +Prevent :mod:`multiprocessing.spawn` from failing to *import* in +environments where ``sys.executable`` is ``None``. This regressed in 3.11 +with the addition of support for path-like objects in multiprocessing. + +.. + +.. date: 2023-07-03-03-46-20 +.. gh-issue: 106350 +.. nonce: LLcTEe +.. section: Library + +Detect possible memory allocation failure in the libtommath function +:c:func:`mp_init` used by the ``_tkinter`` module. + +.. + +.. date: 2023-07-01-16-40-54 +.. gh-issue: 102541 +.. nonce: C1ahtk +.. section: Library + +Make pydoc.doc catch bad module ImportError when output stream is not None. + +.. + +.. date: 2023-06-30-16-42-44 +.. gh-issue: 106263 +.. nonce: tk-t93 +.. section: Library + +Fix crash when calling ``repr`` with a manually constructed SignalDict +object. Patch by Charlie Zhao. + +.. + +.. date: 2023-06-11-22-46-06 +.. gh-issue: 105375 +.. nonce: YkhSNt +.. section: Library + +Fix a bug in :c:func:`!_Unpickler_SetInputStream` where an exception could +end up being overwritten in case of failure. + +.. + +.. date: 2023-06-09-23-46-23 +.. gh-issue: 105375 +.. nonce: 9KaioS +.. section: Library + +Fix bugs in :mod:`sys` where exceptions could end up being overwritten +because of deferred error handling. + +.. + +.. date: 2023-06-09-23-00-13 +.. gh-issue: 105605 +.. nonce: YuwqxY +.. section: Library + +Harden :mod:`pyexpat` error handling during module initialisation to prevent +exceptions from possibly being overwritten, and objects from being +dereferenced twice. + +.. + +.. date: 2023-06-09-22-52-45 +.. gh-issue: 105375 +.. nonce: 6igkhn +.. section: Library + +Fix bug in :mod:`decimal` where an exception could end up being overwritten. + +.. + +.. date: 2023-06-09-22-45-26 +.. gh-issue: 105375 +.. nonce: 9rp6tG +.. section: Library + +Fix bugs in :mod:`!_datetime` where exceptions could be overwritten in case +of module initialisation failure. + +.. + +.. date: 2023-06-09-22-16-46 +.. gh-issue: 105375 +.. nonce: EgVJOP +.. section: Library + +Fix bugs in :mod:`!_ssl` initialisation which could lead to leaked +references and overwritten exceptions. + +.. + +.. date: 2023-06-09-21-46-52 +.. gh-issue: 105375 +.. nonce: yrJelV +.. section: Library + +Fix a bug in :class:`array.array` where an exception could end up being +overwritten. + +.. + +.. date: 2023-06-09-21-40-45 +.. gh-issue: 105375 +.. nonce: _sZilh +.. section: Library + +Fix bugs in :mod:`_ctypes` where exceptions could end up being overwritten. + +.. + +.. date: 2023-06-09-21-30-59 +.. gh-issue: 105375 +.. nonce: eewafp +.. section: Library + +Fix a bug in the :mod:`posix` module where an exception could be +overwritten. + +.. + +.. date: 2023-06-09-21-25-14 +.. gh-issue: 105375 +.. nonce: 95g1eI +.. section: Library + +Fix bugs in :mod:`!_elementtree` where exceptions could be overwritten. + +.. + +.. date: 2023-06-09-21-11-28 +.. gh-issue: 105375 +.. nonce: 4Mxn7t +.. section: Library + +Fix bugs in :mod:`zoneinfo` where exceptions could be overwritten. + +.. + +.. date: 2023-06-09-21-04-39 +.. gh-issue: 105375 +.. nonce: bTcqS9 +.. section: Library + +Fix bugs in :mod:`pickle` where exceptions could be overwritten. + +.. + +.. date: 2023-06-08-17-49-46 +.. gh-issue: 105497 +.. nonce: K6Q8nU +.. section: Library + +Fix flag inversion when alias/mask members exist. + +.. + +.. date: 2023-06-08-08-58-36 +.. gh-issue: 105375 +.. nonce: bTcqS9 +.. section: Library + +Fix bugs in :mod:`pickle` where exceptions could be overwritten. + +.. + +.. date: 2023-06-07-14-24-39 +.. gh-issue: 103171 +.. nonce: b3VJMD +.. section: Library + +Revert undocumented behaviour change with runtime-checkable protocols +decorated with :func:`typing.final` in Python 3.11. The behaviour change had +meant that objects would not be considered instances of these protocols at +runtime unless they had a ``__final__`` attribute. Patch by Alex Waygood. + +.. + +.. date: 2023-06-07-00-09-52 +.. gh-issue: 105375 +.. nonce: Y_9D4n +.. section: Library + +Fix a bug in :mod:`sqlite3` where an exception could be overwritten in the +:meth:`collation ` callback. + +.. + +.. date: 2023-06-06-11-50-33 +.. gh-issue: 105332 +.. nonce: tmpgRA +.. section: Library + +Revert pickling method from by-name back to by-value. + +.. + +.. date: 2023-06-05-14-43-56 +.. gh-issue: 104554 +.. nonce: pwfKIo +.. section: Library + +Add RTSPS scheme support in urllib.parse + +.. + +.. date: 2023-03-14-01-19-57 +.. gh-issue: 100061 +.. nonce: CiXJYn +.. section: Library + +Fix a bug that causes wrong matches for regular expressions with possessive +qualifier. + +.. + +.. date: 2023-03-12-01-17-15 +.. gh-issue: 102541 +.. nonce: LK1adc +.. section: Library + +Hide traceback in :func:`help` prompt, when import failed. + +.. + +.. date: 2022-11-26-22-05-22 +.. gh-issue: 99203 +.. nonce: j0DUae +.. section: Library + +Restore following CPython <= 3.10.5 behavior of :func:`shutil.make_archive`: +do not create an empty archive if ``root_dir`` is not a directory, and, in +that case, raise :class:`FileNotFoundError` or :class:`NotADirectoryError` +regardless of ``format`` choice. Beyond the brought-back behavior, the +function may now also raise these exceptions in ``dry_run`` mode. + +.. + +.. date: 2022-07-12-18-45-13 +.. gh-issue: 94777 +.. nonce: mOybx7 +.. section: Library + +Fix hanging :mod:`multiprocessing` ``ProcessPoolExecutor`` when a child +process crashes while data is being written in the call queue. + +.. + +.. bpo: 18319 +.. date: 2020-05-03-00-33-15 +.. nonce: faPTlx +.. section: Library + +Ensure ``gettext(msg)`` retrieve translations even if a plural form exists. +In other words: ``gettext(msg) == ngettext(msg, '', 1)``. + +.. + +.. date: 2023-07-22-15-14-13 +.. gh-issue: 107008 +.. nonce: 3JQ1Vt +.. section: Documentation + +Document the :mod:`curses` module variables :const:`~curses.LINES` and +:const:`~curses.COLS`. + +.. + +.. date: 2023-07-21-11-51-57 +.. gh-issue: 106948 +.. nonce: K_JQ7j +.. section: Documentation + +Add a number of standard external names to ``nitpick_ignore``. + +.. + +.. date: 2023-05-16-22-08-24 +.. gh-issue: 54738 +.. nonce: mJvCnj +.. section: Documentation + +Add documentation on how to localize the :mod:`argparse` module. + +.. + +.. date: 2023-08-23-04-08-18 +.. gh-issue: 105776 +.. nonce: oE6wp_ +.. section: Tests + +Fix test_cppext when the C compiler command ``-std=c11`` option: remove +``-std=`` options from the compiler command. Patch by Victor Stinner. + +.. + +.. date: 2023-07-25-14-36-33 +.. gh-issue: 107237 +.. nonce: y1pY79 +.. section: Tests + +``test_logging``: Fix ``test_udp_reconnection()`` by increasing the timeout +from 100 ms to 5 minutes (LONG_TIMEOUT). Patch by Victor Stinner. + +.. + +.. date: 2023-06-28-02-51-08 +.. gh-issue: 101634 +.. nonce: Rayczr +.. section: Tests + +When running the Python test suite with ``-jN`` option, if a worker stdout +cannot be decoded from the locale encoding report a failed testn so the +exitcode is non-zero. Patch by Victor Stinner. + +.. + +.. date: 2023-08-09-17-05-33 +.. gh-issue: 107814 +.. nonce: c0Oapq +.. section: Build + +When calling ``find_python.bat`` with ``-q`` it did not properly silence the +output of nuget. That is now fixed. + +.. + +.. date: 2023-07-28-18-17-33 +.. gh-issue: 106881 +.. nonce: U3Ezdq +.. section: Build + +Check for ``linux/limits.h`` before including it in +``Modules/posixmodule.c``. + +.. + +.. date: 2023-05-20-23-49-30 +.. gh-issue: 104692 +.. nonce: s5UIu5 +.. section: Build + +Include ``commoninstall`` as a prerequisite for ``bininstall`` + +This ensures that ``commoninstall`` is completed before ``bininstall`` is +started when parallel builds are used (``make -j install``), and so the +``python3`` symlink is only installed after all standard library modules are +installed. + +.. + +.. date: 2023-01-17-21-32-51 +.. gh-issue: 100340 +.. nonce: i9zRGM +.. section: Build + +Allows -Wno-int-conversion for wasm-sdk 17 and onwards, thus enables +building WASI builds once against the latest sdk. + +.. + +.. date: 2023-08-22-00-36-57 +.. gh-issue: 106242 +.. nonce: q24ITw +.. section: Windows + +Fixes :func:`~os.path.realpath` to behave consistently when passed a path +containing an embedded null character on Windows. In strict mode, it now +raises :exc:`OSError` instead of the unexpected :exc:`ValueError`, and in +non-strict mode will make the path absolute. + +.. + +.. date: 2023-07-18-13-01-26 +.. gh-issue: 106844 +.. nonce: mci4xO +.. section: Windows + +Fix integer overflow in :func:`!_winapi.LCMapStringEx` which affects +:func:`ntpath.normcase`. + +.. + +.. date: 2023-07-11-20-48-17 +.. gh-issue: 99079 +.. nonce: CIMftz +.. section: Windows + +Update Windows build to use OpenSSL 3.0.9 + +.. + +.. date: 2023-06-08-11-30-17 +.. gh-issue: 105436 +.. nonce: 1qlDxw +.. section: Windows + +Ensure that an empty environment block is terminated by two null characters, +as is required by Windows. + +.. + +.. date: 2023-08-12-13-33-57 +.. gh-issue: 107565 +.. nonce: SJwqf4 +.. section: macOS + +Update macOS installer to use OpenSSL 3.0.10. + +.. + +.. date: 2023-07-30-23-42-20 +.. gh-issue: 99079 +.. nonce: JAtoh1 +.. section: macOS + +Update macOS installer to use OpenSSL 3.0.9. + +.. + +.. date: 2023-08-12-13-18-15 +.. gh-issue: 107565 +.. nonce: Tv22Ne +.. section: Tools/Demos + +Update multissltests and GitHub CI workflows to use OpenSSL 1.1.1v, 3.0.10, +and 3.1.2. + +.. + +.. date: 2023-08-07-16-30-48 +.. gh-issue: 95065 +.. nonce: -im4R5 +.. section: Tools/Demos + +Argument Clinic now supports overriding automatically generated signature by +using directive ``@text_signature``. See +:ref:`clinic-howto-override-signature`. + +.. + +.. date: 2023-07-21-23-16-05 +.. gh-issue: 106970 +.. nonce: NLRnml +.. section: Tools/Demos + +Fix bugs in the Argument Clinic ``destination clear`` command; the +destination buffers would never be cleared, and the ``destination`` +directive parser would simply continue to the fault handler after processing +the command. Patch by Erlend E. Aasland. + +.. + +.. date: 2023-08-14-10-59-03 +.. gh-issue: 107916 +.. nonce: KH4Muo +.. section: C API + +C API functions :c:func:`PyErr_SetFromErrnoWithFilename`, +:c:func:`PyErr_SetExcFromWindowsErrWithFilename` and +:c:func:`PyErr_SetFromWindowsErrWithFilename` save now the error code before +calling :c:func:`PyUnicode_DecodeFSDefault`. + +.. + +.. date: 2023-08-13-12-33-00 +.. gh-issue: 107915 +.. nonce: jQ0wOi +.. section: C API + +Such C API functions as ``PyErr_SetString()``, ``PyErr_Format()``, +``PyErr_SetFromErrnoWithFilename()`` and many others no longer crash or +ignore errors if it failed to format the error message or decode the +filename. Instead, they keep a corresponding error. + +.. + +.. date: 2023-07-25-13-41-09 +.. gh-issue: 107226 +.. nonce: N919zH +.. section: C API + +:c:func:`PyModule_AddObjectRef` is now only available in the limited API +version 3.10 or later. + +.. + +.. date: 2023-06-09-23-34-25 +.. gh-issue: 105375 +.. nonce: n7amiF +.. section: C API + +Fix a bug in :c:func:`PyErr_WarnExplicit` where an exception could end up +being overwritten if the API failed internally. + +.. + +.. date: 2022-11-20-09-52-50 +.. gh-issue: 99612 +.. nonce: eBHksg +.. section: C API + +Fix :c:func:`PyUnicode_DecodeUTF8Stateful` for ASCII-only data: +``*consumed`` was not set. diff --git a/Misc/NEWS.d/next/Build/2023-01-17-21-32-51.gh-issue-100340.i9zRGM.rst b/Misc/NEWS.d/next/Build/2023-01-17-21-32-51.gh-issue-100340.i9zRGM.rst deleted file mode 100644 index 3a37f798dc6c6d..00000000000000 --- a/Misc/NEWS.d/next/Build/2023-01-17-21-32-51.gh-issue-100340.i9zRGM.rst +++ /dev/null @@ -1,2 +0,0 @@ -Allows -Wno-int-conversion for wasm-sdk 17 and onwards, thus enables -building WASI builds once against the latest sdk. diff --git a/Misc/NEWS.d/next/Build/2023-05-20-23-49-30.gh-issue-104692.s5UIu5.rst b/Misc/NEWS.d/next/Build/2023-05-20-23-49-30.gh-issue-104692.s5UIu5.rst deleted file mode 100644 index 2936990999e1aa..00000000000000 --- a/Misc/NEWS.d/next/Build/2023-05-20-23-49-30.gh-issue-104692.s5UIu5.rst +++ /dev/null @@ -1,6 +0,0 @@ -Include ``commoninstall`` as a prerequisite for ``bininstall`` - -This ensures that ``commoninstall`` is completed before ``bininstall`` -is started when parallel builds are used (``make -j install``), and so -the ``python3`` symlink is only installed after all standard library -modules are installed. diff --git a/Misc/NEWS.d/next/Build/2023-07-28-18-17-33.gh-issue-106881.U3Ezdq.rst b/Misc/NEWS.d/next/Build/2023-07-28-18-17-33.gh-issue-106881.U3Ezdq.rst deleted file mode 100644 index 7febf99c48a79b..00000000000000 --- a/Misc/NEWS.d/next/Build/2023-07-28-18-17-33.gh-issue-106881.U3Ezdq.rst +++ /dev/null @@ -1 +0,0 @@ -Check for ``linux/limits.h`` before including it in ``Modules/posixmodule.c``. diff --git a/Misc/NEWS.d/next/Build/2023-08-09-17-05-33.gh-issue-107814.c0Oapq.rst b/Misc/NEWS.d/next/Build/2023-08-09-17-05-33.gh-issue-107814.c0Oapq.rst deleted file mode 100644 index d3723353470ce2..00000000000000 --- a/Misc/NEWS.d/next/Build/2023-08-09-17-05-33.gh-issue-107814.c0Oapq.rst +++ /dev/null @@ -1 +0,0 @@ -When calling ``find_python.bat`` with ``-q`` it did not properly silence the output of nuget. That is now fixed. diff --git a/Misc/NEWS.d/next/C API/2022-11-20-09-52-50.gh-issue-99612.eBHksg.rst b/Misc/NEWS.d/next/C API/2022-11-20-09-52-50.gh-issue-99612.eBHksg.rst deleted file mode 100644 index 40e3c8db5403c7..00000000000000 --- a/Misc/NEWS.d/next/C API/2022-11-20-09-52-50.gh-issue-99612.eBHksg.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix :c:func:`PyUnicode_DecodeUTF8Stateful` for ASCII-only data: -``*consumed`` was not set. diff --git a/Misc/NEWS.d/next/C API/2023-06-09-23-34-25.gh-issue-105375.n7amiF.rst b/Misc/NEWS.d/next/C API/2023-06-09-23-34-25.gh-issue-105375.n7amiF.rst deleted file mode 100644 index b9f95496f938ec..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-06-09-23-34-25.gh-issue-105375.n7amiF.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a bug in :c:func:`PyErr_WarnExplicit` where an exception could end up -being overwritten if the API failed internally. diff --git a/Misc/NEWS.d/next/C API/2023-07-25-13-41-09.gh-issue-107226.N919zH.rst b/Misc/NEWS.d/next/C API/2023-07-25-13-41-09.gh-issue-107226.N919zH.rst deleted file mode 100644 index 6178f18517d48f..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-07-25-13-41-09.gh-issue-107226.N919zH.rst +++ /dev/null @@ -1,2 +0,0 @@ -:c:func:`PyModule_AddObjectRef` is now only available in the limited API -version 3.10 or later. diff --git a/Misc/NEWS.d/next/C API/2023-08-13-12-33-00.gh-issue-107915.jQ0wOi.rst b/Misc/NEWS.d/next/C API/2023-08-13-12-33-00.gh-issue-107915.jQ0wOi.rst deleted file mode 100644 index 58ee3f169a28cc..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-08-13-12-33-00.gh-issue-107915.jQ0wOi.rst +++ /dev/null @@ -1,4 +0,0 @@ -Such C API functions as ``PyErr_SetString()``, ``PyErr_Format()``, -``PyErr_SetFromErrnoWithFilename()`` and many others no longer crash or -ignore errors if it failed to format the error message or decode the -filename. Instead, they keep a corresponding error. diff --git a/Misc/NEWS.d/next/C API/2023-08-14-10-59-03.gh-issue-107916.KH4Muo.rst b/Misc/NEWS.d/next/C API/2023-08-14-10-59-03.gh-issue-107916.KH4Muo.rst deleted file mode 100644 index f1f16609b118ba..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-08-14-10-59-03.gh-issue-107916.KH4Muo.rst +++ /dev/null @@ -1,4 +0,0 @@ -C API functions :c:func:`PyErr_SetFromErrnoWithFilename`, -:c:func:`PyErr_SetExcFromWindowsErrWithFilename` and -:c:func:`PyErr_SetFromWindowsErrWithFilename` save now the error code before -calling :c:func:`PyUnicode_DecodeFSDefault`. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-13-11-37-41.gh-issue-101006.fuLvn2.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-13-11-37-41.gh-issue-101006.fuLvn2.rst deleted file mode 100644 index c98670d8c4963d..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-13-11-37-41.gh-issue-101006.fuLvn2.rst +++ /dev/null @@ -1 +0,0 @@ -Improve error handling when read :mod:`marshal` data. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-06-02-19-37-29.gh-issue-105235.fgFGTi.rst b/Misc/NEWS.d/next/Core and Builtins/2023-06-02-19-37-29.gh-issue-105235.fgFGTi.rst deleted file mode 100644 index c28d0101cd4bad..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-06-02-19-37-29.gh-issue-105235.fgFGTi.rst +++ /dev/null @@ -1 +0,0 @@ -Prevent out-of-bounds memory access during ``mmap.find()`` calls. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-06-08-09-25-52.gh-issue-105375.ocB7fT.rst b/Misc/NEWS.d/next/Core and Builtins/2023-06-08-09-25-52.gh-issue-105375.ocB7fT.rst deleted file mode 100644 index 24fac2df4d0955..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-06-08-09-25-52.gh-issue-105375.ocB7fT.rst +++ /dev/null @@ -1,2 +0,0 @@ -Improve error handling in :c:func:`PyUnicode_BuildEncodingMap` where an -exception could end up being overwritten. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-06-08-09-54-37.gh-issue-105375.kqKT3E.rst b/Misc/NEWS.d/next/Core and Builtins/2023-06-08-09-54-37.gh-issue-105375.kqKT3E.rst deleted file mode 100644 index b4d3a1a5a3cedb..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-06-08-09-54-37.gh-issue-105375.kqKT3E.rst +++ /dev/null @@ -1 +0,0 @@ -Fix bug in the compiler where an exception could end up being overwritten. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-06-08-10-10-07.gh-issue-105375.35VGDd.rst b/Misc/NEWS.d/next/Core and Builtins/2023-06-08-10-10-07.gh-issue-105375.35VGDd.rst deleted file mode 100644 index 3ab85538f3fc43..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-06-08-10-10-07.gh-issue-105375.35VGDd.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix bugs in the :mod:`builtins` module where exceptions could end up being -overwritten. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-06-09-11-19-51.gh-issue-105588.Y5ovpY.rst b/Misc/NEWS.d/next/Core and Builtins/2023-06-09-11-19-51.gh-issue-105588.Y5ovpY.rst deleted file mode 100644 index 3981dad7a49dfb..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-06-09-11-19-51.gh-issue-105588.Y5ovpY.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix an issue that could result in crashes when compiling malformed -:mod:`ast` nodes. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-06-15-22-11-43.gh-issue-105840.Fum_g_.rst b/Misc/NEWS.d/next/Core and Builtins/2023-06-15-22-11-43.gh-issue-105840.Fum_g_.rst deleted file mode 100644 index 5225031292e6c7..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-06-15-22-11-43.gh-issue-105840.Fum_g_.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix possible crashes when specializing function calls with too many -``__defaults__``. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-06-22-19-16-24.gh-issue-105979.TDP2CU.rst b/Misc/NEWS.d/next/Core and Builtins/2023-06-22-19-16-24.gh-issue-105979.TDP2CU.rst deleted file mode 100644 index be6962afd0c78f..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-06-22-19-16-24.gh-issue-105979.TDP2CU.rst +++ /dev/null @@ -1 +0,0 @@ -Fix crash in :func:`!_imp.get_frozen_object` due to improper exception handling. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-07-13-14-55-45.gh-issue-106723.KsMufQ.rst b/Misc/NEWS.d/next/Core and Builtins/2023-07-13-14-55-45.gh-issue-106723.KsMufQ.rst deleted file mode 100644 index 207f397f17d3f3..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-07-13-14-55-45.gh-issue-106723.KsMufQ.rst +++ /dev/null @@ -1 +0,0 @@ -Propagate ``frozen_modules`` to multiprocessing spawned process interpreters. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-07-13-15-59-07.gh-issue-106719.jmVrsv.rst b/Misc/NEWS.d/next/Core and Builtins/2023-07-13-15-59-07.gh-issue-106719.jmVrsv.rst deleted file mode 100644 index dc4bef193a3220..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-07-13-15-59-07.gh-issue-106719.jmVrsv.rst +++ /dev/null @@ -1,2 +0,0 @@ -No longer suppress arbitrary errors in the ``__annotations__`` getter and -setter in the type and module types. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-07-18-16-13-51.gh-issue-106092.bObgRM.rst b/Misc/NEWS.d/next/Core and Builtins/2023-07-18-16-13-51.gh-issue-106092.bObgRM.rst deleted file mode 100644 index 7fb5b45c763e45..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-07-18-16-13-51.gh-issue-106092.bObgRM.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a segmentation fault caused by a use-after-free bug in ``frame_dealloc`` -when the trashcan delays the deallocation of a ``PyFrameObject``. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-07-25-22-35-35.gh-issue-77377.EHAbXx.rst b/Misc/NEWS.d/next/Core and Builtins/2023-07-25-22-35-35.gh-issue-77377.EHAbXx.rst deleted file mode 100644 index 194851dea13352..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-07-25-22-35-35.gh-issue-77377.EHAbXx.rst +++ /dev/null @@ -1 +0,0 @@ -Ensure that multiprocessing synchronization objects created in a fork context are not sent to a different process created in a spawn context. This changes a segfault into an actionable RuntimeError in the parent process. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-07-27-11-47-29.gh-issue-104432.oGHF-z.rst b/Misc/NEWS.d/next/Core and Builtins/2023-07-27-11-47-29.gh-issue-104432.oGHF-z.rst deleted file mode 100644 index a9ab5cd43f0ffb..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-07-27-11-47-29.gh-issue-104432.oGHF-z.rst +++ /dev/null @@ -1,4 +0,0 @@ -Fix potential unaligned memory access on C APIs involving returned sequences -of ``char *`` pointers within the :mod:`grp` and :mod:`socket` modules. These -were revealed using a ``-fsaniziter=alignment`` build on ARM macOS. Patch by -Christopher Chavez. diff --git a/Misc/NEWS.d/next/Documentation/2023-05-16-22-08-24.gh-issue-54738.mJvCnj.rst b/Misc/NEWS.d/next/Documentation/2023-05-16-22-08-24.gh-issue-54738.mJvCnj.rst deleted file mode 100644 index 4da58fc982b6d7..00000000000000 --- a/Misc/NEWS.d/next/Documentation/2023-05-16-22-08-24.gh-issue-54738.mJvCnj.rst +++ /dev/null @@ -1 +0,0 @@ -Add documentation on how to localize the :mod:`argparse` module. diff --git a/Misc/NEWS.d/next/Documentation/2023-07-21-11-51-57.gh-issue-106948.K_JQ7j.rst b/Misc/NEWS.d/next/Documentation/2023-07-21-11-51-57.gh-issue-106948.K_JQ7j.rst deleted file mode 100644 index 42b6348153b56a..00000000000000 --- a/Misc/NEWS.d/next/Documentation/2023-07-21-11-51-57.gh-issue-106948.K_JQ7j.rst +++ /dev/null @@ -1 +0,0 @@ -Add a number of standard external names to ``nitpick_ignore``. diff --git a/Misc/NEWS.d/next/Documentation/2023-07-22-15-14-13.gh-issue-107008.3JQ1Vt.rst b/Misc/NEWS.d/next/Documentation/2023-07-22-15-14-13.gh-issue-107008.3JQ1Vt.rst deleted file mode 100644 index a0fa27ec10303e..00000000000000 --- a/Misc/NEWS.d/next/Documentation/2023-07-22-15-14-13.gh-issue-107008.3JQ1Vt.rst +++ /dev/null @@ -1,2 +0,0 @@ -Document the :mod:`curses` module variables :const:`~curses.LINES` and -:const:`~curses.COLS`. diff --git a/Misc/NEWS.d/next/Library/2020-05-03-00-33-15.bpo-18319.faPTlx.rst b/Misc/NEWS.d/next/Library/2020-05-03-00-33-15.bpo-18319.faPTlx.rst deleted file mode 100644 index 94d7cc9deadbb1..00000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-03-00-33-15.bpo-18319.faPTlx.rst +++ /dev/null @@ -1,2 +0,0 @@ -Ensure ``gettext(msg)`` retrieve translations even if a plural form exists. In -other words: ``gettext(msg) == ngettext(msg, '', 1)``. diff --git a/Misc/NEWS.d/next/Library/2022-07-12-18-45-13.gh-issue-94777.mOybx7.rst b/Misc/NEWS.d/next/Library/2022-07-12-18-45-13.gh-issue-94777.mOybx7.rst deleted file mode 100644 index 2c04a35fbfce13..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-07-12-18-45-13.gh-issue-94777.mOybx7.rst +++ /dev/null @@ -1 +0,0 @@ -Fix hanging :mod:`multiprocessing` ``ProcessPoolExecutor`` when a child process crashes while data is being written in the call queue. diff --git a/Misc/NEWS.d/next/Library/2022-11-26-22-05-22.gh-issue-99203.j0DUae.rst b/Misc/NEWS.d/next/Library/2022-11-26-22-05-22.gh-issue-99203.j0DUae.rst deleted file mode 100644 index fcfb044d476acc..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-11-26-22-05-22.gh-issue-99203.j0DUae.rst +++ /dev/null @@ -1,5 +0,0 @@ -Restore following CPython <= 3.10.5 behavior of :func:`shutil.make_archive`: -do not create an empty archive if ``root_dir`` is not a directory, and, in that -case, raise :class:`FileNotFoundError` or :class:`NotADirectoryError` -regardless of ``format`` choice. Beyond the brought-back behavior, the function -may now also raise these exceptions in ``dry_run`` mode. diff --git a/Misc/NEWS.d/next/Library/2023-03-12-01-17-15.gh-issue-102541.LK1adc.rst b/Misc/NEWS.d/next/Library/2023-03-12-01-17-15.gh-issue-102541.LK1adc.rst deleted file mode 100644 index 45b10679e19e2d..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-03-12-01-17-15.gh-issue-102541.LK1adc.rst +++ /dev/null @@ -1 +0,0 @@ -Hide traceback in :func:`help` prompt, when import failed. diff --git a/Misc/NEWS.d/next/Library/2023-03-14-01-19-57.gh-issue-100061.CiXJYn.rst b/Misc/NEWS.d/next/Library/2023-03-14-01-19-57.gh-issue-100061.CiXJYn.rst deleted file mode 100644 index dfed34f6ae9768..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-03-14-01-19-57.gh-issue-100061.CiXJYn.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a bug that causes wrong matches for regular expressions with possessive -qualifier. diff --git a/Misc/NEWS.d/next/Library/2023-06-05-14-43-56.gh-issue-104554.pwfKIo.rst b/Misc/NEWS.d/next/Library/2023-06-05-14-43-56.gh-issue-104554.pwfKIo.rst deleted file mode 100644 index 9ef8c67459c406..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-06-05-14-43-56.gh-issue-104554.pwfKIo.rst +++ /dev/null @@ -1 +0,0 @@ -Add RTSPS scheme support in urllib.parse diff --git a/Misc/NEWS.d/next/Library/2023-06-06-11-50-33.gh-issue-105332.tmpgRA.rst b/Misc/NEWS.d/next/Library/2023-06-06-11-50-33.gh-issue-105332.tmpgRA.rst deleted file mode 100644 index 31b6855a6ebfad..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-06-06-11-50-33.gh-issue-105332.tmpgRA.rst +++ /dev/null @@ -1 +0,0 @@ -Revert pickling method from by-name back to by-value. diff --git a/Misc/NEWS.d/next/Library/2023-06-07-00-09-52.gh-issue-105375.Y_9D4n.rst b/Misc/NEWS.d/next/Library/2023-06-07-00-09-52.gh-issue-105375.Y_9D4n.rst deleted file mode 100644 index ec10d63822c203..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-06-07-00-09-52.gh-issue-105375.Y_9D4n.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a bug in :mod:`sqlite3` where an exception could be overwritten in the -:meth:`collation ` callback. diff --git a/Misc/NEWS.d/next/Library/2023-06-07-14-24-39.gh-issue-103171.b3VJMD.rst b/Misc/NEWS.d/next/Library/2023-06-07-14-24-39.gh-issue-103171.b3VJMD.rst deleted file mode 100644 index 8c424d49012b86..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-06-07-14-24-39.gh-issue-103171.b3VJMD.rst +++ /dev/null @@ -1,4 +0,0 @@ -Revert undocumented behaviour change with runtime-checkable protocols -decorated with :func:`typing.final` in Python 3.11. The behaviour change had -meant that objects would not be considered instances of these protocols at -runtime unless they had a ``__final__`` attribute. Patch by Alex Waygood. diff --git a/Misc/NEWS.d/next/Library/2023-06-08-08-58-36.gh-issue-105375.bTcqS9.rst b/Misc/NEWS.d/next/Library/2023-06-08-08-58-36.gh-issue-105375.bTcqS9.rst deleted file mode 100644 index 3030477c8245b5..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-06-08-08-58-36.gh-issue-105375.bTcqS9.rst +++ /dev/null @@ -1 +0,0 @@ -Fix bugs in :mod:`pickle` where exceptions could be overwritten. diff --git a/Misc/NEWS.d/next/Library/2023-06-08-17-49-46.gh-issue-105497.K6Q8nU.rst b/Misc/NEWS.d/next/Library/2023-06-08-17-49-46.gh-issue-105497.K6Q8nU.rst deleted file mode 100644 index 2d4e2091b50714..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-06-08-17-49-46.gh-issue-105497.K6Q8nU.rst +++ /dev/null @@ -1 +0,0 @@ -Fix flag inversion when alias/mask members exist. diff --git a/Misc/NEWS.d/next/Library/2023-06-09-21-04-39.gh-issue-105375.bTcqS9.rst b/Misc/NEWS.d/next/Library/2023-06-09-21-04-39.gh-issue-105375.bTcqS9.rst deleted file mode 100644 index 3030477c8245b5..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-06-09-21-04-39.gh-issue-105375.bTcqS9.rst +++ /dev/null @@ -1 +0,0 @@ -Fix bugs in :mod:`pickle` where exceptions could be overwritten. diff --git a/Misc/NEWS.d/next/Library/2023-06-09-21-11-28.gh-issue-105375.4Mxn7t.rst b/Misc/NEWS.d/next/Library/2023-06-09-21-11-28.gh-issue-105375.4Mxn7t.rst deleted file mode 100644 index 4202b758d1db56..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-06-09-21-11-28.gh-issue-105375.4Mxn7t.rst +++ /dev/null @@ -1 +0,0 @@ -Fix bugs in :mod:`zoneinfo` where exceptions could be overwritten. diff --git a/Misc/NEWS.d/next/Library/2023-06-09-21-25-14.gh-issue-105375.95g1eI.rst b/Misc/NEWS.d/next/Library/2023-06-09-21-25-14.gh-issue-105375.95g1eI.rst deleted file mode 100644 index 1894b2b94bb334..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-06-09-21-25-14.gh-issue-105375.95g1eI.rst +++ /dev/null @@ -1 +0,0 @@ -Fix bugs in :mod:`!_elementtree` where exceptions could be overwritten. diff --git a/Misc/NEWS.d/next/Library/2023-06-09-21-30-59.gh-issue-105375.eewafp.rst b/Misc/NEWS.d/next/Library/2023-06-09-21-30-59.gh-issue-105375.eewafp.rst deleted file mode 100644 index e000f98828a211..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-06-09-21-30-59.gh-issue-105375.eewafp.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a bug in the :mod:`posix` module where an exception could be -overwritten. diff --git a/Misc/NEWS.d/next/Library/2023-06-09-21-40-45.gh-issue-105375._sZilh.rst b/Misc/NEWS.d/next/Library/2023-06-09-21-40-45.gh-issue-105375._sZilh.rst deleted file mode 100644 index 87db4c2b4e22e3..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-06-09-21-40-45.gh-issue-105375._sZilh.rst +++ /dev/null @@ -1 +0,0 @@ -Fix bugs in :mod:`_ctypes` where exceptions could end up being overwritten. diff --git a/Misc/NEWS.d/next/Library/2023-06-09-21-46-52.gh-issue-105375.yrJelV.rst b/Misc/NEWS.d/next/Library/2023-06-09-21-46-52.gh-issue-105375.yrJelV.rst deleted file mode 100644 index 21aea1b0b4082c..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-06-09-21-46-52.gh-issue-105375.yrJelV.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a bug in :class:`array.array` where an exception could end up being -overwritten. diff --git a/Misc/NEWS.d/next/Library/2023-06-09-22-16-46.gh-issue-105375.EgVJOP.rst b/Misc/NEWS.d/next/Library/2023-06-09-22-16-46.gh-issue-105375.EgVJOP.rst deleted file mode 100644 index 49f7df68e927cb..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-06-09-22-16-46.gh-issue-105375.EgVJOP.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix bugs in :mod:`!_ssl` initialisation which could lead to leaked -references and overwritten exceptions. diff --git a/Misc/NEWS.d/next/Library/2023-06-09-22-45-26.gh-issue-105375.9rp6tG.rst b/Misc/NEWS.d/next/Library/2023-06-09-22-45-26.gh-issue-105375.9rp6tG.rst deleted file mode 100644 index 352d7b83a71632..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-06-09-22-45-26.gh-issue-105375.9rp6tG.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix bugs in :mod:`!_datetime` where exceptions could be overwritten in case -of module initialisation failure. diff --git a/Misc/NEWS.d/next/Library/2023-06-09-22-52-45.gh-issue-105375.6igkhn.rst b/Misc/NEWS.d/next/Library/2023-06-09-22-52-45.gh-issue-105375.6igkhn.rst deleted file mode 100644 index 05e78fdc9b4076..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-06-09-22-52-45.gh-issue-105375.6igkhn.rst +++ /dev/null @@ -1 +0,0 @@ -Fix bug in :mod:`decimal` where an exception could end up being overwritten. diff --git a/Misc/NEWS.d/next/Library/2023-06-09-23-00-13.gh-issue-105605.YuwqxY.rst b/Misc/NEWS.d/next/Library/2023-06-09-23-00-13.gh-issue-105605.YuwqxY.rst deleted file mode 100644 index 5fba6d293a785e..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-06-09-23-00-13.gh-issue-105605.YuwqxY.rst +++ /dev/null @@ -1,3 +0,0 @@ -Harden :mod:`pyexpat` error handling during module initialisation to prevent -exceptions from possibly being overwritten, and objects from being -dereferenced twice. diff --git a/Misc/NEWS.d/next/Library/2023-06-09-23-46-23.gh-issue-105375.9KaioS.rst b/Misc/NEWS.d/next/Library/2023-06-09-23-46-23.gh-issue-105375.9KaioS.rst deleted file mode 100644 index b12d7c864e7b86..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-06-09-23-46-23.gh-issue-105375.9KaioS.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix bugs in :mod:`sys` where exceptions could end up being overwritten -because of deferred error handling. diff --git a/Misc/NEWS.d/next/Library/2023-06-11-22-46-06.gh-issue-105375.YkhSNt.rst b/Misc/NEWS.d/next/Library/2023-06-11-22-46-06.gh-issue-105375.YkhSNt.rst deleted file mode 100644 index dda8f428760ba1..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-06-11-22-46-06.gh-issue-105375.YkhSNt.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a bug in :c:func:`!_Unpickler_SetInputStream` where an exception could -end up being overwritten in case of failure. diff --git a/Misc/NEWS.d/next/Library/2023-06-30-16-42-44.gh-issue-106263.tk-t93.rst b/Misc/NEWS.d/next/Library/2023-06-30-16-42-44.gh-issue-106263.tk-t93.rst deleted file mode 100644 index d86a6bfdabbfb0..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-06-30-16-42-44.gh-issue-106263.tk-t93.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix crash when calling ``repr`` with a manually constructed SignalDict object. -Patch by Charlie Zhao. diff --git a/Misc/NEWS.d/next/Library/2023-07-01-16-40-54.gh-issue-102541.C1ahtk.rst b/Misc/NEWS.d/next/Library/2023-07-01-16-40-54.gh-issue-102541.C1ahtk.rst deleted file mode 100644 index efaf5db10f3e1c..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-07-01-16-40-54.gh-issue-102541.C1ahtk.rst +++ /dev/null @@ -1 +0,0 @@ -Make pydoc.doc catch bad module ImportError when output stream is not None. diff --git a/Misc/NEWS.d/next/Library/2023-07-03-03-46-20.gh-issue-106350.LLcTEe.rst b/Misc/NEWS.d/next/Library/2023-07-03-03-46-20.gh-issue-106350.LLcTEe.rst deleted file mode 100644 index 681d63a6668be8..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-07-03-03-46-20.gh-issue-106350.LLcTEe.rst +++ /dev/null @@ -1,2 +0,0 @@ -Detect possible memory allocation failure in the libtommath function :c:func:`mp_init` -used by the ``_tkinter`` module. diff --git a/Misc/NEWS.d/next/Library/2023-07-05-13-08-23.gh-issue-90876.Qvlkfl.rst b/Misc/NEWS.d/next/Library/2023-07-05-13-08-23.gh-issue-90876.Qvlkfl.rst deleted file mode 100644 index 3e062b5add6d89..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-07-05-13-08-23.gh-issue-90876.Qvlkfl.rst +++ /dev/null @@ -1,3 +0,0 @@ -Prevent :mod:`multiprocessing.spawn` from failing to *import* in environments -where ``sys.executable`` is ``None``. This regressed in 3.11 with the addition -of support for path-like objects in multiprocessing. diff --git a/Misc/NEWS.d/next/Library/2023-07-05-14-34-10.gh-issue-105497.HU5u89.rst b/Misc/NEWS.d/next/Library/2023-07-05-14-34-10.gh-issue-105497.HU5u89.rst deleted file mode 100644 index f4f2db08f73f50..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-07-05-14-34-10.gh-issue-105497.HU5u89.rst +++ /dev/null @@ -1 +0,0 @@ -Fix flag mask inversion when unnamed flags exist. diff --git a/Misc/NEWS.d/next/Library/2023-07-07-13-47-28.gh-issue-106510.9n5BdC.rst b/Misc/NEWS.d/next/Library/2023-07-07-13-47-28.gh-issue-106510.9n5BdC.rst deleted file mode 100644 index e0646fa9bc0211..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-07-07-13-47-28.gh-issue-106510.9n5BdC.rst +++ /dev/null @@ -1 +0,0 @@ -Improve debug output for atomic groups in regular expressions. diff --git a/Misc/NEWS.d/next/Library/2023-07-07-14-52-31.gh-issue-106052.ak8nbs.rst b/Misc/NEWS.d/next/Library/2023-07-07-14-52-31.gh-issue-106052.ak8nbs.rst deleted file mode 100644 index f2d4c2f7b18ec7..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-07-07-14-52-31.gh-issue-106052.ak8nbs.rst +++ /dev/null @@ -1,2 +0,0 @@ -:mod:`re` module: fix the matching of possessive quantifiers in the case of -a subpattern containing backtracking. diff --git a/Misc/NEWS.d/next/Library/2023-07-11-09-25-40.gh-issue-106530.VgXrMx.rst b/Misc/NEWS.d/next/Library/2023-07-11-09-25-40.gh-issue-106530.VgXrMx.rst deleted file mode 100644 index 09fc647cc01d21..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-07-11-09-25-40.gh-issue-106530.VgXrMx.rst +++ /dev/null @@ -1,2 +0,0 @@ -Revert a change to :func:`colorsys.rgb_to_hls` that caused division by zero -for certain almost-white inputs. Patch by Terry Jan Reedy. diff --git a/Misc/NEWS.d/next/Library/2023-07-12-04-58-45.gh-issue-106602.dGCcXe.rst b/Misc/NEWS.d/next/Library/2023-07-12-04-58-45.gh-issue-106602.dGCcXe.rst deleted file mode 100644 index d9c122f1d3c723..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-07-12-04-58-45.gh-issue-106602.dGCcXe.rst +++ /dev/null @@ -1 +0,0 @@ -Add __copy__ and __deepcopy__ in :mod:`enum` diff --git a/Misc/NEWS.d/next/Library/2023-07-14-16-54-13.gh-issue-106752.BT1Yxw.rst b/Misc/NEWS.d/next/Library/2023-07-14-16-54-13.gh-issue-106752.BT1Yxw.rst deleted file mode 100644 index d36c97da49d1b8..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-07-14-16-54-13.gh-issue-106752.BT1Yxw.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fixed several bug in zipfile.Path in -``name``/``suffix``/``suffixes``/``stem`` operations when no filename is -present and the Path is not at the root of the zipfile. diff --git a/Misc/NEWS.d/next/Library/2023-07-15-10-24-56.gh-issue-106774.FJcqCj.rst b/Misc/NEWS.d/next/Library/2023-07-15-10-24-56.gh-issue-106774.FJcqCj.rst deleted file mode 100644 index ed467573b89e14..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-07-15-10-24-56.gh-issue-106774.FJcqCj.rst +++ /dev/null @@ -1 +0,0 @@ -Update the bundled copy of pip to version 23.2.1. diff --git a/Misc/NEWS.d/next/Library/2023-07-17-21-45-15.gh-issue-106831.RqVq9X.rst b/Misc/NEWS.d/next/Library/2023-07-17-21-45-15.gh-issue-106831.RqVq9X.rst deleted file mode 100644 index d3b98626845392..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-07-17-21-45-15.gh-issue-106831.RqVq9X.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix potential missing ``NULL`` check of ``d2i_SSL_SESSION`` result in -``_ssl.c``. diff --git a/Misc/NEWS.d/next/Library/2023-07-22-13-09-28.gh-issue-106186.EIsUNG.rst b/Misc/NEWS.d/next/Library/2023-07-22-13-09-28.gh-issue-106186.EIsUNG.rst deleted file mode 100644 index 07fdcc96fa38a6..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-07-22-13-09-28.gh-issue-106186.EIsUNG.rst +++ /dev/null @@ -1,3 +0,0 @@ -Do not report ``MultipartInvariantViolationDefect`` defect -when the :class:`email.parser.Parser` class is used -to parse emails with ``headersonly=True``. diff --git a/Misc/NEWS.d/next/Library/2023-07-22-15-51-33.gh-issue-83006.21zaCz.rst b/Misc/NEWS.d/next/Library/2023-07-22-15-51-33.gh-issue-83006.21zaCz.rst deleted file mode 100644 index e64d1860828430..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-07-22-15-51-33.gh-issue-83006.21zaCz.rst +++ /dev/null @@ -1,2 +0,0 @@ -Document behavior of :func:`shutil.disk_usage` for non-mounted filesystems -on Unix. diff --git a/Misc/NEWS.d/next/Library/2023-07-23-12-26-23.gh-issue-62519.w8-81X.rst b/Misc/NEWS.d/next/Library/2023-07-23-12-26-23.gh-issue-62519.w8-81X.rst deleted file mode 100644 index 96e2a3dcc24fb0..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-07-23-12-26-23.gh-issue-62519.w8-81X.rst +++ /dev/null @@ -1,2 +0,0 @@ -Make :func:`gettext.pgettext` search plural definitions when -translation is not found. diff --git a/Misc/NEWS.d/next/Library/2023-07-31-07-36-24.gh-issue-107396.3_Kh6D.rst b/Misc/NEWS.d/next/Library/2023-07-31-07-36-24.gh-issue-107396.3_Kh6D.rst deleted file mode 100644 index 73bff4bdbe024d..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-07-31-07-36-24.gh-issue-107396.3_Kh6D.rst +++ /dev/null @@ -1 +0,0 @@ -tarfiles; Fixed use before assignment of self.exception for gzip decompression diff --git a/Misc/NEWS.d/next/Library/2023-08-03-12-52-19.gh-issue-107077.-pzHD6.rst b/Misc/NEWS.d/next/Library/2023-08-03-12-52-19.gh-issue-107077.-pzHD6.rst deleted file mode 100644 index ecaf437a48e0ae..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-08-03-12-52-19.gh-issue-107077.-pzHD6.rst +++ /dev/null @@ -1,6 +0,0 @@ -Seems that in some conditions, OpenSSL will return ``SSL_ERROR_SYSCALL`` -instead of ``SSL_ERROR_SSL`` when a certification verification has failed, -but the error parameters will still contain ``ERR_LIB_SSL`` and -``SSL_R_CERTIFICATE_VERIFY_FAILED``. We are now detecting this situation and -raising the appropiate ``ssl.SSLCertVerificationError``. Patch by Pablo -Galindo diff --git a/Misc/NEWS.d/next/Library/2023-08-05-05-10-41.gh-issue-106684.P9zRXb.rst b/Misc/NEWS.d/next/Library/2023-08-05-05-10-41.gh-issue-106684.P9zRXb.rst deleted file mode 100644 index 85bce76229853f..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-08-05-05-10-41.gh-issue-106684.P9zRXb.rst +++ /dev/null @@ -1 +0,0 @@ -Close :class:`asyncio.StreamWriter` when it is not closed by application leading to memory leaks. Patch by Kumar Aditya. diff --git a/Misc/NEWS.d/next/Library/2023-08-06-15-29-00.gh-issue-100814.h195gW.rst b/Misc/NEWS.d/next/Library/2023-08-06-15-29-00.gh-issue-100814.h195gW.rst deleted file mode 100644 index 86cb7bf79f3078..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-08-06-15-29-00.gh-issue-100814.h195gW.rst +++ /dev/null @@ -1,2 +0,0 @@ -Passing a callable object as an option value to a Tkinter image now raises -the expected TclError instead of an AttributeError. diff --git a/Misc/NEWS.d/next/Library/2023-08-07-14-12-07.gh-issue-107715.238r2f.rst b/Misc/NEWS.d/next/Library/2023-08-07-14-12-07.gh-issue-107715.238r2f.rst deleted file mode 100644 index 4bf08c071df2f9..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-08-07-14-12-07.gh-issue-107715.238r2f.rst +++ /dev/null @@ -1 +0,0 @@ -Fix :meth:`doctest.DocTestFinder.find` in presence of class names with special characters. Patch by Gertjan van Zwieten. diff --git a/Misc/NEWS.d/next/Library/2023-08-10-17-36-22.gh-issue-107845.dABiMJ.rst b/Misc/NEWS.d/next/Library/2023-08-10-17-36-22.gh-issue-107845.dABiMJ.rst deleted file mode 100644 index 32c1fb93f4ab2c..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-08-10-17-36-22.gh-issue-107845.dABiMJ.rst +++ /dev/null @@ -1,3 +0,0 @@ -:func:`tarfile.data_filter` now takes the location of symlinks into account -when determining their target, so it will no longer reject some valid -tarballs with ``LinkOutsideDestinationError``. diff --git a/Misc/NEWS.d/next/Library/2023-08-14-23-11-11.gh-issue-106242.71HMym.rst b/Misc/NEWS.d/next/Library/2023-08-14-23-11-11.gh-issue-106242.71HMym.rst deleted file mode 100644 index 44237a9f15708c..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-08-14-23-11-11.gh-issue-106242.71HMym.rst +++ /dev/null @@ -1 +0,0 @@ -Fixes :func:`os.path.normpath` to handle embedded null characters without truncating the path. diff --git a/Misc/NEWS.d/next/Library/2023-08-15-18-20-00.gh-issue-107963.20g5BG.rst b/Misc/NEWS.d/next/Library/2023-08-15-18-20-00.gh-issue-107963.20g5BG.rst deleted file mode 100644 index 3a73b2da0c4334..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-08-15-18-20-00.gh-issue-107963.20g5BG.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix :func:`multiprocessing.set_forkserver_preload` to check the given list -of modules names. Patch by Dong-hee Na. diff --git a/Misc/NEWS.d/next/Library/2023-08-17-14-45-25.gh-issue-105736.NJsH7r.rst b/Misc/NEWS.d/next/Library/2023-08-17-14-45-25.gh-issue-105736.NJsH7r.rst deleted file mode 100644 index 7fed54a5184c0f..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-08-17-14-45-25.gh-issue-105736.NJsH7r.rst +++ /dev/null @@ -1,3 +0,0 @@ -Harmonized the pure Python version of OrderedDict with the C version. Now, -both versions set up their internal state in ``__new__``. Formerly, the pure -Python version did the set up in ``__init__``. diff --git a/Misc/NEWS.d/next/Security/2023-08-22-17-39-12.gh-issue-108310.fVM3sg.rst b/Misc/NEWS.d/next/Security/2023-08-22-17-39-12.gh-issue-108310.fVM3sg.rst deleted file mode 100644 index 403c77a9d480ee..00000000000000 --- a/Misc/NEWS.d/next/Security/2023-08-22-17-39-12.gh-issue-108310.fVM3sg.rst +++ /dev/null @@ -1,7 +0,0 @@ -Fixed an issue where instances of :class:`ssl.SSLSocket` were vulnerable to -a bypass of the TLS handshake and included protections (like certificate -verification) and treating sent unencrypted data as if it were -post-handshake TLS encrypted data. Security issue reported as -`CVE-2023-40217 -`_ by -Aapo Oksman. Patch by Gregory P. Smith. diff --git a/Misc/NEWS.d/next/Tests/2023-06-28-02-51-08.gh-issue-101634.Rayczr.rst b/Misc/NEWS.d/next/Tests/2023-06-28-02-51-08.gh-issue-101634.Rayczr.rst deleted file mode 100644 index 6fbfc84c19e1b8..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-06-28-02-51-08.gh-issue-101634.Rayczr.rst +++ /dev/null @@ -1,3 +0,0 @@ -When running the Python test suite with ``-jN`` option, if a worker stdout -cannot be decoded from the locale encoding report a failed testn so the -exitcode is non-zero. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Tests/2023-07-25-14-36-33.gh-issue-107237.y1pY79.rst b/Misc/NEWS.d/next/Tests/2023-07-25-14-36-33.gh-issue-107237.y1pY79.rst deleted file mode 100644 index a04f7eeddef174..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-07-25-14-36-33.gh-issue-107237.y1pY79.rst +++ /dev/null @@ -1,2 +0,0 @@ -``test_logging``: Fix ``test_udp_reconnection()`` by increasing the timeout -from 100 ms to 5 minutes (LONG_TIMEOUT). Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Tests/2023-08-23-04-08-18.gh-issue-105776.oE6wp_.rst b/Misc/NEWS.d/next/Tests/2023-08-23-04-08-18.gh-issue-105776.oE6wp_.rst deleted file mode 100644 index 0e0a3aa9b11e68..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-08-23-04-08-18.gh-issue-105776.oE6wp_.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix test_cppext when the C compiler command ``-std=c11`` option: remove -``-std=`` options from the compiler command. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Tools-Demos/2023-07-21-23-16-05.gh-issue-106970.NLRnml.rst b/Misc/NEWS.d/next/Tools-Demos/2023-07-21-23-16-05.gh-issue-106970.NLRnml.rst deleted file mode 100644 index 194e3351b0470c..00000000000000 --- a/Misc/NEWS.d/next/Tools-Demos/2023-07-21-23-16-05.gh-issue-106970.NLRnml.rst +++ /dev/null @@ -1,4 +0,0 @@ -Fix bugs in the Argument Clinic ``destination clear`` command; the -destination buffers would never be cleared, and the ``destination`` -directive parser would simply continue to the fault handler after processing -the command. Patch by Erlend E. Aasland. diff --git a/Misc/NEWS.d/next/Tools-Demos/2023-08-07-16-30-48.gh-issue-95065.-im4R5.rst b/Misc/NEWS.d/next/Tools-Demos/2023-08-07-16-30-48.gh-issue-95065.-im4R5.rst deleted file mode 100644 index 7284f5bd548810..00000000000000 --- a/Misc/NEWS.d/next/Tools-Demos/2023-08-07-16-30-48.gh-issue-95065.-im4R5.rst +++ /dev/null @@ -1,2 +0,0 @@ -Argument Clinic now supports overriding automatically generated signature by -using directive ``@text_signature``. See :ref:`clinic-howto-override-signature`. diff --git a/Misc/NEWS.d/next/Tools-Demos/2023-08-12-13-18-15.gh-issue-107565.Tv22Ne.rst b/Misc/NEWS.d/next/Tools-Demos/2023-08-12-13-18-15.gh-issue-107565.Tv22Ne.rst deleted file mode 100644 index c43ee680e8158e..00000000000000 --- a/Misc/NEWS.d/next/Tools-Demos/2023-08-12-13-18-15.gh-issue-107565.Tv22Ne.rst +++ /dev/null @@ -1,2 +0,0 @@ -Update multissltests and GitHub CI workflows to use OpenSSL 1.1.1v, 3.0.10, -and 3.1.2. diff --git a/Misc/NEWS.d/next/Windows/2023-06-08-11-30-17.gh-issue-105436.1qlDxw.rst b/Misc/NEWS.d/next/Windows/2023-06-08-11-30-17.gh-issue-105436.1qlDxw.rst deleted file mode 100644 index 1e3f298096cdd6..00000000000000 --- a/Misc/NEWS.d/next/Windows/2023-06-08-11-30-17.gh-issue-105436.1qlDxw.rst +++ /dev/null @@ -1,2 +0,0 @@ -Ensure that an empty environment block is terminated by two null characters, -as is required by Windows. diff --git a/Misc/NEWS.d/next/Windows/2023-07-11-20-48-17.gh-issue-99079.CIMftz.rst b/Misc/NEWS.d/next/Windows/2023-07-11-20-48-17.gh-issue-99079.CIMftz.rst deleted file mode 100644 index 11f411be0f17c5..00000000000000 --- a/Misc/NEWS.d/next/Windows/2023-07-11-20-48-17.gh-issue-99079.CIMftz.rst +++ /dev/null @@ -1 +0,0 @@ -Update Windows build to use OpenSSL 3.0.9 diff --git a/Misc/NEWS.d/next/Windows/2023-07-18-13-01-26.gh-issue-106844.mci4xO.rst b/Misc/NEWS.d/next/Windows/2023-07-18-13-01-26.gh-issue-106844.mci4xO.rst deleted file mode 100644 index 11fca7e0452f40..00000000000000 --- a/Misc/NEWS.d/next/Windows/2023-07-18-13-01-26.gh-issue-106844.mci4xO.rst +++ /dev/null @@ -1 +0,0 @@ -Fix integer overflow in :func:`!_winapi.LCMapStringEx` which affects :func:`ntpath.normcase`. diff --git a/Misc/NEWS.d/next/Windows/2023-08-22-00-36-57.gh-issue-106242.q24ITw.rst b/Misc/NEWS.d/next/Windows/2023-08-22-00-36-57.gh-issue-106242.q24ITw.rst deleted file mode 100644 index ffe42ec5dc3faf..00000000000000 --- a/Misc/NEWS.d/next/Windows/2023-08-22-00-36-57.gh-issue-106242.q24ITw.rst +++ /dev/null @@ -1,4 +0,0 @@ -Fixes :func:`~os.path.realpath` to behave consistently when passed a path -containing an embedded null character on Windows. In strict mode, it now -raises :exc:`OSError` instead of the unexpected :exc:`ValueError`, and in -non-strict mode will make the path absolute. diff --git a/Misc/NEWS.d/next/macOS/2023-07-30-23-42-20.gh-issue-99079.JAtoh1.rst b/Misc/NEWS.d/next/macOS/2023-07-30-23-42-20.gh-issue-99079.JAtoh1.rst deleted file mode 100644 index d0eef4ec1003ce..00000000000000 --- a/Misc/NEWS.d/next/macOS/2023-07-30-23-42-20.gh-issue-99079.JAtoh1.rst +++ /dev/null @@ -1 +0,0 @@ -Update macOS installer to use OpenSSL 3.0.9. diff --git a/Misc/NEWS.d/next/macOS/2023-08-12-13-33-57.gh-issue-107565.SJwqf4.rst b/Misc/NEWS.d/next/macOS/2023-08-12-13-33-57.gh-issue-107565.SJwqf4.rst deleted file mode 100644 index c238c4760239e1..00000000000000 --- a/Misc/NEWS.d/next/macOS/2023-08-12-13-33-57.gh-issue-107565.SJwqf4.rst +++ /dev/null @@ -1 +0,0 @@ -Update macOS installer to use OpenSSL 3.0.10. diff --git a/README.rst b/README.rst index 5e492ff2d2f3e6..c26c85db42cc6c 100644 --- a/README.rst +++ b/README.rst @@ -1,4 +1,4 @@ -This is Python version 3.11.4 +This is Python version 3.11.5 ============================= .. image:: https://github.com/python/cpython/workflows/Tests/badge.svg From ea77520094085ff86160f64fd4bc4f7e8e4ec0d2 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Thu, 24 Aug 2023 20:31:59 +0100 Subject: [PATCH 207/632] Post 3.11.5 --- Include/patchlevel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/patchlevel.h b/Include/patchlevel.h index 7023a3f6c366cd..59f90803983732 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -23,7 +23,7 @@ #define PY_RELEASE_SERIAL 0 /* Version as a string */ -#define PY_VERSION "3.11.5" +#define PY_VERSION "3.11.5+" /*--end constants--*/ /* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2. From ee61f7db3184110216b907002222ce10355026fa Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 24 Aug 2023 23:06:34 -0700 Subject: [PATCH 208/632] [3.11] GH-108202: Document ``calendar`` exceptions (GH-108398) (#108468) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/library/calendar.rst | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Doc/library/calendar.rst b/Doc/library/calendar.rst index 66f59f0e2ced27..6460e6f0f49745 100644 --- a/Doc/library/calendar.rst +++ b/Doc/library/calendar.rst @@ -417,6 +417,29 @@ The :mod:`calendar` module exports the following data attributes: Aliases for day numbers, where ``MONDAY`` is ``0`` and ``SUNDAY`` is ``6``. + +The :mod:`calendar` module defines the following exceptions: + +.. exception:: IllegalMonthError(month) + + A subclass of :exc:`ValueError`, + raised when the given month number is outside of the range 1-12 (inclusive). + + .. attribute:: month + + The invalid month number. + + +.. exception:: IllegalWeekdayError(weekday) + + A subclass of :exc:`ValueError`, + raised when the given weekday number is outside of the range 0-6 (inclusive). + + .. attribute:: weekday + + The invalid weekday number. + + .. seealso:: Module :mod:`datetime` From 8cf3cae8c51f684ae9ea9531456398b7e8fddba5 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 25 Aug 2023 04:04:00 -0700 Subject: [PATCH 209/632] [3.11] Docs: Datamodel: Merge "Notes on using __slots__" with the parent section (GH-108400) (#108475) (cherry picked from commit 7f5b1a06612bf1454232ac634ad4d2c845f77b37) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/reference/datamodel.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 65d1ca1b8ddcbe..2666a01dc24dc7 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -1907,8 +1907,7 @@ Attribute lookup speed can be significantly improved as well. .. _datamodel-note-slots: -Notes on using *__slots__* -"""""""""""""""""""""""""" +Notes on using *__slots__*: * When inheriting from a class without *__slots__*, the :attr:`~object.__dict__` and From 0c79fabb036ccbe71298b141b0a9ff0506c67ae6 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 25 Aug 2023 15:10:22 +0300 Subject: [PATCH 210/632] [3.11] gh-108418: Speed up bigmem compression tests in dry mode (GH-108419) (GH-108481) Only generate and compress small amount of random data in dry run. (cherry picked from commit 4ae3edf3008b70e20663143553a736d80ff3a501) --- Lib/test/test_bz2.py | 4 ++-- Lib/test/test_lzma.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_bz2.py b/Lib/test/test_bz2.py index c97ed1cea0d113..ba1c02c833847c 100644 --- a/Lib/test/test_bz2.py +++ b/Lib/test/test_bz2.py @@ -721,10 +721,10 @@ def testEOFError(self): @bigmemtest(size=_4G + 100, memuse=3.3) def testDecompress4G(self, size): # "Test BZ2Decompressor.decompress() with >4GiB input" - blocksize = 10 * 1024 * 1024 + blocksize = min(10 * 1024 * 1024, size) block = random.randbytes(blocksize) try: - data = block * (size // blocksize + 1) + data = block * ((size-1) // blocksize + 1) compressed = bz2.compress(data) bz2d = BZ2Decompressor() decompressed = bz2d.decompress(compressed) diff --git a/Lib/test/test_lzma.py b/Lib/test/test_lzma.py index 145c8cfced4080..49042d7390b66d 100644 --- a/Lib/test/test_lzma.py +++ b/Lib/test/test_lzma.py @@ -352,10 +352,10 @@ def test_compressor_bigmem(self, size): @bigmemtest(size=_4G + 100, memuse=3) def test_decompressor_bigmem(self, size): lzd = LZMADecompressor() - blocksize = 10 * 1024 * 1024 + blocksize = min(10 * 1024 * 1024, size) block = random.randbytes(blocksize) try: - input = block * (size // blocksize + 1) + input = block * ((size-1) // blocksize + 1) cdata = lzma.compress(input) ddata = lzd.decompress(cdata) self.assertEqual(ddata, input) From 5505bfd687be376c9ed7dd9fca55e25d3c1515e3 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Fri, 25 Aug 2023 16:27:34 +0100 Subject: [PATCH 211/632] [3.11] Datamodel: Add headings to the standard type hierarchy (GH-108146) (#108484) * [3.11] Datamodel: Add headings to the standard type hierarchy (GH-108146) Dedent content according to the new layout.. (cherry picked from commit 2b7bff0655a4caf51cd1a9e5bf85b3b96dd031c9) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> * Fix indentation --- Doc/reference/datamodel.rst | 2066 ++++++++++++++++++----------------- 1 file changed, 1085 insertions(+), 981 deletions(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 2666a01dc24dc7..27ac00216a01df 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -141,694 +141,771 @@ Some of the type descriptions below contain a paragraph listing 'special attributes.' These are attributes that provide access to the implementation and are not intended for general use. Their definition may change in the future. + None - .. index:: pair: object; None +---- + +.. index:: pair: object; None + +This type has a single value. There is a single object with this value. This +object is accessed through the built-in name ``None``. It is used to signify the +absence of a value in many situations, e.g., it is returned from functions that +don't explicitly return anything. Its truth value is false. - This type has a single value. There is a single object with this value. This - object is accessed through the built-in name ``None``. It is used to signify the - absence of a value in many situations, e.g., it is returned from functions that - don't explicitly return anything. Its truth value is false. NotImplemented - .. index:: pair: object; NotImplemented +-------------- + +.. index:: pair: object; NotImplemented - This type has a single value. There is a single object with this value. This - object is accessed through the built-in name ``NotImplemented``. Numeric methods - and rich comparison methods should return this value if they do not implement the - operation for the operands provided. (The interpreter will then try the - reflected operation, or some other fallback, depending on the operator.) It - should not be evaluated in a boolean context. +This type has a single value. There is a single object with this value. This +object is accessed through the built-in name ``NotImplemented``. Numeric methods +and rich comparison methods should return this value if they do not implement the +operation for the operands provided. (The interpreter will then try the +reflected operation, or some other fallback, depending on the operator.) It +should not be evaluated in a boolean context. - See - :ref:`implementing-the-arithmetic-operations` - for more details. +See +:ref:`implementing-the-arithmetic-operations` +for more details. - .. versionchanged:: 3.9 - Evaluating ``NotImplemented`` in a boolean context is deprecated. While - it currently evaluates as true, it will emit a :exc:`DeprecationWarning`. - It will raise a :exc:`TypeError` in a future version of Python. +.. versionchanged:: 3.9 + Evaluating ``NotImplemented`` in a boolean context is deprecated. While + it currently evaluates as true, it will emit a :exc:`DeprecationWarning`. + It will raise a :exc:`TypeError` in a future version of Python. Ellipsis - .. index:: - pair: object; Ellipsis - single: ...; ellipsis literal +-------- +.. index:: + pair: object; Ellipsis + single: ...; ellipsis literal + +This type has a single value. There is a single object with this value. This +object is accessed through the literal ``...`` or the built-in name +``Ellipsis``. Its truth value is true. - This type has a single value. There is a single object with this value. This - object is accessed through the literal ``...`` or the built-in name - ``Ellipsis``. Its truth value is true. :class:`numbers.Number` - .. index:: pair: object; numeric +----------------------- + +.. index:: pair: object; numeric + +These are created by numeric literals and returned as results by arithmetic +operators and arithmetic built-in functions. Numeric objects are immutable; +once created their value never changes. Python numbers are of course strongly +related to mathematical numbers, but subject to the limitations of numerical +representation in computers. + +The string representations of the numeric classes, computed by +:meth:`~object.__repr__` and :meth:`~object.__str__`, have the following +properties: + +* They are valid numeric literals which, when passed to their + class constructor, produce an object having the value of the + original numeric. + +* The representation is in base 10, when possible. + +* Leading zeros, possibly excepting a single zero before a + decimal point, are not shown. - These are created by numeric literals and returned as results by arithmetic - operators and arithmetic built-in functions. Numeric objects are immutable; - once created their value never changes. Python numbers are of course strongly - related to mathematical numbers, but subject to the limitations of numerical - representation in computers. +* Trailing zeros, possibly excepting a single zero after a + decimal point, are not shown. - The string representations of the numeric classes, computed by - :meth:`~object.__repr__` and :meth:`~object.__str__`, have the following - properties: +* A sign is shown only when the number is negative. - * They are valid numeric literals which, when passed to their - class constructor, produce an object having the value of the - original numeric. +Python distinguishes between integers, floating point numbers, and complex +numbers: - * The representation is in base 10, when possible. - * Leading zeros, possibly excepting a single zero before a - decimal point, are not shown. +:class:`numbers.Integral` +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: pair: object; integer - * Trailing zeros, possibly excepting a single zero after a - decimal point, are not shown. +These represent elements from the mathematical set of integers (positive and +negative). - * A sign is shown only when the number is negative. +.. note:: + .. index:: pair: integer; representation - Python distinguishes between integers, floating point numbers, and complex - numbers: + The rules for integer representation are intended to give the most meaningful + interpretation of shift and mask operations involving negative integers. - :class:`numbers.Integral` - .. index:: pair: object; integer +There are two types of integers: - These represent elements from the mathematical set of integers (positive and - negative). +Integers (:class:`int`) + These represent numbers in an unlimited range, subject to available (virtual) + memory only. For the purpose of shift and mask operations, a binary + representation is assumed, and negative numbers are represented in a variant of + 2's complement which gives the illusion of an infinite string of sign bits + extending to the left. - There are two types of integers: +Booleans (:class:`bool`) + .. index:: + pair: object; Boolean + single: False + single: True - Integers (:class:`int`) - These represent numbers in an unlimited range, subject to available (virtual) - memory only. For the purpose of shift and mask operations, a binary - representation is assumed, and negative numbers are represented in a variant of - 2's complement which gives the illusion of an infinite string of sign bits - extending to the left. + These represent the truth values False and True. The two objects representing + the values ``False`` and ``True`` are the only Boolean objects. The Boolean type is a + subtype of the integer type, and Boolean values behave like the values 0 and 1, + respectively, in almost all contexts, the exception being that when converted to + a string, the strings ``"False"`` or ``"True"`` are returned, respectively. - Booleans (:class:`bool`) - .. index:: - pair: object; Boolean - single: False - single: True - These represent the truth values False and True. The two objects representing - the values ``False`` and ``True`` are the only Boolean objects. The Boolean type is a - subtype of the integer type, and Boolean values behave like the values 0 and 1, - respectively, in almost all contexts, the exception being that when converted to - a string, the strings ``"False"`` or ``"True"`` are returned, respectively. +:class:`numbers.Real` (:class:`float`) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: + pair: object; floating point + pair: floating point; number + pair: C; language + pair: Java; language - .. index:: pair: integer; representation +These represent machine-level double precision floating point numbers. You are +at the mercy of the underlying machine architecture (and C or Java +implementation) for the accepted range and handling of overflow. Python does not +support single-precision floating point numbers; the savings in processor and +memory usage that are usually the reason for using these are dwarfed by the +overhead of using objects in Python, so there is no reason to complicate the +language with two kinds of floating point numbers. - The rules for integer representation are intended to give the most meaningful - interpretation of shift and mask operations involving negative integers. - :class:`numbers.Real` (:class:`float`) - .. index:: - pair: object; floating point - pair: floating point; number - pair: C; language - pair: Java; language +:class:`numbers.Complex` (:class:`complex`) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - These represent machine-level double precision floating point numbers. You are - at the mercy of the underlying machine architecture (and C or Java - implementation) for the accepted range and handling of overflow. Python does not - support single-precision floating point numbers; the savings in processor and - memory usage that are usually the reason for using these are dwarfed by the - overhead of using objects in Python, so there is no reason to complicate the - language with two kinds of floating point numbers. +.. index:: + pair: object; complex + pair: complex; number - :class:`numbers.Complex` (:class:`complex`) - .. index:: - pair: object; complex - pair: complex; number +These represent complex numbers as a pair of machine-level double precision +floating point numbers. The same caveats apply as for floating point numbers. +The real and imaginary parts of a complex number ``z`` can be retrieved through +the read-only attributes ``z.real`` and ``z.imag``. - These represent complex numbers as a pair of machine-level double precision - floating point numbers. The same caveats apply as for floating point numbers. - The real and imaginary parts of a complex number ``z`` can be retrieved through - the read-only attributes ``z.real`` and ``z.imag``. Sequences +--------- + +.. index:: + pair: built-in function; len + pair: object; sequence + single: index operation + single: item selection + single: subscription + +These represent finite ordered sets indexed by non-negative numbers. The +built-in function :func:`len` returns the number of items of a sequence. When +the length of a sequence is *n*, the index set contains the numbers 0, 1, +..., *n*-1. Item *i* of sequence *a* is selected by ``a[i]``. + +.. index:: single: slicing + +Sequences also support slicing: ``a[i:j]`` selects all items with index *k* such +that *i* ``<=`` *k* ``<`` *j*. When used as an expression, a slice is a +sequence of the same type. This implies that the index set is renumbered so +that it starts at 0. + +Some sequences also support "extended slicing" with a third "step" parameter: +``a[i:j:k]`` selects all items of *a* with index *x* where ``x = i + n*k``, *n* +``>=`` ``0`` and *i* ``<=`` *x* ``<`` *j*. + +Sequences are distinguished according to their mutability: + + +Immutable sequences +^^^^^^^^^^^^^^^^^^^ + +.. index:: + pair: object; immutable sequence + pair: object; immutable + +An object of an immutable sequence type cannot change once it is created. (If +the object contains references to other objects, these other objects may be +mutable and may be changed; however, the collection of objects directly +referenced by an immutable object cannot change.) + +The following types are immutable sequences: + +.. index:: + single: string; immutable sequences + +Strings .. index:: - pair: built-in function; len - pair: object; sequence - single: index operation - single: item selection - single: subscription - - These represent finite ordered sets indexed by non-negative numbers. The - built-in function :func:`len` returns the number of items of a sequence. When - the length of a sequence is *n*, the index set contains the numbers 0, 1, - ..., *n*-1. Item *i* of sequence *a* is selected by ``a[i]``. - - .. index:: single: slicing - - Sequences also support slicing: ``a[i:j]`` selects all items with index *k* such - that *i* ``<=`` *k* ``<`` *j*. When used as an expression, a slice is a - sequence of the same type. This implies that the index set is renumbered so - that it starts at 0. - - Some sequences also support "extended slicing" with a third "step" parameter: - ``a[i:j:k]`` selects all items of *a* with index *x* where ``x = i + n*k``, *n* - ``>=`` ``0`` and *i* ``<=`` *x* ``<`` *j*. - - Sequences are distinguished according to their mutability: - - Immutable sequences - .. index:: - pair: object; immutable sequence - pair: object; immutable - - An object of an immutable sequence type cannot change once it is created. (If - the object contains references to other objects, these other objects may be - mutable and may be changed; however, the collection of objects directly - referenced by an immutable object cannot change.) - - The following types are immutable sequences: - - .. index:: - single: string; immutable sequences - - Strings - .. index:: - pair: built-in function; chr - pair: built-in function; ord - single: character - single: integer - single: Unicode - - A string is a sequence of values that represent Unicode code points. - All the code points in the range ``U+0000 - U+10FFFF`` can be - represented in a string. Python doesn't have a :c:expr:`char` type; - instead, every code point in the string is represented as a string - object with length ``1``. The built-in function :func:`ord` - converts a code point from its string form to an integer in the - range ``0 - 10FFFF``; :func:`chr` converts an integer in the range - ``0 - 10FFFF`` to the corresponding length ``1`` string object. - :meth:`str.encode` can be used to convert a :class:`str` to - :class:`bytes` using the given text encoding, and - :meth:`bytes.decode` can be used to achieve the opposite. - - Tuples - .. index:: - pair: object; tuple - pair: singleton; tuple - pair: empty; tuple - - The items of a tuple are arbitrary Python objects. Tuples of two or - more items are formed by comma-separated lists of expressions. A tuple - of one item (a 'singleton') can be formed by affixing a comma to an - expression (an expression by itself does not create a tuple, since - parentheses must be usable for grouping of expressions). An empty - tuple can be formed by an empty pair of parentheses. - - Bytes - .. index:: bytes, byte - - A bytes object is an immutable array. The items are 8-bit bytes, - represented by integers in the range 0 <= x < 256. Bytes literals - (like ``b'abc'``) and the built-in :func:`bytes()` constructor - can be used to create bytes objects. Also, bytes objects can be - decoded to strings via the :meth:`~bytes.decode` method. - - Mutable sequences - .. index:: - pair: object; mutable sequence - pair: object; mutable - pair: assignment; statement - single: subscription - single: slicing - - Mutable sequences can be changed after they are created. The subscription and - slicing notations can be used as the target of assignment and :keyword:`del` - (delete) statements. - - There are currently two intrinsic mutable sequence types: - - Lists - .. index:: pair: object; list - - The items of a list are arbitrary Python objects. Lists are formed by - placing a comma-separated list of expressions in square brackets. (Note - that there are no special cases needed to form lists of length 0 or 1.) - - Byte Arrays - .. index:: bytearray - - A bytearray object is a mutable array. They are created by the built-in - :func:`bytearray` constructor. Aside from being mutable - (and hence unhashable), byte arrays otherwise provide the same interface - and functionality as immutable :class:`bytes` objects. - - .. index:: pair: module; array - - The extension module :mod:`array` provides an additional example of a - mutable sequence type, as does the :mod:`collections` module. + pair: built-in function; chr + pair: built-in function; ord + single: character + single: integer + single: Unicode + + A string is a sequence of values that represent Unicode code points. + All the code points in the range ``U+0000 - U+10FFFF`` can be + represented in a string. Python doesn't have a :c:expr:`char` type; + instead, every code point in the string is represented as a string + object with length ``1``. The built-in function :func:`ord` + converts a code point from its string form to an integer in the + range ``0 - 10FFFF``; :func:`chr` converts an integer in the range + ``0 - 10FFFF`` to the corresponding length ``1`` string object. + :meth:`str.encode` can be used to convert a :class:`str` to + :class:`bytes` using the given text encoding, and + :meth:`bytes.decode` can be used to achieve the opposite. + +Tuples + .. index:: + pair: object; tuple + pair: singleton; tuple + pair: empty; tuple + + The items of a tuple are arbitrary Python objects. Tuples of two or + more items are formed by comma-separated lists of expressions. A tuple + of one item (a 'singleton') can be formed by affixing a comma to an + expression (an expression by itself does not create a tuple, since + parentheses must be usable for grouping of expressions). An empty + tuple can be formed by an empty pair of parentheses. + +Bytes + .. index:: bytes, byte + + A bytes object is an immutable array. The items are 8-bit bytes, + represented by integers in the range 0 <= x < 256. Bytes literals + (like ``b'abc'``) and the built-in :func:`bytes()` constructor + can be used to create bytes objects. Also, bytes objects can be + decoded to strings via the :meth:`~bytes.decode` method. + + +Mutable sequences +^^^^^^^^^^^^^^^^^ + +.. index:: + pair: object; mutable sequence + pair: object; mutable + pair: assignment; statement + single: subscription + single: slicing + +Mutable sequences can be changed after they are created. The subscription and +slicing notations can be used as the target of assignment and :keyword:`del` +(delete) statements. + +.. note:: + .. index:: pair: module; array + .. index:: pair: module; collections + + The :mod:`collections` and :mod:`array` module provide + additional examples of mutable sequence types. + +There are currently two intrinsic mutable sequence types: + +Lists + .. index:: pair: object; list + + The items of a list are arbitrary Python objects. Lists are formed by + placing a comma-separated list of expressions in square brackets. (Note + that there are no special cases needed to form lists of length 0 or 1.) + +Byte Arrays + .. index:: bytearray + + A bytearray object is a mutable array. They are created by the built-in + :func:`bytearray` constructor. Aside from being mutable + (and hence unhashable), byte arrays otherwise provide the same interface + and functionality as immutable :class:`bytes` objects. + Set types - .. index:: - pair: built-in function; len - pair: object; set type +--------- + +.. index:: + pair: built-in function; len + pair: object; set type + +These represent unordered, finite sets of unique, immutable objects. As such, +they cannot be indexed by any subscript. However, they can be iterated over, and +the built-in function :func:`len` returns the number of items in a set. Common +uses for sets are fast membership testing, removing duplicates from a sequence, +and computing mathematical operations such as intersection, union, difference, +and symmetric difference. - These represent unordered, finite sets of unique, immutable objects. As such, - they cannot be indexed by any subscript. However, they can be iterated over, and - the built-in function :func:`len` returns the number of items in a set. Common - uses for sets are fast membership testing, removing duplicates from a sequence, - and computing mathematical operations such as intersection, union, difference, - and symmetric difference. +For set elements, the same immutability rules apply as for dictionary keys. Note +that numeric types obey the normal rules for numeric comparison: if two numbers +compare equal (e.g., ``1`` and ``1.0``), only one of them can be contained in a +set. - For set elements, the same immutability rules apply as for dictionary keys. Note - that numeric types obey the normal rules for numeric comparison: if two numbers - compare equal (e.g., ``1`` and ``1.0``), only one of them can be contained in a - set. +There are currently two intrinsic set types: - There are currently two intrinsic set types: - Sets - .. index:: pair: object; set +Sets + .. index:: pair: object; set - These represent a mutable set. They are created by the built-in :func:`set` - constructor and can be modified afterwards by several methods, such as - :meth:`~set.add`. + These represent a mutable set. They are created by the built-in :func:`set` + constructor and can be modified afterwards by several methods, such as + :meth:`~set.add`. - Frozen sets - .. index:: pair: object; frozenset - These represent an immutable set. They are created by the built-in - :func:`frozenset` constructor. As a frozenset is immutable and - :term:`hashable`, it can be used again as an element of another set, or as - a dictionary key. +Frozen sets + .. index:: pair: object; frozenset + + These represent an immutable set. They are created by the built-in + :func:`frozenset` constructor. As a frozenset is immutable and + :term:`hashable`, it can be used again as an element of another set, or as + a dictionary key. + Mappings - .. index:: - pair: built-in function; len - single: subscription - pair: object; mapping - - These represent finite sets of objects indexed by arbitrary index sets. The - subscript notation ``a[k]`` selects the item indexed by ``k`` from the mapping - ``a``; this can be used in expressions and as the target of assignments or - :keyword:`del` statements. The built-in function :func:`len` returns the number - of items in a mapping. - - There is currently a single intrinsic mapping type: - - Dictionaries - .. index:: pair: object; dictionary - - These represent finite sets of objects indexed by nearly arbitrary values. The - only types of values not acceptable as keys are values containing lists or - dictionaries or other mutable types that are compared by value rather than by - object identity, the reason being that the efficient implementation of - dictionaries requires a key's hash value to remain constant. Numeric types used - for keys obey the normal rules for numeric comparison: if two numbers compare - equal (e.g., ``1`` and ``1.0``) then they can be used interchangeably to index - the same dictionary entry. - - Dictionaries preserve insertion order, meaning that keys will be produced - in the same order they were added sequentially over the dictionary. - Replacing an existing key does not change the order, however removing a key - and re-inserting it will add it to the end instead of keeping its old place. - - Dictionaries are mutable; they can be created by the ``{...}`` notation (see - section :ref:`dict`). - - .. index:: - pair: module; dbm.ndbm - pair: module; dbm.gnu - - The extension modules :mod:`dbm.ndbm` and :mod:`dbm.gnu` provide - additional examples of mapping types, as does the :mod:`collections` - module. - - .. versionchanged:: 3.7 - Dictionaries did not preserve insertion order in versions of Python before 3.6. - In CPython 3.6, insertion order was preserved, but it was considered - an implementation detail at that time rather than a language guarantee. +-------- + +.. index:: + pair: built-in function; len + single: subscription + pair: object; mapping + +These represent finite sets of objects indexed by arbitrary index sets. The +subscript notation ``a[k]`` selects the item indexed by ``k`` from the mapping +``a``; this can be used in expressions and as the target of assignments or +:keyword:`del` statements. The built-in function :func:`len` returns the number +of items in a mapping. + +There is currently a single intrinsic mapping type: + + +Dictionaries +^^^^^^^^^^^^ + +.. index:: pair: object; dictionary + +These represent finite sets of objects indexed by nearly arbitrary values. The +only types of values not acceptable as keys are values containing lists or +dictionaries or other mutable types that are compared by value rather than by +object identity, the reason being that the efficient implementation of +dictionaries requires a key's hash value to remain constant. Numeric types used +for keys obey the normal rules for numeric comparison: if two numbers compare +equal (e.g., ``1`` and ``1.0``) then they can be used interchangeably to index +the same dictionary entry. + +Dictionaries preserve insertion order, meaning that keys will be produced +in the same order they were added sequentially over the dictionary. +Replacing an existing key does not change the order, however removing a key +and re-inserting it will add it to the end instead of keeping its old place. + +Dictionaries are mutable; they can be created by the ``{...}`` notation (see +section :ref:`dict`). + +.. index:: + pair: module; dbm.ndbm + pair: module; dbm.gnu + +The extension modules :mod:`dbm.ndbm` and :mod:`dbm.gnu` provide +additional examples of mapping types, as does the :mod:`collections` +module. + +.. versionchanged:: 3.7 + Dictionaries did not preserve insertion order in versions of Python before 3.6. + In CPython 3.6, insertion order was preserved, but it was considered + an implementation detail at that time rather than a language guarantee. + Callable types - .. index:: - pair: object; callable - pair: function; call - single: invocation - pair: function; argument - - These are the types to which the function call operation (see section - :ref:`calls`) can be applied: - - User-defined functions - .. index:: - pair: user-defined; function - pair: object; function - pair: object; user-defined function - - A user-defined function object is created by a function definition (see - section :ref:`function`). It should be called with an argument list - containing the same number of items as the function's formal parameter - list. - - Special attributes: - - .. tabularcolumns:: |l|L|l| - - .. index:: - single: __doc__ (function attribute) - single: __name__ (function attribute) - single: __module__ (function attribute) - single: __dict__ (function attribute) - single: __defaults__ (function attribute) - single: __closure__ (function attribute) - single: __code__ (function attribute) - single: __globals__ (function attribute) - single: __annotations__ (function attribute) - single: __kwdefaults__ (function attribute) - pair: global; namespace - - +-------------------------+-------------------------------+-----------+ - | Attribute | Meaning | | - +=========================+===============================+===========+ - | :attr:`__doc__` | The function's documentation | Writable | - | | string, or ``None`` if | | - | | unavailable; not inherited by | | - | | subclasses. | | - +-------------------------+-------------------------------+-----------+ - | :attr:`~definition.\ | The function's name. | Writable | - | __name__` | | | - +-------------------------+-------------------------------+-----------+ - | :attr:`~definition.\ | The function's | Writable | - | __qualname__` | :term:`qualified name`. | | - | | | | - | | .. versionadded:: 3.3 | | - +-------------------------+-------------------------------+-----------+ - | :attr:`__module__` | The name of the module the | Writable | - | | function was defined in, or | | - | | ``None`` if unavailable. | | - +-------------------------+-------------------------------+-----------+ - | :attr:`__defaults__` | A tuple containing default | Writable | - | | argument values for those | | - | | arguments that have defaults, | | - | | or ``None`` if no arguments | | - | | have a default value. | | - +-------------------------+-------------------------------+-----------+ - | :attr:`__code__` | The code object representing | Writable | - | | the compiled function body. | | - +-------------------------+-------------------------------+-----------+ - | :attr:`__globals__` | A reference to the dictionary | Read-only | - | | that holds the function's | | - | | global variables --- the | | - | | global namespace of the | | - | | module in which the function | | - | | was defined. | | - +-------------------------+-------------------------------+-----------+ - | :attr:`~object.__dict__`| The namespace supporting | Writable | - | | arbitrary function | | - | | attributes. | | - +-------------------------+-------------------------------+-----------+ - | :attr:`__closure__` | ``None`` or a tuple of cells | Read-only | - | | that contain bindings for the | | - | | function's free variables. | | - | | See below for information on | | - | | the ``cell_contents`` | | - | | attribute. | | - +-------------------------+-------------------------------+-----------+ - | :attr:`__annotations__` | A dict containing annotations | Writable | - | | of parameters. The keys of | | - | | the dict are the parameter | | - | | names, and ``'return'`` for | | - | | the return annotation, if | | - | | provided. For more | | - | | information on working with | | - | | this attribute, see | | - | | :ref:`annotations-howto`. | | - +-------------------------+-------------------------------+-----------+ - | :attr:`__kwdefaults__` | A dict containing defaults | Writable | - | | for keyword-only parameters. | | - +-------------------------+-------------------------------+-----------+ - - Most of the attributes labelled "Writable" check the type of the assigned value. - - Function objects also support getting and setting arbitrary attributes, which - can be used, for example, to attach metadata to functions. Regular attribute - dot-notation is used to get and set such attributes. *Note that the current - implementation only supports function attributes on user-defined functions. - Function attributes on built-in functions may be supported in the future.* - - A cell object has the attribute ``cell_contents``. This can be used to get - the value of the cell, as well as set the value. - - Additional information about a function's definition can be retrieved from its - code object; see the description of internal types below. The - :data:`cell ` type can be accessed in the :mod:`types` - module. - - Instance methods - .. index:: - pair: object; method - pair: object; user-defined method - pair: user-defined; method - - An instance method object combines a class, a class instance and any - callable object (normally a user-defined function). - - .. index:: - single: __func__ (method attribute) - single: __self__ (method attribute) - single: __doc__ (method attribute) - 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. - - Methods also support accessing (but not setting) the arbitrary function - attributes on the underlying 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. - - 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 - 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 - 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 - 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 - 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 - transformation only happens for user-defined functions; other callable - objects (and all non-callable objects) are retrieved without - transformation. It is also important to note that user-defined functions - which are attributes of a class instance are not converted to bound - methods; this *only* happens when the function is an attribute of the - class. - - Generator functions - .. index:: - single: generator; function - single: generator; iterator - - A function or method which uses the :keyword:`yield` statement (see section - :ref:`yield`) is called a :dfn:`generator function`. Such a function, when - called, always returns an :term:`iterator` object which can be used to - execute the body of the function: calling the iterator's - :meth:`iterator.__next__` method will cause the function to execute until - it provides a value using the :keyword:`!yield` statement. When the - function executes a :keyword:`return` statement or falls off the end, a - :exc:`StopIteration` exception is raised and the iterator will have - reached the end of the set of values to be returned. - - Coroutine functions - .. index:: - single: coroutine; function - - A function or method which is defined using :keyword:`async def` is called - a :dfn:`coroutine function`. Such a function, when called, returns a - :term:`coroutine` object. It may contain :keyword:`await` expressions, - as well as :keyword:`async with` and :keyword:`async for` statements. See - also the :ref:`coroutine-objects` section. - - Asynchronous generator functions - .. index:: - single: asynchronous generator; function - single: asynchronous generator; asynchronous iterator - - A function or method which is defined using :keyword:`async def` and - which uses the :keyword:`yield` statement is called a - :dfn:`asynchronous generator function`. Such a function, when called, - returns an :term:`asynchronous iterator` object which can be used in an - :keyword:`async for` statement to execute the body of the function. - - Calling the asynchronous iterator's - :meth:`aiterator.__anext__ ` method - will return an :term:`awaitable` which when awaited - will execute until it provides a value using the :keyword:`yield` - expression. When the function executes an empty :keyword:`return` - statement or falls off the end, a :exc:`StopAsyncIteration` exception - is raised and the asynchronous iterator will have reached the end of - the set of values to be yielded. - - Built-in functions - .. index:: - pair: object; built-in function - pair: object; function - pair: C; language - - A built-in function object is a wrapper around a C function. Examples of - built-in functions are :func:`len` and :func:`math.sin` (:mod:`math` is a - standard built-in module). The number and type of the arguments are - determined by the C function. Special read-only attributes: - :attr:`__doc__` is the function's documentation string, or ``None`` if - unavailable; :attr:`~definition.__name__` is the function's name; :attr:`__self__` is - 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. - - Built-in methods - .. index:: - pair: object; built-in method - pair: object; method - pair: built-in; method - - 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*. - - 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 - initialize the new instance. - - Class Instances - Instances of arbitrary classes can be made callable by defining a - :meth:`~object.__call__` method in their class. +-------------- + +.. index:: + pair: object; callable + pair: function; call + single: invocation + pair: function; argument + +These are the types to which the function call operation (see section +:ref:`calls`) can be applied: + + +User-defined functions +^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: + pair: user-defined; function + pair: object; function + pair: object; user-defined function + +A user-defined function object is created by a function definition (see +section :ref:`function`). It should be called with an argument list +containing the same number of items as the function's formal parameter +list. + +Special attributes: + +.. tabularcolumns:: |l|L|l| + +.. index:: + single: __doc__ (function attribute) + single: __name__ (function attribute) + single: __module__ (function attribute) + single: __dict__ (function attribute) + single: __defaults__ (function attribute) + single: __closure__ (function attribute) + single: __code__ (function attribute) + single: __globals__ (function attribute) + single: __annotations__ (function attribute) + single: __kwdefaults__ (function attribute) + pair: global; namespace + ++-------------------------+-------------------------------+-----------+ +| Attribute | Meaning | | ++=========================+===============================+===========+ +| :attr:`__doc__` | The function's documentation | Writable | +| | string, or ``None`` if | | +| | unavailable; not inherited by | | +| | subclasses. | | ++-------------------------+-------------------------------+-----------+ +| :attr:`~definition.\ | The function's name. | Writable | +| __name__` | | | ++-------------------------+-------------------------------+-----------+ +| :attr:`~definition.\ | The function's | Writable | +| __qualname__` | :term:`qualified name`. | | +| | | | +| | .. versionadded:: 3.3 | | ++-------------------------+-------------------------------+-----------+ +| :attr:`__module__` | The name of the module the | Writable | +| | function was defined in, or | | +| | ``None`` if unavailable. | | ++-------------------------+-------------------------------+-----------+ +| :attr:`__defaults__` | A tuple containing default | Writable | +| | argument values for those | | +| | arguments that have defaults, | | +| | or ``None`` if no arguments | | +| | have a default value. | | ++-------------------------+-------------------------------+-----------+ +| :attr:`__code__` | The code object representing | Writable | +| | the compiled function body. | | ++-------------------------+-------------------------------+-----------+ +| :attr:`__globals__` | A reference to the dictionary | Read-only | +| | that holds the function's | | +| | global variables --- the | | +| | global namespace of the | | +| | module in which the function | | +| | was defined. | | ++-------------------------+-------------------------------+-----------+ +| :attr:`~object.__dict__`| The namespace supporting | Writable | +| | arbitrary function | | +| | attributes. | | ++-------------------------+-------------------------------+-----------+ +| :attr:`__closure__` | ``None`` or a tuple of cells | Read-only | +| | that contain bindings for the | | +| | function's free variables. | | +| | See below for information on | | +| | the ``cell_contents`` | | +| | attribute. | | ++-------------------------+-------------------------------+-----------+ +| :attr:`__annotations__` | A dict containing annotations | Writable | +| | of parameters. The keys of | | +| | the dict are the parameter | | +| | names, and ``'return'`` for | | +| | the return annotation, if | | +| | provided. For more | | +| | information on working with | | +| | this attribute, see | | +| | :ref:`annotations-howto`. | | ++-------------------------+-------------------------------+-----------+ +| :attr:`__kwdefaults__` | A dict containing defaults | Writable | +| | for keyword-only parameters. | | ++-------------------------+-------------------------------+-----------+ + +Most of the attributes labelled "Writable" check the type of the assigned value. + +Function objects also support getting and setting arbitrary attributes, which +can be used, for example, to attach metadata to functions. Regular attribute +dot-notation is used to get and set such attributes. *Note that the current +implementation only supports function attributes on user-defined functions. +Function attributes on built-in functions may be supported in the future.* + +A cell object has the attribute ``cell_contents``. This can be used to get +the value of the cell, as well as set the value. + +Additional information about a function's definition can be retrieved from its +code object; see the description of internal types below. The +:data:`cell ` type can be accessed in the :mod:`types` +module. + + +Instance methods +^^^^^^^^^^^^^^^^ + +.. index:: + pair: object; method + pair: object; user-defined method + pair: user-defined; method + +An instance method object combines a class, a class instance and any +callable object (normally a user-defined function). + +.. index:: + single: __func__ (method attribute) + single: __self__ (method attribute) + single: __doc__ (method attribute) + 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. + +Methods also support accessing (but not setting) the arbitrary function +attributes on the underlying 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. + +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 +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 +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 +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 +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 +transformation only happens for user-defined functions; other callable +objects (and all non-callable objects) are retrieved without +transformation. It is also important to note that user-defined functions +which are attributes of a class instance are not converted to bound +methods; this *only* happens when the function is an attribute of the +class. + + +Generator functions +^^^^^^^^^^^^^^^^^^^ + +.. index:: + single: generator; function + single: generator; iterator + +A function or method which uses the :keyword:`yield` statement (see section +:ref:`yield`) is called a :dfn:`generator function`. Such a function, when +called, always returns an :term:`iterator` object which can be used to +execute the body of the function: calling the iterator's +:meth:`iterator.__next__` method will cause the function to execute until +it provides a value using the :keyword:`!yield` statement. When the +function executes a :keyword:`return` statement or falls off the end, a +:exc:`StopIteration` exception is raised and the iterator will have +reached the end of the set of values to be returned. + + +Coroutine functions +^^^^^^^^^^^^^^^^^^^ + +.. index:: + single: coroutine; function + +A function or method which is defined using :keyword:`async def` is called +a :dfn:`coroutine function`. Such a function, when called, returns a +:term:`coroutine` object. It may contain :keyword:`await` expressions, +as well as :keyword:`async with` and :keyword:`async for` statements. See +also the :ref:`coroutine-objects` section. + + +Asynchronous generator functions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: + single: asynchronous generator; function + single: asynchronous generator; asynchronous iterator + +A function or method which is defined using :keyword:`async def` and +which uses the :keyword:`yield` statement is called a +:dfn:`asynchronous generator function`. Such a function, when called, +returns an :term:`asynchronous iterator` object which can be used in an +:keyword:`async for` statement to execute the body of the function. + +Calling the asynchronous iterator's +:meth:`aiterator.__anext__ ` method +will return an :term:`awaitable` which when awaited +will execute until it provides a value using the :keyword:`yield` +expression. When the function executes an empty :keyword:`return` +statement or falls off the end, a :exc:`StopAsyncIteration` exception +is raised and the asynchronous iterator will have reached the end of +the set of values to be yielded. + + +Built-in functions +^^^^^^^^^^^^^^^^^^ + +.. index:: + pair: object; built-in function + pair: object; function + pair: C; language + +A built-in function object is a wrapper around a C function. Examples of +built-in functions are :func:`len` and :func:`math.sin` (:mod:`math` is a +standard built-in module). The number and type of the arguments are +determined by the C function. Special read-only attributes: +:attr:`__doc__` is the function's documentation string, or ``None`` if +unavailable; :attr:`~definition.__name__` is the function's name; :attr:`__self__` is +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. + + +Built-in methods +^^^^^^^^^^^^^^^^ + +.. index:: + pair: object; built-in method + pair: object; method + pair: built-in; method + +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*. + + +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 +initialize the new instance. + + +Class Instances +^^^^^^^^^^^^^^^ + +Instances of arbitrary classes can be made callable by defining a +:meth:`~object.__call__` method in their class. Modules - .. index:: - pair: statement; import - pair: object; module - - Modules are a basic organizational unit of Python code, and are created by - the :ref:`import system ` as invoked either by the - :keyword:`import` statement, or by calling - functions such as :func:`importlib.import_module` and built-in - :func:`__import__`. A module object has a namespace implemented by a - dictionary object (this is the dictionary referenced by the ``__globals__`` - attribute of functions defined in the module). Attribute references are - translated to lookups in this dictionary, e.g., ``m.x`` is equivalent to - ``m.__dict__["x"]``. A module object does not contain the code object used - to initialize the module (since it isn't needed once the initialization is - done). - - Attribute assignment updates the module's namespace dictionary, e.g., - ``m.x = 1`` is equivalent to ``m.__dict__["x"] = 1``. +------- - .. index:: - single: __name__ (module attribute) - single: __doc__ (module attribute) - single: __file__ (module attribute) - single: __annotations__ (module attribute) - pair: module; namespace +.. index:: + pair: statement; import + pair: object; module + +Modules are a basic organizational unit of Python code, and are created by +the :ref:`import system ` as invoked either by the +:keyword:`import` statement, or by calling +functions such as :func:`importlib.import_module` and built-in +:func:`__import__`. A module object has a namespace implemented by a +dictionary object (this is the dictionary referenced by the ``__globals__`` +attribute of functions defined in the module). Attribute references are +translated to lookups in this dictionary, e.g., ``m.x`` is equivalent to +``m.__dict__["x"]``. A module object does not contain the code object used +to initialize the module (since it isn't needed once the initialization is +done). + +Attribute assignment updates the module's namespace dictionary, e.g., +``m.x = 1`` is equivalent to ``m.__dict__["x"] = 1``. - Predefined (writable) attributes: +.. index:: + single: __name__ (module attribute) + single: __doc__ (module attribute) + single: __file__ (module attribute) + single: __annotations__ (module attribute) + pair: module; namespace - :attr:`__name__` - The module's name. +Predefined (writable) attributes: - :attr:`__doc__` - The module's documentation string, or ``None`` if - unavailable. + :attr:`__name__` + The module's name. - :attr:`__file__` - The pathname of the file from which the - module was loaded, if it was loaded from a file. - The :attr:`__file__` - attribute may be missing for certain types of modules, such as C modules - that are statically linked into the interpreter. For extension modules - loaded dynamically from a shared library, it's the pathname of the shared - library file. + :attr:`__doc__` + The module's documentation string, or ``None`` if + unavailable. - :attr:`__annotations__` - A dictionary containing - :term:`variable annotations ` collected during - module body execution. For best practices on working - with :attr:`__annotations__`, please see :ref:`annotations-howto`. + :attr:`__file__` + The pathname of the file from which the + module was loaded, if it was loaded from a file. + The :attr:`__file__` + attribute may be missing for certain types of modules, such as C modules + that are statically linked into the interpreter. For extension modules + loaded dynamically from a shared library, it's the pathname of the shared + library file. - .. index:: single: __dict__ (module attribute) + :attr:`__annotations__` + A dictionary containing + :term:`variable annotations ` collected during + module body execution. For best practices on working + with :attr:`__annotations__`, please see :ref:`annotations-howto`. - Special read-only attribute: :attr:`~object.__dict__` is the module's - namespace as a dictionary object. +.. index:: single: __dict__ (module attribute) - .. impl-detail:: +Special read-only attribute: :attr:`~object.__dict__` is the module's +namespace as a dictionary object. + +.. impl-detail:: + + Because of the way CPython clears module dictionaries, the module + dictionary will be cleared when the module falls out of scope even if the + dictionary still has live references. To avoid this, copy the dictionary + or keep the module around while using its dictionary directly. - Because of the way CPython clears module dictionaries, the module - dictionary will be cleared when the module falls out of scope even if the - dictionary still has live references. To avoid this, copy the dictionary - or keep the module around while using its dictionary directly. Custom classes - Custom class types are typically created by class definitions (see section - :ref:`class`). A class has a namespace implemented by a dictionary object. - Class attribute references are translated to lookups in this dictionary, e.g., - ``C.x`` is translated to ``C.__dict__["x"]`` (although there are a number of - hooks which allow for other means of locating attributes). When the attribute - name is not found there, the attribute search continues in the base classes. - This search of the base classes uses the C3 method resolution order which - behaves correctly even in the presence of 'diamond' inheritance structures - where there are multiple inheritance paths leading back to a common ancestor. - Additional details on the C3 MRO used by Python can be found in the - documentation accompanying the 2.3 release at - https://www.python.org/download/releases/2.3/mro/. - - .. XXX: Could we add that MRO doc as an appendix to the language ref? +-------------- + +Custom class types are typically created by class definitions (see section +:ref:`class`). A class has a namespace implemented by a dictionary object. +Class attribute references are translated to lookups in this dictionary, e.g., +``C.x`` is translated to ``C.__dict__["x"]`` (although there are a number of +hooks which allow for other means of locating attributes). When the attribute +name is not found there, the attribute search continues in the base classes. +This search of the base classes uses the C3 method resolution order which +behaves correctly even in the presence of 'diamond' inheritance structures +where there are multiple inheritance paths leading back to a common ancestor. +Additional details on the C3 MRO used by Python can be found in the +documentation accompanying the 2.3 release at +https://www.python.org/download/releases/2.3/mro/. + +.. XXX: Could we add that MRO doc as an appendix to the language ref? - .. index:: - pair: object; class - pair: object; class instance - pair: object; instance - pair: class object; call - single: container - pair: object; dictionary - pair: class; attribute +.. index:: + pair: object; class + pair: object; class instance + pair: object; instance + pair: class object; call + single: container + pair: object; dictionary + pair: class; attribute - 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 - 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__`. +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 +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__`. - .. index:: triple: class; attribute; assignment +.. index:: triple: class; attribute; assignment - Class attribute assignments update the class's dictionary, never the dictionary - of a base class. +Class attribute assignments update the class's dictionary, never the dictionary +of a base class. - .. index:: pair: class object; call +.. index:: pair: class object; call - A class object can be called (see above) to yield a class instance (see below). +A class object can be called (see above) to yield a class instance (see below). .. index:: single: __name__ (class attribute) @@ -838,385 +915,412 @@ Custom classes single: __doc__ (class attribute) single: __annotations__ (class attribute) - Special attributes: +Special attributes: + + :attr:`~definition.__name__` + The class name. - :attr:`~definition.__name__` - The class name. + :attr:`__module__` + The name of the module in which the class was defined. - :attr:`__module__` - The name of the module in which the class was defined. + :attr:`~object.__dict__` + The dictionary containing the class's namespace. - :attr:`~object.__dict__` - The dictionary containing the class's namespace. + :attr:`~class.__bases__` + A tuple containing the base classes, in the order of + their occurrence in the base class list. - :attr:`~class.__bases__` - A tuple containing the base classes, in the order of - their occurrence in the base class list. + :attr:`__doc__` + The class's documentation string, or ``None`` if undefined. - :attr:`__doc__` - The class's documentation string, or ``None`` if undefined. + :attr:`__annotations__` + A dictionary containing + :term:`variable annotations ` + collected during class body execution. For best practices on + working with :attr:`__annotations__`, please see + :ref:`annotations-howto`. - :attr:`__annotations__` - A dictionary containing - :term:`variable annotations ` - collected during class body execution. For best practices on - working with :attr:`__annotations__`, please see - :ref:`annotations-howto`. Class instances - .. index:: - pair: object; class instance - pair: object; instance - pair: class; instance - pair: class instance; attribute - - A class instance is created by calling a class object (see above). A class - instance has a namespace implemented as a dictionary which is the first place - 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 - 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 - the class's :attr:`~object.__dict__`. If no class attribute is found, and the - object's class has a :meth:`~object.__getattr__` method, that is called to satisfy - the lookup. - - .. index:: triple: class instance; attribute; assignment - - Attribute assignments and deletions update the instance's dictionary, never a - class's dictionary. If the class has a :meth:`~object.__setattr__` or - :meth:`~object.__delattr__` method, this is called instead of updating the instance - dictionary directly. +--------------- - .. index:: - pair: object; numeric - pair: object; sequence - pair: object; mapping +.. index:: + pair: object; class instance + pair: object; instance + pair: class; instance + pair: class instance; attribute + +A class instance is created by calling a class object (see above). A class +instance has a namespace implemented as a dictionary which is the first place +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 +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 +the class's :attr:`~object.__dict__`. If no class attribute is found, and the +object's class has a :meth:`~object.__getattr__` method, that is called to satisfy +the lookup. + +.. index:: triple: class instance; attribute; assignment + +Attribute assignments and deletions update the instance's dictionary, never a +class's dictionary. If the class has a :meth:`~object.__setattr__` or +:meth:`~object.__delattr__` method, this is called instead of updating the instance +dictionary directly. - Class instances can pretend to be numbers, sequences, or mappings if they have - methods with certain special names. See section :ref:`specialnames`. +.. index:: + pair: object; numeric + pair: object; sequence + pair: object; mapping - .. index:: - single: __dict__ (instance attribute) - single: __class__ (instance attribute) +Class instances can pretend to be numbers, sequences, or mappings if they have +methods with certain special names. See section :ref:`specialnames`. + +.. index:: + single: __dict__ (instance attribute) + single: __class__ (instance attribute) + +Special attributes: :attr:`~object.__dict__` is the attribute dictionary; +:attr:`~instance.__class__` is the instance's class. - Special attributes: :attr:`~object.__dict__` is the attribute dictionary; - :attr:`~instance.__class__` is the instance's class. I/O objects (also known as file objects) - .. index:: - pair: built-in function; open - pair: module; io - single: popen() (in module os) - single: makefile() (socket method) - single: sys.stdin - single: sys.stdout - single: sys.stderr - single: stdio - single: stdin (in module sys) - single: stdout (in module sys) - single: stderr (in module sys) - - A :term:`file object` represents an open file. Various shortcuts are - available to create file objects: the :func:`open` built-in function, and - also :func:`os.popen`, :func:`os.fdopen`, and the - :meth:`~socket.socket.makefile` method of socket objects (and perhaps by - other functions or methods provided by extension modules). - - The objects ``sys.stdin``, ``sys.stdout`` and ``sys.stderr`` are - initialized to file objects corresponding to the interpreter's standard - input, output and error streams; they are all open in text mode and - therefore follow the interface defined by the :class:`io.TextIOBase` - abstract class. +---------------------------------------- + +.. index:: + pair: built-in function; open + pair: module; io + single: popen() (in module os) + single: makefile() (socket method) + single: sys.stdin + single: sys.stdout + single: sys.stderr + single: stdio + single: stdin (in module sys) + single: stdout (in module sys) + single: stderr (in module sys) + +A :term:`file object` represents an open file. Various shortcuts are +available to create file objects: the :func:`open` built-in function, and +also :func:`os.popen`, :func:`os.fdopen`, and the +:meth:`~socket.socket.makefile` method of socket objects (and perhaps by +other functions or methods provided by extension modules). + +The objects ``sys.stdin``, ``sys.stdout`` and ``sys.stderr`` are +initialized to file objects corresponding to the interpreter's standard +input, output and error streams; they are all open in text mode and +therefore follow the interface defined by the :class:`io.TextIOBase` +abstract class. + Internal types - .. index:: - single: internal type - single: types, internal - - A few types used internally by the interpreter are exposed to the user. Their - definitions may change with future versions of the interpreter, but they are - mentioned here for completeness. - - .. index:: bytecode, object; code, code object - - Code objects - Code objects represent *byte-compiled* executable Python code, or :term:`bytecode`. - The difference between a code object and a function object is that the function - object contains an explicit reference to the function's globals (the module in - which it was defined), while a code object contains no context; also the default - argument values are stored in the function object, not in the code object - (because they represent values calculated at run-time). Unlike function - objects, code objects are immutable and contain no references (directly or - indirectly) to mutable objects. - - .. index:: - single: co_argcount (code object attribute) - single: co_posonlyargcount (code object attribute) - single: co_kwonlyargcount (code object attribute) - single: co_code (code object attribute) - single: co_consts (code object attribute) - single: co_filename (code object attribute) - single: co_firstlineno (code object attribute) - single: co_flags (code object attribute) - single: co_lnotab (code object attribute) - single: co_name (code object attribute) - single: co_names (code object attribute) - single: co_nlocals (code object attribute) - single: co_stacksize (code object attribute) - single: co_varnames (code object attribute) - single: co_cellvars (code object attribute) - 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); :attr:`co_stacksize` is the - required stack size; :attr:`co_flags` is an 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 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. - - Future feature declarations (``from __future__ import division``) also use bits - in :attr:`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. - - .. index:: single: documentation string - - If a code object represents a function, the first item in :attr:`co_consts` is - the documentation string of the function, or ``None`` if undefined. - - .. method:: codeobject.co_positions() - - Returns an iterable over the source code positions of each bytecode - instruction in the code object. - - The iterator returns tuples 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 - line. - - This positional information can be missing. A non-exhaustive lists of - cases where this may happen: - - - Running the interpreter with :option:`-X` ``no_debug_ranges``. - - Loading a pyc file compiled while using :option:`-X` ``no_debug_ranges``. - - Position tuples corresponding to artificial instructions. - - Line and column numbers that can't be represented due to - implementation specific limitations. - - When this occurs, some or all of the tuple elements can be - :const:`None`. - - .. versionadded:: 3.11 - - .. note:: - This feature requires storing column positions in code objects which may - result in a small increase of disk usage of compiled Python files or - interpreter memory usage. To avoid storing the extra information and/or - deactivate printing the extra traceback information, the - :option:`-X` ``no_debug_ranges`` command line flag or the :envvar:`PYTHONNODEBUGRANGES` - environment variable can be used. - - .. _frame-objects: - - Frame objects - .. index:: pair: object; frame - - Frame objects represent execution frames. They may occur in traceback objects - (see below), and are also passed to registered trace functions. - - .. index:: - single: f_back (frame attribute) - single: f_code (frame attribute) - single: f_globals (frame attribute) - single: f_locals (frame attribute) - 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). - - Accessing ``f_code`` raises an :ref:`auditing event ` - ``object.__getattr__`` with arguments ``obj`` and ``"f_code"``. - - .. index:: - single: f_trace (frame attribute) - single: f_trace_lines (frame attribute) - 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`. - - 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. - - :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 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 - is finalized. This helps break reference cycles involving frame - objects (for example when catching an exception and storing its - traceback for later use). - - :exc:`RuntimeError` is raised if the frame is currently executing. - - .. versionadded:: 3.4 - - .. _traceback-objects: - - Traceback objects - .. index:: - pair: object; traceback - pair: stack; trace - pair: exception; handler - pair: execution; stack - single: exc_info (in module sys) - single: last_traceback (in module sys) - single: sys.exc_info - single: sys.exception - single: sys.last_traceback - - Traceback objects represent a stack trace of an exception. A traceback object - is implicitly created when an exception occurs, and may also be explicitly - created by calling :class:`types.TracebackType`. - - For implicitly created tracebacks, when the search for an exception handler - unwinds the execution stack, at each unwound level a traceback object is - inserted in front of the current traceback. When an exception handler is - entered, the stack trace is made available to the program. (See section - :ref:`try`.) It is accessible as the third item of the - tuple returned by ``sys.exc_info()``, and as the ``__traceback__`` attribute - of the caught exception. - - When the program contains no suitable - handler, the stack trace is written (nicely formatted) to the standard error - stream; if the interpreter is interactive, it is also made available to the user - as ``sys.last_traceback``. - - For explicitly created tracebacks, it is up to the creator of the traceback - to determine how the ``tb_next`` attributes should be linked to form a - full stack trace. - - .. index:: - single: tb_frame (traceback attribute) - single: tb_lineno (traceback attribute) - single: tb_lasti (traceback attribute) - pair: statement; try - - Special read-only attributes: - :attr:`tb_frame` points to the execution frame of the current level; - :attr:`tb_lineno` gives the line number where the exception occurred; - :attr:`tb_lasti` indicates the precise instruction. - The line number and last instruction in the traceback may differ from the - line number of its frame object if the exception occurred in a - :keyword:`try` statement with no matching except clause or with a - finally clause. - - Accessing ``tb_frame`` raises an :ref:`auditing event ` - ``object.__getattr__`` with arguments ``obj`` and ``"tb_frame"``. - - .. index:: - single: tb_next (traceback attribute) - - Special writable attribute: :attr:`tb_next` is the next level in the stack - trace (towards the frame where the exception occurred), or ``None`` if - there is no next level. - - .. versionchanged:: 3.7 - Traceback objects can now be explicitly instantiated from Python code, - and the ``tb_next`` attribute of existing instances can be updated. - - Slice objects - .. index:: pair: built-in function; slice - - Slice objects are used to represent slices for - :meth:`~object.__getitem__` - methods. They are also created by the built-in :func:`slice` function. - - .. index:: - single: start (slice object attribute) - single: stop (slice object attribute) - single: step (slice object attribute) - - Special read-only attributes: :attr:`~slice.start` is the lower bound; - :attr:`~slice.stop` is the upper bound; :attr:`~slice.step` is the step - value; each is ``None`` if omitted. These attributes can have any type. - - Slice objects support one method: - - .. method:: slice.indices(self, length) - - This method takes a single integer argument *length* and computes - information about the slice that the slice object would describe if - applied to a sequence of *length* items. It returns a tuple of three - integers; respectively these are the *start* and *stop* indices and the - *step* or stride length of the slice. Missing or out-of-bounds indices - are handled in a manner consistent with regular slices. - - Static method objects - Static method objects provide a way of defeating the transformation of function - objects to method objects described above. A static method object is a wrapper - around any other object, usually a user-defined method object. When a static - method object is retrieved from a class or a class instance, the object actually - returned is the wrapped object, which is not subject to any further - transformation. Static method objects are also callable. Static method - objects are created by the built-in :func:`staticmethod` constructor. - - Class method objects - A class method object, like a static method object, is a wrapper around another - object that alters the way in which that object is retrieved from classes and - class instances. The behaviour of class method objects upon such retrieval is - described above, under "User-defined methods". Class method objects are created - by the built-in :func:`classmethod` constructor. +-------------- + +.. index:: + single: internal type + single: types, internal + +A few types used internally by the interpreter are exposed to the user. Their +definitions may change with future versions of the interpreter, but they are +mentioned here for completeness. + +.. index:: bytecode, object; code, code object + + +Code objects +^^^^^^^^^^^^ + +Code objects represent *byte-compiled* executable Python code, or :term:`bytecode`. +The difference between a code object and a function object is that the function +object contains an explicit reference to the function's globals (the module in +which it was defined), while a code object contains no context; also the default +argument values are stored in the function object, not in the code object +(because they represent values calculated at run-time). Unlike function +objects, code objects are immutable and contain no references (directly or +indirectly) to mutable objects. + +.. index:: + single: co_argcount (code object attribute) + single: co_posonlyargcount (code object attribute) + single: co_kwonlyargcount (code object attribute) + single: co_code (code object attribute) + single: co_consts (code object attribute) + single: co_filename (code object attribute) + single: co_firstlineno (code object attribute) + single: co_flags (code object attribute) + single: co_lnotab (code object attribute) + single: co_name (code object attribute) + single: co_names (code object attribute) + single: co_nlocals (code object attribute) + single: co_stacksize (code object attribute) + single: co_varnames (code object attribute) + single: co_cellvars (code object attribute) + 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); :attr:`co_stacksize` is the +required stack size; :attr:`co_flags` is an 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 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. + +Future feature declarations (``from __future__ import division``) also use bits +in :attr:`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. + +.. index:: single: documentation string + +If a code object represents a function, the first item in :attr:`co_consts` is +the documentation string of the function, or ``None`` if undefined. + +.. method:: codeobject.co_positions() + + Returns an iterable over the source code positions of each bytecode + instruction in the code object. + + The iterator returns tuples 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 + line. + + This positional information can be missing. A non-exhaustive lists of + cases where this may happen: + + - Running the interpreter with :option:`-X` ``no_debug_ranges``. + - Loading a pyc file compiled while using :option:`-X` ``no_debug_ranges``. + - Position tuples corresponding to artificial instructions. + - Line and column numbers that can't be represented due to + implementation specific limitations. + + When this occurs, some or all of the tuple elements can be + :const:`None`. + + .. versionadded:: 3.11 + + .. note:: + This feature requires storing column positions in code objects which may + result in a small increase of disk usage of compiled Python files or + interpreter memory usage. To avoid storing the extra information and/or + deactivate printing the extra traceback information, the + :option:`-X` ``no_debug_ranges`` command line flag or the :envvar:`PYTHONNODEBUGRANGES` + environment variable can be used. + + +.. _frame-objects: + +Frame objects +^^^^^^^^^^^^^ + +.. index:: pair: object; frame + +Frame objects represent execution frames. They may occur in traceback objects +(see below), and are also passed to registered trace functions. + +.. index:: + single: f_back (frame attribute) + single: f_code (frame attribute) + single: f_globals (frame attribute) + single: f_locals (frame attribute) + 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). + +Accessing ``f_code`` raises an :ref:`auditing event ` +``object.__getattr__`` with arguments ``obj`` and ``"f_code"``. + +.. index:: + single: f_trace (frame attribute) + single: f_trace_lines (frame attribute) + 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`. + +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. + +: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 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 + is finalized. This helps break reference cycles involving frame + objects (for example when catching an exception and storing its + traceback for later use). + + :exc:`RuntimeError` is raised if the frame is currently executing. + + .. versionadded:: 3.4 + + +.. _traceback-objects: + +Traceback objects +^^^^^^^^^^^^^^^^^ + +.. index:: + pair: object; traceback + pair: stack; trace + pair: exception; handler + pair: execution; stack + single: exc_info (in module sys) + single: last_traceback (in module sys) + single: sys.exc_info + single: sys.exception + single: sys.last_traceback + +Traceback objects represent a stack trace of an exception. A traceback object +is implicitly created when an exception occurs, and may also be explicitly +created by calling :class:`types.TracebackType`. + +For implicitly created tracebacks, when the search for an exception handler +unwinds the execution stack, at each unwound level a traceback object is +inserted in front of the current traceback. When an exception handler is +entered, the stack trace is made available to the program. (See section +:ref:`try`.) It is accessible as the third item of the +tuple returned by ``sys.exc_info()``, and as the ``__traceback__`` attribute +of the caught exception. + +When the program contains no suitable +handler, the stack trace is written (nicely formatted) to the standard error +stream; if the interpreter is interactive, it is also made available to the user +as ``sys.last_traceback``. + +For explicitly created tracebacks, it is up to the creator of the traceback +to determine how the ``tb_next`` attributes should be linked to form a +full stack trace. + +.. index:: + single: tb_frame (traceback attribute) + single: tb_lineno (traceback attribute) + single: tb_lasti (traceback attribute) + pair: statement; try + +Special read-only attributes: +:attr:`tb_frame` points to the execution frame of the current level; +:attr:`tb_lineno` gives the line number where the exception occurred; +:attr:`tb_lasti` indicates the precise instruction. +The line number and last instruction in the traceback may differ from the +line number of its frame object if the exception occurred in a +:keyword:`try` statement with no matching except clause or with a +finally clause. + +Accessing ``tb_frame`` raises an :ref:`auditing event ` +``object.__getattr__`` with arguments ``obj`` and ``"tb_frame"``. + +.. index:: + single: tb_next (traceback attribute) + +Special writable attribute: :attr:`tb_next` is the next level in the stack +trace (towards the frame where the exception occurred), or ``None`` if +there is no next level. + +.. versionchanged:: 3.7 + Traceback objects can now be explicitly instantiated from Python code, + and the ``tb_next`` attribute of existing instances can be updated. + + +Slice objects +^^^^^^^^^^^^^ + +.. index:: pair: built-in function; slice + +Slice objects are used to represent slices for +:meth:`~object.__getitem__` +methods. They are also created by the built-in :func:`slice` function. + +.. index:: + single: start (slice object attribute) + single: stop (slice object attribute) + single: step (slice object attribute) + +Special read-only attributes: :attr:`~slice.start` is the lower bound; +:attr:`~slice.stop` is the upper bound; :attr:`~slice.step` is the step +value; each is ``None`` if omitted. These attributes can have any type. + +Slice objects support one method: + +.. method:: slice.indices(self, length) + + This method takes a single integer argument *length* and computes + information about the slice that the slice object would describe if + applied to a sequence of *length* items. It returns a tuple of three + integers; respectively these are the *start* and *stop* indices and the + *step* or stride length of the slice. Missing or out-of-bounds indices + are handled in a manner consistent with regular slices. + + +Static method objects +^^^^^^^^^^^^^^^^^^^^^ + +Static method objects provide a way of defeating the transformation of function +objects to method objects described above. A static method object is a wrapper +around any other object, usually a user-defined method object. When a static +method object is retrieved from a class or a class instance, the object actually +returned is the wrapped object, which is not subject to any further +transformation. Static method objects are also callable. Static method +objects are created by the built-in :func:`staticmethod` constructor. + + +Class method objects +^^^^^^^^^^^^^^^^^^^^ + +A class method object, like a static method object, is a wrapper around another +object that alters the way in which that object is retrieved from classes and +class instances. The behaviour of class method objects upon such retrieval is +described above, under "User-defined methods". Class method objects are created +by the built-in :func:`classmethod` constructor. .. _specialnames: From cc81f5b61a3a2023c61a1f41892f45a4479a694f Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 25 Aug 2023 09:48:25 -0700 Subject: [PATCH 212/632] [3.11] [3.12] gh-108314: PyDict_GetItemString() mentions UTF-8 (GH-108448) (#108489) [3.12] gh-108314: PyDict_GetItemString() mentions UTF-8 (GH-108448) gh-108314: PyDict_GetItemString() mentions UTF-8 PyDict_GetItemString(), PyDict_SetItemString() and PyDict_DelItemString() expects a UTF-8 encoding string for the key. (cherry picked from commit 9a225d7d5b0530ee73fa00d4816897997a9eb733) Co-authored-by: Victor Stinner --- Doc/c-api/dict.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Doc/c-api/dict.rst b/Doc/c-api/dict.rst index e6919b015837a6..c9233798c138f0 100644 --- a/Doc/c-api/dict.rst +++ b/Doc/c-api/dict.rst @@ -73,7 +73,7 @@ Dictionary Objects .. index:: single: PyUnicode_FromString() Insert *val* into the dictionary *p* using *key* as a key. *key* should - be a :c:expr:`const char*`. The key object is created using + be a :c:expr:`const char*` UTF-8 encoded bytes string. The key object is created using ``PyUnicode_FromString(key)``. Return ``0`` on success or ``-1`` on failure. This function *does not* steal a reference to *val*. @@ -88,7 +88,8 @@ Dictionary Objects .. c:function:: int PyDict_DelItemString(PyObject *p, const char *key) - Remove the entry in dictionary *p* which has a key specified by the string *key*. + Remove the entry in dictionary *p* which has a key specified by the UTF-8 + encoded bytes string *key*. If *key* is not in the dictionary, :exc:`KeyError` is raised. Return ``0`` on success or ``-1`` on failure. @@ -120,7 +121,8 @@ Dictionary Objects .. c:function:: PyObject* PyDict_GetItemString(PyObject *p, const char *key) This is the same as :c:func:`PyDict_GetItem`, but *key* is specified as a - :c:expr:`const char*`, rather than a :c:expr:`PyObject*`. + :c:expr:`const char*` UTF-8 encoded bytes string, rather than a + :c:expr:`PyObject*`. .. note:: From af13f0e91b541f981c1752474f058dfe07d100b1 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 25 Aug 2023 10:28:56 -0700 Subject: [PATCH 213/632] =?UTF-8?q?[3.11]=20gh-102211:=20Document=20`re.{P?= =?UTF-8?q?attern,Match}`=E2=80=99s=20existence=20(GH-102212)=20(#108491)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Philipp A Co-authored-by: Jelle Zijlstra Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Co-authored-by: Alex Waygood --- Doc/library/re.rst | 67 +++++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/Doc/library/re.rst b/Doc/library/re.rst index 798b56aa5d0c35..3693afc956daff 100644 --- a/Doc/library/re.rst +++ b/Doc/library/re.rst @@ -855,18 +855,17 @@ Functions .. function:: search(pattern, string, flags=0) Scan through *string* looking for the first location where the regular expression - *pattern* produces a match, and return a corresponding :ref:`match object - `. Return ``None`` if no position in the string matches the - pattern; note that this is different from finding a zero-length match at some - point in the string. + *pattern* produces a match, and return a corresponding :class:`~re.Match`. Return + ``None`` if no position in the string matches the pattern; note that this is + different from finding a zero-length match at some point in the string. .. function:: match(pattern, string, flags=0) If zero or more characters at the beginning of *string* match the regular - expression *pattern*, return a corresponding :ref:`match object - `. Return ``None`` if the string does not match the pattern; - note that this is different from a zero-length match. + expression *pattern*, return a corresponding :class:`~re.Match`. Return + ``None`` if the string does not match the pattern; note that this is + different from a zero-length match. Note that even in :const:`MULTILINE` mode, :func:`re.match` will only match at the beginning of the string and not at the beginning of each line. @@ -878,9 +877,8 @@ Functions .. function:: fullmatch(pattern, string, flags=0) If the whole *string* matches the regular expression *pattern*, return a - corresponding :ref:`match object `. Return ``None`` if the - string does not match the pattern; note that this is different from a - zero-length match. + corresponding :class:`~re.Match`. Return ``None`` if the string does not match + the pattern; note that this is different from a zero-length match. .. versionadded:: 3.4 @@ -953,7 +951,7 @@ Functions .. function:: finditer(pattern, string, flags=0) - Return an :term:`iterator` yielding :ref:`match objects ` over + Return an :term:`iterator` yielding :class:`~re.Match` objects over all non-overlapping matches for the RE *pattern* in *string*. The *string* is scanned left-to-right, and matches are returned in the order found. Empty matches are included in the result. @@ -981,8 +979,8 @@ Functions 'static PyObject*\npy_myfunc(void)\n{' If *repl* is a function, it is called for every non-overlapping occurrence of - *pattern*. The function takes a single :ref:`match object ` - argument, and returns the replacement string. For example:: + *pattern*. The function takes a single :class:`~re.Match` argument, and returns + the replacement string. For example:: >>> def dashrepl(matchobj): ... if matchobj.group(0) == '-': return ' ' @@ -992,7 +990,7 @@ Functions >>> re.sub(r'\sAND\s', ' & ', 'Baked Beans And Spam', flags=re.IGNORECASE) 'Baked Beans & Spam' - The pattern may be a string or a :ref:`pattern object `. + The pattern may be a string or a :class:`~re.Pattern`. The optional argument *count* is the maximum number of pattern occurrences to be replaced; *count* must be a non-negative integer. If omitted or zero, all @@ -1127,16 +1125,20 @@ Exceptions Regular Expression Objects -------------------------- -Compiled regular expression objects support the following methods and -attributes: +.. class:: Pattern + + Compiled regular expression object returned by :func:`re.compile`. + + .. versionchanged:: 3.9 + :py:class:`re.Pattern` supports ``[]`` to indicate a Unicode (str) or bytes pattern. + See :ref:`types-genericalias`. .. method:: Pattern.search(string[, pos[, endpos]]) Scan through *string* looking for the first location where this regular - expression produces a match, and return a corresponding :ref:`match object - `. Return ``None`` if no position in the string matches the - pattern; note that this is different from finding a zero-length match at some - point in the string. + expression produces a match, and return a corresponding :class:`~re.Match`. + Return ``None`` if no position in the string matches the pattern; note that + this is different from finding a zero-length match at some point in the string. The optional second parameter *pos* gives an index in the string where the search is to start; it defaults to ``0``. This is not completely equivalent to @@ -1160,9 +1162,9 @@ attributes: .. method:: Pattern.match(string[, pos[, endpos]]) If zero or more characters at the *beginning* of *string* match this regular - expression, return a corresponding :ref:`match object `. - Return ``None`` if the string does not match the pattern; note that this is - different from a zero-length match. + expression, return a corresponding :class:`~re.Match`. Return ``None`` if the + string does not match the pattern; note that this is different from a + zero-length match. The optional *pos* and *endpos* parameters have the same meaning as for the :meth:`~Pattern.search` method. :: @@ -1179,8 +1181,8 @@ attributes: .. method:: Pattern.fullmatch(string[, pos[, endpos]]) If the whole *string* matches this regular expression, return a corresponding - :ref:`match object `. Return ``None`` if the string does not - match the pattern; note that this is different from a zero-length match. + :class:`~re.Match`. Return ``None`` if the string does not match the pattern; + note that this is different from a zero-length match. The optional *pos* and *endpos* parameters have the same meaning as for the :meth:`~Pattern.search` method. :: @@ -1266,8 +1268,13 @@ when there is no match, you can test whether there was a match with a simple if match: process(match) -Match objects support the following methods and attributes: +.. class:: Match + + Match object returned by successful ``match``\ es and ``search``\ es. + .. versionchanged:: 3.9 + :py:class:`re.Match` supports ``[]`` to indicate a Unicode (str) or bytes match. + See :ref:`types-genericalias`. .. method:: Match.expand(template) @@ -1710,10 +1717,10 @@ Finding all Adverbs and their Positions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If one wants more information about all matches of a pattern than the matched -text, :func:`finditer` is useful as it provides :ref:`match objects -` instead of strings. Continuing with the previous example, if -a writer wanted to find all of the adverbs *and their positions* in -some text, they would use :func:`finditer` in the following manner:: +text, :func:`finditer` is useful as it provides :class:`~re.Match` objects +instead of strings. Continuing with the previous example, if a writer wanted +to find all of the adverbs *and their positions* in some text, they would use +:func:`finditer` in the following manner:: >>> text = "He was carefully disguised but captured quickly by police." >>> for m in re.finditer(r"\w+ly\b", text): From a44dcfde1893a7e050c72180f9bde6f8083c11fe Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 26 Aug 2023 22:33:23 -0700 Subject: [PATCH 214/632] [3.11] gh-107453: Document errno.{ECANCELED,EOWNERDEAD,ENOTRECOVERABLE,ENOTSUP} (GH-107486) (#108530) Co-authored-by: qqwqqw689 <114795525+qqwqqw689@users.noreply.github.com> Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/library/errno.rst | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/Doc/library/errno.rst b/Doc/library/errno.rst index 5122c69697ef91..283e8b013265d9 100644 --- a/Doc/library/errno.rst +++ b/Doc/library/errno.rst @@ -511,6 +511,13 @@ defined by the module. The specific list of defined symbols is available as Operation not supported on transport endpoint +.. data:: ENOTSUP + + Operation not supported + + .. versionadded:: 3.2 + + .. data:: EPFNOSUPPORT Protocol family not supported @@ -666,3 +673,24 @@ defined by the module. The specific list of defined symbols is available as .. availability:: WASI, FreeBSD .. versionadded:: 3.11.1 + + +.. data:: ECANCELED + + Operation canceled + + .. versionadded:: 3.2 + + +.. data:: EOWNERDEAD + + Owner died + + .. versionadded:: 3.2 + + +.. data:: ENOTRECOVERABLE + + State not recoverable + + .. versionadded:: 3.2 From 8a275f7c0120c6ebd1cabe2ddba38c2c6a33c958 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 27 Aug 2023 00:57:32 -0700 Subject: [PATCH 215/632] [3.11] gh-105052:update timeit function's description (GH-105060) (#108535) --------- Co-authored-by: Terry Jan Reedy tjreedy@udel.edu Co-authored-by: R (cherry picked from commit 7096a2be33619dc02c06a6dc30aac414a9eba462) --- Doc/library/timeit.rst | 6 ++++-- Lib/timeit.py | 18 ++++++++++++------ ...3-05-29-14-10-24.gh-issue-105052.MGFwbm.rst | 1 + 3 files changed, 17 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/Documentation/2023-05-29-14-10-24.gh-issue-105052.MGFwbm.rst diff --git a/Doc/library/timeit.rst b/Doc/library/timeit.rst index 660a546e721892..b59a6bc3742ae4 100644 --- a/Doc/library/timeit.rst +++ b/Doc/library/timeit.rst @@ -86,9 +86,11 @@ The module defines three convenience functions and a public class: .. versionchanged:: 3.7 Default value of *repeat* changed from 3 to 5. + .. function:: default_timer() - The default timer, which is always :func:`time.perf_counter`. + The default timer, which is always time.perf_counter(), returns float seconds. + An alternative, time.perf_counter_ns, returns integer nanoseconds. .. versionchanged:: 3.3 :func:`time.perf_counter` is now the default timer. @@ -124,7 +126,7 @@ The module defines three convenience functions and a public class: Time *number* executions of the main statement. This executes the setup statement once, and then returns the time it takes to execute the main - statement a number of times, measured in seconds as a float. + statement a number of times. The default timer returns seconds as a float. The argument is the number of times through the loop, defaulting to one million. The main statement, the setup statement and the timer function to be used are passed to the constructor. diff --git a/Lib/timeit.py b/Lib/timeit.py index 9dfd454936e6b8..3250563f4222dd 100755 --- a/Lib/timeit.py +++ b/Lib/timeit.py @@ -50,9 +50,9 @@ """ import gc +import itertools import sys import time -import itertools __all__ = ["Timer", "timeit", "repeat", "default_timer"] @@ -77,9 +77,11 @@ def inner(_it, _timer{init}): return _t1 - _t0 """ + def reindent(src, indent): """Helper to reindent a multi-line statement.""" - return src.replace("\n", "\n" + " "*indent) + return src.replace("\n", "\n" + " " * indent) + class Timer: """Class for timing execution speed of small code snippets. @@ -166,7 +168,7 @@ def timeit(self, number=default_number): To be precise, this executes the setup statement once, and then returns the time it takes to execute the main statement - a number of times, as a float measured in seconds. The + a number of times, as float seconds if using the default timer. The argument is the number of times through the loop, defaulting to one million. The main statement, the setup statement and the timer function to be used are passed to the constructor. @@ -228,16 +230,19 @@ def autorange(self, callback=None): return (number, time_taken) i *= 10 + def timeit(stmt="pass", setup="pass", timer=default_timer, number=default_number, globals=None): """Convenience function to create Timer object and call timeit method.""" return Timer(stmt, setup, timer, globals).timeit(number) + def repeat(stmt="pass", setup="pass", timer=default_timer, repeat=default_repeat, number=default_number, globals=None): """Convenience function to create Timer object and call repeat method.""" return Timer(stmt, setup, timer, globals).repeat(repeat, number) + def main(args=None, *, _wrap_timer=None): """Main program, used when run as a script. @@ -270,7 +275,7 @@ def main(args=None, *, _wrap_timer=None): timer = default_timer stmt = "\n".join(args) or "pass" - number = 0 # auto-determine + number = 0 # auto-determine setup = [] repeat = default_repeat verbose = 0 @@ -287,7 +292,7 @@ def main(args=None, *, _wrap_timer=None): time_unit = a else: print("Unrecognized unit. Please select nsec, usec, msec, or sec.", - file=sys.stderr) + file=sys.stderr) return 2 if o in ("-r", "--repeat"): repeat = int(a) @@ -321,7 +326,7 @@ def callback(number, time_taken): msg = "{num} loop{s} -> {secs:.{prec}g} secs" plural = (number != 1) print(msg.format(num=number, s='s' if plural else '', - secs=time_taken, prec=precision)) + secs=time_taken, prec=precision)) try: number, _ = t.autorange(callback) except: @@ -372,5 +377,6 @@ def format_time(dt): UserWarning, '', 0) return None + if __name__ == "__main__": sys.exit(main()) diff --git a/Misc/NEWS.d/next/Documentation/2023-05-29-14-10-24.gh-issue-105052.MGFwbm.rst b/Misc/NEWS.d/next/Documentation/2023-05-29-14-10-24.gh-issue-105052.MGFwbm.rst new file mode 100644 index 00000000000000..8fdc38d439f54f --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2023-05-29-14-10-24.gh-issue-105052.MGFwbm.rst @@ -0,0 +1 @@ +Update ``timeit`` doc to specify that time in seconds is just the default. From b9fc5363997c0cbb78a8d654f9bf6cac1fc056df Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 27 Aug 2023 15:18:58 +0300 Subject: [PATCH 216/632] [3.11] gh-107913: Fix possible losses of OSError error codes (GH-107930) (GH-108524) Functions like PyErr_SetFromErrno() and SetFromWindowsErr() should be called immediately after using the C API which sets errno or the Windows error code. (cherry picked from commit 2b15536fa94d07e9e286826c23507402313ec7f4) --- ...-08-14-11-18-13.gh-issue-107913.4ooY6i.rst | 3 + Modules/_cursesmodule.c | 2 +- Modules/_io/fileio.c | 12 +- Modules/_io/winconsoleio.c | 2 +- Modules/_localemodule.c | 2 +- Modules/_multiprocessing/semaphore.c | 9 +- Modules/_ssl.c | 8 +- Modules/faulthandler.c | 3 +- Modules/fcntlmodule.c | 5 +- Modules/getpath.c | 3 +- Modules/mmapmodule.c | 2 + Modules/overlapped.c | 3 +- Modules/posixmodule.c | 130 ++++++++++++------ Modules/selectmodule.c | 4 +- Modules/socketmodule.c | 12 +- Objects/unicodeobject.c | 2 +- Python/fileutils.c | 4 +- 17 files changed, 129 insertions(+), 77 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-08-14-11-18-13.gh-issue-107913.4ooY6i.rst diff --git a/Misc/NEWS.d/next/Library/2023-08-14-11-18-13.gh-issue-107913.4ooY6i.rst b/Misc/NEWS.d/next/Library/2023-08-14-11-18-13.gh-issue-107913.4ooY6i.rst new file mode 100644 index 00000000000000..de5e21abfc3ae7 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-08-14-11-18-13.gh-issue-107913.4ooY6i.rst @@ -0,0 +1,3 @@ +Fix possible losses of ``errno`` and ``winerror`` values in :exc:`OSError` +exceptions if they were cleared or modified by the cleanup code before +creating the exception object. diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index c10b2b302c6024..45f3d81ec033bd 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -3075,8 +3075,8 @@ _curses_getwin(PyObject *module, PyObject *file) } datalen = PyBytes_GET_SIZE(data); if (fwrite(PyBytes_AS_STRING(data), 1, datalen, fp) != datalen) { - Py_DECREF(data); PyErr_SetFromErrno(PyExc_OSError); + Py_DECREF(data); goto error; } Py_DECREF(data); diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c index 4496609afcbc26..fee97f266f09d2 100644 --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -389,6 +389,11 @@ _Py_COMP_DIAG_POP if (async_err) goto error; + + if (self->fd < 0) { + PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, nameobj); + goto error; + } } else { PyObject *fdobj; @@ -420,12 +425,7 @@ _Py_COMP_DIAG_POP goto error; } } - fd_is_own = 1; - if (self->fd < 0) { - PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, nameobj); - goto error; - } #ifndef MS_WINDOWS if (_Py_set_inheritable(self->fd, 0, atomic_flag_works) < 0) @@ -1044,8 +1044,8 @@ _io_FileIO_truncate_impl(fileio *self, PyObject *posobj) Py_END_ALLOW_THREADS if (ret != 0) { - Py_DECREF(posobj); PyErr_SetFromErrno(PyExc_OSError); + Py_DECREF(posobj); return NULL; } diff --git a/Modules/_io/winconsoleio.c b/Modules/_io/winconsoleio.c index c8f3481e665abd..89431b1e4c3c7b 100644 --- a/Modules/_io/winconsoleio.c +++ b/Modules/_io/winconsoleio.c @@ -368,8 +368,8 @@ _io__WindowsConsoleIO___init___impl(winconsoleio *self, PyObject *nameobj, else self->fd = _Py_open_osfhandle_noraise(handle, _O_RDONLY | _O_BINARY); if (self->fd < 0) { - CloseHandle(handle); PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, nameobj); + CloseHandle(handle); goto error; } } diff --git a/Modules/_localemodule.c b/Modules/_localemodule.c index 23c38e14d997d1..ac9709cabcd0a3 100644 --- a/Modules/_localemodule.c +++ b/Modules/_localemodule.c @@ -735,8 +735,8 @@ _locale_bindtextdomain_impl(PyObject *module, const char *domain, } current_dirname = bindtextdomain(domain, dirname); if (current_dirname == NULL) { - Py_XDECREF(dirname_bytes); PyErr_SetFromErrno(PyExc_OSError); + Py_XDECREF(dirname_bytes); return NULL; } result = PyUnicode_DecodeLocale(current_dirname, NULL); diff --git a/Modules/_multiprocessing/semaphore.c b/Modules/_multiprocessing/semaphore.c index f5fd3257f066a3..49174d39f1c769 100644 --- a/Modules/_multiprocessing/semaphore.c +++ b/Modules/_multiprocessing/semaphore.c @@ -516,12 +516,12 @@ _multiprocessing_SemLock_impl(PyTypeObject *type, int kind, int value, return result; failure: - if (handle != SEM_FAILED) - SEM_CLOSE(handle); - PyMem_Free(name_copy); if (!PyErr_Occurred()) { _PyMp_SetError(NULL, MP_STANDARD_ERROR); } + if (handle != SEM_FAILED) + SEM_CLOSE(handle); + PyMem_Free(name_copy); return NULL; } @@ -556,8 +556,9 @@ _multiprocessing_SemLock__rebuild_impl(PyTypeObject *type, SEM_HANDLE handle, if (name != NULL) { handle = sem_open(name, 0); if (handle == SEM_FAILED) { + PyErr_SetFromErrno(PyExc_OSError); PyMem_Free(name_copy); - return PyErr_SetFromErrno(PyExc_OSError); + return NULL; } } #endif diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 4ccd1240bac3e6..67ce6e97af9016 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -3898,8 +3898,8 @@ _ssl__SSLContext_load_cert_chain_impl(PySSLContext *self, PyObject *certfile, /* the password callback has already set the error information */ } else if (errno != 0) { - ERR_clear_error(); PyErr_SetFromErrno(PyExc_OSError); + ERR_clear_error(); } else { _setSSLError(get_state_ctx(self), NULL, 0, __FILE__, __LINE__); @@ -3919,8 +3919,8 @@ _ssl__SSLContext_load_cert_chain_impl(PySSLContext *self, PyObject *certfile, /* the password callback has already set the error information */ } else if (errno != 0) { - ERR_clear_error(); PyErr_SetFromErrno(PyExc_OSError); + ERR_clear_error(); } else { _setSSLError(get_state_ctx(self), NULL, 0, __FILE__, __LINE__); @@ -4147,8 +4147,8 @@ _ssl__SSLContext_load_verify_locations_impl(PySSLContext *self, PySSL_END_ALLOW_THREADS if (r != 1) { if (errno != 0) { - ERR_clear_error(); PyErr_SetFromErrno(PyExc_OSError); + ERR_clear_error(); } else { _setSSLError(get_state_ctx(self), NULL, 0, __FILE__, __LINE__); @@ -4195,8 +4195,8 @@ _ssl__SSLContext_load_dh_params(PySSLContext *self, PyObject *filepath) PySSL_END_ALLOW_THREADS if (dh == NULL) { if (errno != 0) { - ERR_clear_error(); PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, filepath); + ERR_clear_error(); } else { _setSSLError(get_state_ctx(self), NULL, 0, __FILE__, __LINE__); diff --git a/Modules/faulthandler.c b/Modules/faulthandler.c index 04995d2e745792..f69f56bfdc9381 100644 --- a/Modules/faulthandler.c +++ b/Modules/faulthandler.c @@ -469,11 +469,10 @@ faulthandler_allocate_stack(void) int err = sigaltstack(&stack, &old_stack); if (err) { + PyErr_SetFromErrno(PyExc_OSError); /* Release the stack to retry sigaltstack() next time */ PyMem_Free(stack.ss_sp); stack.ss_sp = NULL; - - PyErr_SetFromErrno(PyExc_OSError); return -1; } return 0; diff --git a/Modules/fcntlmodule.c b/Modules/fcntlmodule.c index ea9b2bc14a9f24..e4bdd3a493af37 100644 --- a/Modules/fcntlmodule.c +++ b/Modules/fcntlmodule.c @@ -208,11 +208,12 @@ fcntl_ioctl_impl(PyObject *module, int fd, unsigned int code, if (mutate_arg && (len <= IOCTL_BUFSZ)) { memcpy(str, buf, len); } - PyBuffer_Release(&pstr); /* No further access to str below this point */ if (ret < 0) { PyErr_SetFromErrno(PyExc_OSError); + PyBuffer_Release(&pstr); return NULL; } + PyBuffer_Release(&pstr); if (mutate_arg) { return PyLong_FromLong(ret); } @@ -237,8 +238,8 @@ fcntl_ioctl_impl(PyObject *module, int fd, unsigned int code, ret = ioctl(fd, code, buf); Py_END_ALLOW_THREADS if (ret < 0) { - PyBuffer_Release(&pstr); PyErr_SetFromErrno(PyExc_OSError); + PyBuffer_Release(&pstr); return NULL; } PyBuffer_Release(&pstr); diff --git a/Modules/getpath.c b/Modules/getpath.c index bc730fcd7dc757..2a14de0c915e9b 100644 --- a/Modules/getpath.c +++ b/Modules/getpath.c @@ -347,11 +347,12 @@ getpath_readlines(PyObject *Py_UNUSED(self), PyObject *args) return NULL; } FILE *fp = _Py_wfopen(path, L"rb"); - PyMem_Free((void *)path); if (!fp) { PyErr_SetFromErrno(PyExc_OSError); + PyMem_Free((void *)path); return NULL; } + PyMem_Free((void *)path); r = PyList_New(0); if (!r) { diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c index 8ff63f51188ad5..a8d48ec6f30989 100644 --- a/Modules/mmapmodule.c +++ b/Modules/mmapmodule.c @@ -1366,6 +1366,7 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict) prot, flags, fd, offset); + int saved_errno = errno; if (devzero != -1) { close(devzero); } @@ -1373,6 +1374,7 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict) if (m_obj->data == (char *)-1) { m_obj->data = NULL; Py_DECREF(m_obj); + errno = saved_errno; PyErr_SetFromErrno(PyExc_OSError); return NULL; } diff --git a/Modules/overlapped.c b/Modules/overlapped.c index 9334dcaa2ee8c3..d1f2f11d484e20 100644 --- a/Modules/overlapped.c +++ b/Modules/overlapped.c @@ -365,8 +365,9 @@ _overlapped_RegisterWaitWithQueue_impl(PyObject *module, HANDLE Object, &NewWaitObject, Object, PostToQueueCallback, pdata, Milliseconds, WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE)) { + SetFromWindowsErr(0); PyMem_RawFree(pdata); - return SetFromWindowsErr(0); + return NULL; } return Py_BuildValue(F_HANDLE, NewWaitObject); diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index cfadb6a06602fd..7c5c21398befce 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -3810,9 +3810,10 @@ posix_getcwd(int use_bytes) return NULL; } if (!len) { + PyErr_SetFromWindowsErr(0); if (wbuf2 != wbuf) PyMem_RawFree(wbuf2); - return PyErr_SetFromWindowsErr(0); + return NULL; } PyObject *resobj = PyUnicode_FromWideChar(wbuf2, len); @@ -3860,8 +3861,9 @@ posix_getcwd(int use_bytes) return PyErr_NoMemory(); } if (cwd == NULL) { + posix_error(); PyMem_RawFree(buf); - return posix_error(); + return NULL; } PyObject *obj; @@ -4073,8 +4075,8 @@ _listdir_windows_no_opendir(path_t *path, PyObject *list) int error = GetLastError(); if (error == ERROR_FILE_NOT_FOUND) goto exit; - Py_DECREF(list); - list = path_error(path); + path_error(path); + Py_CLEAR(list); goto exit; } do { @@ -4087,14 +4089,12 @@ _listdir_windows_no_opendir(path_t *path, PyObject *list) Py_SETREF(v, PyUnicode_EncodeFSDefault(v)); } if (v == NULL) { - Py_DECREF(list); - list = NULL; + Py_CLEAR(list); break; } if (PyList_Append(list, v) != 0) { Py_DECREF(v); - Py_DECREF(list); - list = NULL; + Py_CLEAR(list); break; } Py_DECREF(v); @@ -4105,8 +4105,8 @@ _listdir_windows_no_opendir(path_t *path, PyObject *list) /* FindNextFile sets error to ERROR_NO_MORE_FILES if it got to the end of the directory. */ if (!result && GetLastError() != ERROR_NO_MORE_FILES) { - Py_DECREF(list); - list = path_error(path); + path_error(path); + Py_CLEAR(list); goto exit; } } while (result == TRUE); @@ -4115,8 +4115,8 @@ _listdir_windows_no_opendir(path_t *path, PyObject *list) if (hFindFile != INVALID_HANDLE_VALUE) { if (FindClose(hFindFile) == FALSE) { if (list != NULL) { - Py_DECREF(list); - list = path_error(path); + path_error(path); + Py_CLEAR(list); } } } @@ -4178,7 +4178,8 @@ _posix_listdir(path_t *path, PyObject *list) } if (dirp == NULL) { - list = path_error(path); + path_error(path); + list = NULL; #ifdef HAVE_FDOPENDIR if (fd != -1) { Py_BEGIN_ALLOW_THREADS @@ -4200,8 +4201,8 @@ _posix_listdir(path_t *path, PyObject *list) if (errno == 0) { break; } else { - Py_DECREF(list); - list = path_error(path); + path_error(path); + Py_CLEAR(list); goto exit; } } @@ -5879,8 +5880,9 @@ os_execv_impl(PyObject *module, path_t *path, PyObject *argv) /* If we get here it's definitely an error */ + posix_error(); free_string_array(argvlist, argc); - return posix_error(); + return NULL; } @@ -6177,11 +6179,12 @@ parse_file_actions(PyObject *file_actions, } errno = posix_spawn_file_actions_addopen(file_actionsp, fd, PyBytes_AS_STRING(path), oflag, (mode_t)mode); - Py_DECREF(path); if (errno) { posix_error(); + Py_DECREF(path); goto fail; } + Py_DECREF(path); break; } case POSIX_SPAWN_CLOSE: { @@ -6578,12 +6581,15 @@ os_spawnv_impl(PyObject *module, int mode, path_t *path, PyObject *argv) _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS + int saved_errno = errno; free_string_array(argvlist, argc); - if (spawnval == -1) - return posix_error(); - else - return Py_BuildValue(_Py_PARSE_INTPTR, spawnval); + if (spawnval == -1) { + errno = saved_errno; + posix_error(); + return NULL; + } + return Py_BuildValue(_Py_PARSE_INTPTR, spawnval); } /*[clinic input] @@ -6797,6 +6803,7 @@ os_fork1_impl(PyObject *module) } PyOS_BeforeFork(); pid = fork1(); + int saved_errno = errno; if (pid == 0) { /* child: this clobbers and resets the import lock. */ PyOS_AfterFork_Child(); @@ -6804,8 +6811,10 @@ os_fork1_impl(PyObject *module) /* parent: release the import lock. */ PyOS_AfterFork_Parent(); } - if (pid == -1) + if (pid == -1) { + errno = saved_errno; return posix_error(); + } return PyLong_FromPid(pid); } #endif /* HAVE_FORK1 */ @@ -6836,6 +6845,7 @@ os_fork_impl(PyObject *module) } PyOS_BeforeFork(); pid = fork(); + int saved_errno = errno; if (pid == 0) { /* child: this clobbers and resets the import lock. */ PyOS_AfterFork_Child(); @@ -6843,8 +6853,10 @@ os_fork_impl(PyObject *module) /* parent: release the import lock. */ PyOS_AfterFork_Parent(); } - if (pid == -1) + if (pid == -1) { + errno = saved_errno; return posix_error(); + } return PyLong_FromPid(pid); } #endif /* HAVE_FORK */ @@ -7382,13 +7394,17 @@ os_openpty_impl(PyObject *module) /* change permission of slave */ if (grantpt(master_fd) < 0) { + int saved_errno = errno; PyOS_setsig(SIGCHLD, sig_saved); + errno = saved_errno; goto posix_error; } /* unlock slave */ if (unlockpt(master_fd) < 0) { + int saved_errno = errno; PyOS_setsig(SIGCHLD, sig_saved); + errno = saved_errno; goto posix_error; } @@ -7741,8 +7757,9 @@ os_getgroups_impl(PyObject *module) n = getgroups(n, grouplist); if (n == -1) { + posix_error(); PyMem_Free(grouplist); - return posix_error(); + return NULL; } PyObject *result = PyList_New(n); @@ -8314,8 +8331,9 @@ os_setgroups(PyObject *module, PyObject *groups) } if (setgroups(len, grouplist) < 0) { + posix_error(); PyMem_Free(grouplist); - return posix_error(); + return NULL; } PyMem_Free(grouplist); Py_RETURN_NONE; @@ -9711,10 +9729,13 @@ os_readv_impl(PyObject *module, int fd, PyObject *buffers) Py_END_ALLOW_THREADS } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + int saved_errno = errno; iov_cleanup(iov, buf, cnt); if (n < 0) { - if (!async_err) + if (!async_err) { + errno = saved_errno; posix_error(); + } return -1; } @@ -9763,8 +9784,11 @@ os_pread_impl(PyObject *module, int fd, Py_ssize_t length, Py_off_t offset) } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); if (n < 0) { + if (!async_err) { + posix_error(); + } Py_DECREF(buffer); - return (!async_err) ? posix_error() : NULL; + return NULL; } if (n != length) _PyBytes_Resize(&buffer, n); @@ -9861,9 +9885,11 @@ os_preadv_impl(PyObject *module, int fd, PyObject *buffers, Py_off_t offset, #endif + int saved_errno = errno; iov_cleanup(iov, buf, cnt); if (n < 0) { if (!async_err) { + errno = saved_errno; posix_error(); } return -1; @@ -10032,24 +10058,26 @@ os_sendfile_impl(PyObject *module, int out_fd, int in_fd, PyObject *offobj, } while (ret < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); _Py_END_SUPPRESS_IPH + int saved_errno = errno; if (sf.headers != NULL) iov_cleanup(sf.headers, hbuf, sf.hdr_cnt); if (sf.trailers != NULL) iov_cleanup(sf.trailers, tbuf, sf.trl_cnt); if (ret < 0) { - if ((errno == EAGAIN) || (errno == EBUSY)) { + if ((saved_errno == EAGAIN) || (saved_errno == EBUSY)) { if (sbytes != 0) { // some data has been sent goto done; } - else { - // no data has been sent; upper application is supposed - // to retry on EAGAIN or EBUSY - return posix_error(); - } + // no data has been sent; upper application is supposed + // to retry on EAGAIN or EBUSY } - return (!async_err) ? posix_error() : NULL; + if (!async_err) { + errno = saved_errno; + posix_error(); + } + return NULL; } goto done; @@ -10366,10 +10394,10 @@ os_writev_impl(PyObject *module, int fd, PyObject *buffers) Py_END_ALLOW_THREADS } while (result < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); - iov_cleanup(iov, buf, cnt); if (result < 0 && !async_err) posix_error(); + iov_cleanup(iov, buf, cnt); return result; } #endif /* HAVE_WRITEV */ @@ -10504,13 +10532,13 @@ os_pwritev_impl(PyObject *module, int fd, PyObject *buffers, Py_off_t offset, #endif - iov_cleanup(iov, buf, cnt); if (result < 0) { if (!async_err) { posix_error(); } - return -1; + result = -1; } + iov_cleanup(iov, buf, cnt); return result; } @@ -11072,12 +11100,13 @@ win32_putenv(PyObject *name, PyObject *value) Prefer _wputenv() to be compatible with C libraries using CRT variables and CRT functions using these variables (ex: getenv()). */ int err = _wputenv(env); - PyMem_Free(env); if (err) { posix_error(); + PyMem_Free(env); return NULL; } + PyMem_Free(env); Py_RETURN_NONE; } @@ -12919,10 +12948,12 @@ os_getxattr_impl(PyObject *module, path_t *path, path_t *attribute, Py_END_ALLOW_THREADS; if (result < 0) { - Py_DECREF(buffer); - if (errno == ERANGE) + if (errno == ERANGE) { + Py_DECREF(buffer); continue; + } path_error(path); + Py_DECREF(buffer); return NULL; } @@ -13688,14 +13719,18 @@ _Py_COMP_DIAG_POP } Py_END_ALLOW_THREADS } + int saved_errno = errno; #if defined(MS_WINDOWS) && !USE_UNICODE_WCHAR_CACHE PyMem_Free(path); #else /* USE_UNICODE_WCHAR_CACHE */ Py_DECREF(ub); #endif /* USE_UNICODE_WCHAR_CACHE */ - if (result != 0) - return path_object_error(self->path); + if (result != 0) { + errno = saved_errno; + path_object_error(self->path); + return NULL; + } return _pystat_fromstructstat(module, &st); } @@ -13891,17 +13926,22 @@ _Py_COMP_DIAG_PUSH _Py_COMP_DIAG_IGNORE_DEPR_DECLS const wchar_t *path = PyUnicode_AsUnicode(unicode); result = LSTAT(path, &stat); + int saved_errno = errno; Py_DECREF(unicode); _Py_COMP_DIAG_POP #else /* USE_UNICODE_WCHAR_CACHE */ wchar_t *path = PyUnicode_AsWideCharString(unicode, NULL); Py_DECREF(unicode); result = LSTAT(path, &stat); + + int saved_errno = errno; PyMem_Free(path); #endif /* USE_UNICODE_WCHAR_CACHE */ - if (result != 0) + if (result != 0) { + errno = saved_errno; return path_object_error(self->path); + } self->win32_file_index = stat.st_ino; self->got_file_index = 1; @@ -14468,12 +14508,12 @@ os_scandir_impl(PyObject *module, path_t *path) iterator->handle = FindFirstFileW(path_strW, &iterator->file_data); Py_END_ALLOW_THREADS - PyMem_Free(path_strW); - if (iterator->handle == INVALID_HANDLE_VALUE) { path_error(&iterator->path); + PyMem_Free(path_strW); goto error; } + PyMem_Free(path_strW); #else /* POSIX */ errno = 0; #ifdef HAVE_FDOPENDIR diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c index 4eea928a2683ae..82102698672874 100644 --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -1290,8 +1290,8 @@ newPyEpoll_Object(PyTypeObject *type, int sizehint, SOCKET fd) self->epfd = fd; } if (self->epfd < 0) { - Py_DECREF(self); PyErr_SetFromErrno(PyExc_OSError); + Py_DECREF(self); return NULL; } @@ -1973,8 +1973,8 @@ newKqueue_Object(PyTypeObject *type, SOCKET fd) self->kqfd = fd; } if (self->kqfd < 0) { - Py_DECREF(self); PyErr_SetFromErrno(PyExc_OSError); + Py_DECREF(self); return NULL; } diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index e6d983afa7da85..db3e0519c63f3d 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -5351,8 +5351,8 @@ sock_initobj_impl(PySocketSockObject *self, int family, int type, int proto, if (!support_wsa_no_inherit) { if (!SetHandleInformation((HANDLE)fd, HANDLE_FLAG_INHERIT, 0)) { - closesocket(fd); PyErr_SetFromWindowsErr(0); + closesocket(fd); return -1; } } @@ -5495,8 +5495,9 @@ socket_gethostname(PyObject *self, PyObject *unused) name, &size)) { + PyErr_SetFromWindowsErr(0); PyMem_Free(name); - return PyErr_SetFromWindowsErr(0); + return NULL; } result = PyUnicode_FromWideChar(name, size); @@ -6083,8 +6084,8 @@ socket_dup(PyObject *self, PyObject *fdobj) return set_error(); if (!SetHandleInformation((HANDLE)newfd, HANDLE_FLAG_INHERIT, 0)) { - closesocket(newfd); PyErr_SetFromWindowsErr(0); + closesocket(newfd); return NULL; } #else @@ -6528,11 +6529,12 @@ socket_inet_ntop(PyObject *self, PyObject *args) /* inet_ntop guarantee NUL-termination of resulting string. */ retval = inet_ntop(af, packed_ip.buf, ip, sizeof(ip)); - PyBuffer_Release(&packed_ip); if (!retval) { PyErr_SetFromErrno(PyExc_OSError); + PyBuffer_Release(&packed_ip); return NULL; } else { + PyBuffer_Release(&packed_ip); return PyUnicode_FromString(retval); } } @@ -6862,8 +6864,8 @@ socket_if_nameindex(PyObject *self, PyObject *arg) ni = if_nameindex(); if (ni == NULL) { - Py_DECREF(list); PyErr_SetFromErrno(PyExc_OSError); + Py_DECREF(list); return NULL; } diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index ffa8b982bdc29f..42e13f069060b8 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -7556,7 +7556,7 @@ decode_code_page_errors(UINT code_page, if (err != ERROR_NO_UNICODE_TRANSLATION && err != ERROR_INSUFFICIENT_BUFFER) { - PyErr_SetFromWindowsErr(0); + PyErr_SetFromWindowsErr(err); goto error; } insize++; diff --git a/Python/fileutils.c b/Python/fileutils.c index b310a6c077d91e..24341dda27a45c 100644 --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -1687,6 +1687,7 @@ _Py_fopen_obj(PyObject *path, const char *mode) Py_END_ALLOW_THREADS } while (f == NULL && errno == EINTR && !(async_err = PyErr_CheckSignals())); + int saved_errno = errno; #if !USE_UNICODE_WCHAR_CACHE PyMem_Free(wpath); #endif /* USE_UNICODE_WCHAR_CACHE */ @@ -1711,13 +1712,14 @@ _Py_fopen_obj(PyObject *path, const char *mode) Py_END_ALLOW_THREADS } while (f == NULL && errno == EINTR && !(async_err = PyErr_CheckSignals())); - + int saved_errno = errno; Py_DECREF(bytes); #endif if (async_err) return NULL; if (f == NULL) { + errno = saved_errno; PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path); return NULL; } From c8e66c47da4be4f9c91d9d54b7c8ea21120cc71d Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 27 Aug 2023 06:12:51 -0700 Subject: [PATCH 217/632] [3.11] Fix grammatical error in stringprep documentation (GH-108414) (#108538) Fix grammatical error in stringprep documentation (GH-108414) Remove the word "them", which didn't make grammatical sense. (cherry picked from commit cd0a8aece974330ef44ffe4e0f2e8aa632e98438) Co-authored-by: Matthew James Kraai Co-authored-by: KRAAI, MATTHEW [VISUS] --- Doc/library/stringprep.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/stringprep.rst b/Doc/library/stringprep.rst index 5cfb533d802db4..c6d78a356d97bc 100644 --- a/Doc/library/stringprep.rst +++ b/Doc/library/stringprep.rst @@ -27,7 +27,7 @@ procedure are part of the profile. One example of a ``stringprep`` profile is ``nameprep``, which is used for internationalized domain names. The module :mod:`stringprep` only exposes the tables from :rfc:`3454`. As these -tables would be very large to represent them as dictionaries or lists, the +tables would be very large to represent as dictionaries or lists, the module uses the Unicode character database internally. The module source code itself was generated using the ``mkstringprep.py`` utility. From 73f89b145849e395420c74ec6b5c39fb2ae4b6a7 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sun, 27 Aug 2023 22:07:56 +0200 Subject: [PATCH 218/632] [3.11] gh-108542: Fix incorrect module name in NEWS entry for gh-105475 (#108543) (#108544) (cherry picked from commit a429eafef2d86eafc007ac19682e7d372c32da31) --- Misc/NEWS.d/3.11.5.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/3.11.5.rst b/Misc/NEWS.d/3.11.5.rst index 765c85964a18bc..575e34e58a00f5 100644 --- a/Misc/NEWS.d/3.11.5.rst +++ b/Misc/NEWS.d/3.11.5.rst @@ -500,7 +500,7 @@ Fix bugs in :mod:`zoneinfo` where exceptions could be overwritten. .. nonce: bTcqS9 .. section: Library -Fix bugs in :mod:`pickle` where exceptions could be overwritten. +Fix bugs in :mod:`errno` where exceptions could be overwritten. .. From bd951cd95b7436057067fd5b02c479008c6ead9e Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sun, 27 Aug 2023 23:22:43 +0200 Subject: [PATCH 219/632] [3.11] gh-107801: Document io.TextIOWrapper.tell (#108265) (#108548) (cherry picked from commit 38afa4af9bfc8297a5ee270c37f3f120a04297ea) --- Doc/library/io.rst | 10 +++++++++- Modules/_io/clinic/textio.c.h | 8 ++++++-- Modules/_io/textio.c | 7 ++++++- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/Doc/library/io.rst b/Doc/library/io.rst index 792bf43d9811bb..25f4d0d5841333 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -1054,13 +1054,21 @@ Text I/O * ``seek(0, SEEK_SET)``: Rewind to the start of the stream. * ``seek(cookie, SEEK_SET)``: Restore a previous position; - *cookie* **must be** a number returned by :meth:`!tell`. + *cookie* **must be** a number returned by :meth:`tell`. * ``seek(0, SEEK_END)``: Fast-forward to the end of the stream. * ``seek(0, SEEK_CUR)``: Leave the current stream position unchanged. Any other argument combinations are invalid, and may raise exceptions. + .. method:: tell() + + Return the stream position as an opaque number. + The return value of :meth:`!tell` can be given as input to :meth:`seek`, + to restore a previous stream position. + + + .. class:: StringIO(initial_value='', newline='\n') A text stream using an in-memory text buffer. It inherits diff --git a/Modules/_io/clinic/textio.c.h b/Modules/_io/clinic/textio.c.h index 91755dc3ed568a..c220b449888fd9 100644 --- a/Modules/_io/clinic/textio.c.h +++ b/Modules/_io/clinic/textio.c.h @@ -526,7 +526,11 @@ _io_TextIOWrapper_seek(textio *self, PyObject *const *args, Py_ssize_t nargs) PyDoc_STRVAR(_io_TextIOWrapper_tell__doc__, "tell($self, /)\n" "--\n" -"\n"); +"\n" +"Return the stream position as an opaque number.\n" +"\n" +"The return value of tell() can be given as input to seek(), to restore a\n" +"previous stream position."); #define _IO_TEXTIOWRAPPER_TELL_METHODDEF \ {"tell", (PyCFunction)_io_TextIOWrapper_tell, METH_NOARGS, _io_TextIOWrapper_tell__doc__}, @@ -689,4 +693,4 @@ _io_TextIOWrapper_close(textio *self, PyObject *Py_UNUSED(ignored)) { return _io_TextIOWrapper_close_impl(self); } -/*[clinic end generated code: output=f9bda53adf576a8e input=a9049054013a1b77]*/ +/*[clinic end generated code: output=4865229ff65da09a input=a9049054013a1b77]*/ diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 403687dac03362..7d5afb4c4441d6 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -2627,11 +2627,16 @@ _io_TextIOWrapper_seek_impl(textio *self, PyObject *cookieObj, int whence) /*[clinic input] _io.TextIOWrapper.tell + +Return the stream position as an opaque number. + +The return value of tell() can be given as input to seek(), to restore a +previous stream position. [clinic start generated code]*/ static PyObject * _io_TextIOWrapper_tell_impl(textio *self) -/*[clinic end generated code: output=4f168c08bf34ad5f input=9a2caf88c24f9ddf]*/ +/*[clinic end generated code: output=4f168c08bf34ad5f input=0852d627d76fb520]*/ { PyObject *res; PyObject *posobj = NULL; From c19713d4a1d7c4033ae18896ab2b6714cf60fb21 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 28 Aug 2023 04:28:19 -0700 Subject: [PATCH 220/632] [3.11] Fix typo in typing docs: Remove redundant backtick (GH-108559) (#108561) Fix typo in typing docs: Remove redundant backtick (GH-108559) (cherry picked from commit 72b615ab015ccff8a92e22c5b5f97fa8aca3ba1f) Co-authored-by: nikkie --- Doc/library/typing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 0419f6799c6646..09332c7c213374 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -440,7 +440,7 @@ contrast, a variable annotated with ``type[C]`` (or themselves -- specifically, it will accept the *class object* of ``C``. For example:: - a = 3 # Has type ``int``` + a = 3 # Has type ``int`` b = int # Has type ``type[int]`` c = type(a) # Also has type ``type[int]`` From 6f6171b33bf096130ba776b9fc518b1181f1e01c Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 28 Aug 2023 15:09:10 +0200 Subject: [PATCH 221/632] [3.11] gh-108550: Speed up sqlite3 tests (#108551) (#108567) Disable the busy handler for all concurrency tests; we have full control over the order of the SQLite C API calls, so we can safely do this. test_sqlite3.test_transactions now completes ~10 times faster than before. Co-authored-by: Serhiy Storchaka --- Lib/test/test_sqlite3/test_dbapi.py | 2 +- Lib/test/test_sqlite3/test_transactions.py | 16 ++++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/Lib/test/test_sqlite3/test_dbapi.py b/Lib/test/test_sqlite3/test_dbapi.py index 899f5cfbd31969..ff86291bc5702f 100644 --- a/Lib/test/test_sqlite3/test_dbapi.py +++ b/Lib/test/test_sqlite3/test_dbapi.py @@ -1837,7 +1837,7 @@ def test_on_conflict_replace(self): @requires_subprocess() class MultiprocessTests(unittest.TestCase): - CONNECTION_TIMEOUT = SHORT_TIMEOUT / 1000. # Defaults to 30 ms + CONNECTION_TIMEOUT = 0 # Disable the busy timeout. def tearDown(self): unlink(TESTFN) diff --git a/Lib/test/test_sqlite3/test_transactions.py b/Lib/test/test_sqlite3/test_transactions.py index a67d72709d39ab..b8d786bbd710cd 100644 --- a/Lib/test/test_sqlite3/test_transactions.py +++ b/Lib/test/test_sqlite3/test_transactions.py @@ -23,21 +23,19 @@ import os, unittest import sqlite3 as sqlite -from test.support import LOOPBACK_TIMEOUT from test.support.os_helper import TESTFN, unlink from test.test_sqlite3.test_dbapi import memory_database -TIMEOUT = LOOPBACK_TIMEOUT / 10 - - class TransactionTests(unittest.TestCase): def setUp(self): - self.con1 = sqlite.connect(TESTFN, timeout=TIMEOUT) + # We can disable the busy handlers, since we control + # the order of SQLite C API operations. + self.con1 = sqlite.connect(TESTFN, timeout=0) self.cur1 = self.con1.cursor() - self.con2 = sqlite.connect(TESTFN, timeout=TIMEOUT) + self.con2 = sqlite.connect(TESTFN, timeout=0) self.cur2 = self.con2.cursor() def tearDown(self): @@ -117,10 +115,8 @@ def test_raise_timeout(self): self.cur2.execute("insert into test(i) values (5)") def test_locking(self): - """ - This tests the improved concurrency with pysqlite 2.3.4. You needed - to roll back con2 before you could commit con1. - """ + # This tests the improved concurrency with pysqlite 2.3.4. You needed + # to roll back con2 before you could commit con1. self.cur1.execute("create table test(i)") self.cur1.execute("insert into test(i) values (5)") with self.assertRaises(sqlite.OperationalError): From 33eee4340ef8f528878b9c0f7580305e29e97af0 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 28 Aug 2023 15:09:33 +0200 Subject: [PATCH 222/632] [3.11] gh-64662: Fix virtual table support in sqlite3.Connection.iterdump (#108340) (#108564) (cherry picked from commit d0160c7c22c8dff0a61c49b5304244df6e36465e) Co-authored-by: Aviv Palivoda --- Lib/sqlite3/dump.py | 20 ++++++++++++------- Lib/test/test_sqlite3/test_dump.py | 20 +++++++++++++++++++ ...3-08-22-22-29-42.gh-issue-64662.jHl_Bt.rst | 2 ++ 3 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-08-22-22-29-42.gh-issue-64662.jHl_Bt.rst diff --git a/Lib/sqlite3/dump.py b/Lib/sqlite3/dump.py index 07b9da10b920f9..1cf8759f8970f1 100644 --- a/Lib/sqlite3/dump.py +++ b/Lib/sqlite3/dump.py @@ -16,6 +16,7 @@ def _iterdump(connection): directly but instead called from the Connection method, iterdump(). """ + writeable_schema = False cu = connection.cursor() yield('BEGIN TRANSACTION;') @@ -42,13 +43,15 @@ def _iterdump(connection): yield('ANALYZE "sqlite_master";') elif table_name.startswith('sqlite_'): continue - # NOTE: Virtual table support not implemented - #elif sql.startswith('CREATE VIRTUAL TABLE'): - # qtable = table_name.replace("'", "''") - # yield("INSERT INTO sqlite_master(type,name,tbl_name,rootpage,sql)"\ - # "VALUES('table','{0}','{0}',0,'{1}');".format( - # qtable, - # sql.replace("''"))) + elif sql.startswith('CREATE VIRTUAL TABLE'): + if not writeable_schema: + writeable_schema = True + yield('PRAGMA writable_schema=ON;') + yield("INSERT INTO sqlite_master(type,name,tbl_name,rootpage,sql)" + "VALUES('table','{0}','{0}',0,'{1}');".format( + table_name.replace("'", "''"), + sql.replace("'", "''"), + )) else: yield('{0};'.format(sql)) @@ -74,6 +77,9 @@ def _iterdump(connection): for name, type, sql in schema_res.fetchall(): yield('{0};'.format(sql)) + if writeable_schema: + yield('PRAGMA writable_schema=OFF;') + # gh-79009: Yield statements concerning the sqlite_sequence table at the # end of the transaction. for row in sqlite_sequence: diff --git a/Lib/test/test_sqlite3/test_dump.py b/Lib/test/test_sqlite3/test_dump.py index d0c24b9c60e613..c3ed3aefef0445 100644 --- a/Lib/test/test_sqlite3/test_dump.py +++ b/Lib/test/test_sqlite3/test_dump.py @@ -117,6 +117,26 @@ def __getitem__(self, index): got = list(self.cx.iterdump()) self.assertEqual(expected, got) + def test_dump_virtual_tables(self): + # gh-64662 + expected = [ + "BEGIN TRANSACTION;", + "PRAGMA writable_schema=ON;", + ("INSERT INTO sqlite_master(type,name,tbl_name,rootpage,sql)" + "VALUES('table','test','test',0,'CREATE VIRTUAL TABLE test USING fts4(example)');"), + "CREATE TABLE 'test_content'(docid INTEGER PRIMARY KEY, 'c0example');", + "CREATE TABLE 'test_docsize'(docid INTEGER PRIMARY KEY, size BLOB);", + ("CREATE TABLE 'test_segdir'(level INTEGER,idx INTEGER,start_block INTEGER," + "leaves_end_block INTEGER,end_block INTEGER,root BLOB,PRIMARY KEY(level, idx));"), + "CREATE TABLE 'test_segments'(blockid INTEGER PRIMARY KEY, block BLOB);", + "CREATE TABLE 'test_stat'(id INTEGER PRIMARY KEY, value BLOB);", + "PRAGMA writable_schema=OFF;", + "COMMIT;" + ] + self.cu.execute("CREATE VIRTUAL TABLE test USING fts4(example)") + actual = list(self.cx.iterdump()) + self.assertEqual(expected, actual) + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2023-08-22-22-29-42.gh-issue-64662.jHl_Bt.rst b/Misc/NEWS.d/next/Library/2023-08-22-22-29-42.gh-issue-64662.jHl_Bt.rst new file mode 100644 index 00000000000000..1b4c33a82b0b86 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-08-22-22-29-42.gh-issue-64662.jHl_Bt.rst @@ -0,0 +1,2 @@ +Fix support for virtual tables in :meth:`sqlite3.Connection.iterdump`. Patch +by Aviv Palivoda. From ed749be3aab081ce4e5d54e0273b96bdea02983f Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 28 Aug 2023 10:44:13 -0700 Subject: [PATCH 223/632] [3.11] gh-105821: Use a raw f-string in test_httpservers.py (GH-105822) (#108576) gh-105821: Use a raw f-string in test_httpservers.py (GH-105822) Use a raw f-string in test_httpservers.py (cherry picked from commit 09ce8c3b48f940eb8865330f029b8069854c3106) Co-authored-by: Pablo Galindo Salgado --- Lib/test/test_httpservers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index cdd1bea754a020..cfd8a101dcc1c1 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -442,10 +442,10 @@ def test_undecodable_filename(self): def test_undecodable_parameter(self): # sanity check using a valid parameter response = self.request(self.base_url + '/?x=123').read() - self.assertRegex(response, f'listing for {self.base_url}/\?x=123'.encode('latin1')) + self.assertRegex(response, rf'listing for {self.base_url}/\?x=123'.encode('latin1')) # now the bogus encoding response = self.request(self.base_url + '/?x=%bb').read() - self.assertRegex(response, f'listing for {self.base_url}/\?x=\xef\xbf\xbd'.encode('latin1')) + self.assertRegex(response, rf'listing for {self.base_url}/\?x=\xef\xbf\xbd'.encode('latin1')) def test_get_dir_redirect_location_domain_injection_bug(self): """Ensure //evil.co/..%2f../../X does not put //evil.co/ in Location. From b1ed10f41363dd2460837fce33f09027a799362b Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Tue, 29 Aug 2023 09:56:14 +0100 Subject: [PATCH 224/632] [3.11] Fix misc doc typos (GH-108592) (#108613) (cherry picked from commit 88f1c5b) Co-authored-by: xzmeng --- Doc/library/idle.rst | 2 +- Doc/library/sqlite3.rst | 2 +- Doc/library/tkinter.rst | 2 +- Doc/using/configure.rst | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Doc/library/idle.rst b/Doc/library/idle.rst index 3058bcead661f3..3211da50dc745c 100644 --- a/Doc/library/idle.rst +++ b/Doc/library/idle.rst @@ -479,7 +479,7 @@ Search and Replace Any selection becomes a search target. However, only selections within a line work because searches are only performed within lines with the -terminal newline removed. If ``[x] Regular expresion`` is checked, the +terminal newline removed. If ``[x] Regular expression`` is checked, the target is interpreted according to the Python re module. .. _completions: diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 53c21aed2d6098..f3bcfb5a28f9a3 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -1380,7 +1380,7 @@ Cursor objects :raises ProgrammingError: If *sql* contains more than one SQL statement, - or is not a DML statment. + or is not a DML statement. Example: diff --git a/Doc/library/tkinter.rst b/Doc/library/tkinter.rst index 2aa34422703872..246abf374b0219 100644 --- a/Doc/library/tkinter.rst +++ b/Doc/library/tkinter.rst @@ -352,7 +352,7 @@ Understanding How Tkinter Wraps Tcl/Tk When your application uses Tkinter's classes and methods, internally Tkinter is assembling strings representing Tcl/Tk commands, and executing those -commands in the Tcl interpreter attached to your applicaton's :class:`Tk` +commands in the Tcl interpreter attached to your application's :class:`Tk` instance. Whether it's trying to navigate reference documentation, trying to find diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst index 25ba5084d5daa7..c2b2dee5615ba5 100644 --- a/Doc/using/configure.rst +++ b/Doc/using/configure.rst @@ -179,7 +179,7 @@ Install Options Install architecture-independent files in PREFIX. On Unix, it defaults to :file:`/usr/local`. - This value can be retrived at runtime using :data:`sys.prefix`. + This value can be retrieved at runtime using :data:`sys.prefix`. As an example, one can use ``--prefix="$HOME/.local/"`` to install a Python in its home directory. @@ -188,7 +188,7 @@ Install Options Install architecture-dependent files in EPREFIX, defaults to :option:`--prefix`. - This value can be retrived at runtime using :data:`sys.exec_prefix`. + This value can be retrieved at runtime using :data:`sys.exec_prefix`. .. cmdoption:: --disable-test-modules From cc12c965af16550bb1af32e0485217ae6d718218 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 29 Aug 2023 02:47:27 -0700 Subject: [PATCH 225/632] [3.11] gh-108558: Improve sqlite3 row factory tests (GH-108578) (#108616) Add test_sqlite_row_keys() to explicitly test sqlite3.Row.keys(). Cleanups: - Reduce test noise by converting docstrings to regular comments - Reduce boilerplate code by adding a setUp() method to RowFactoryTests (cherry picked from commit 6eaddc10e972273c1aed8b88c538e65e4773496e) Co-authored-by: Edward Schauman-Haigh <142528725+EddInSverige@users.noreply.github.com> Co-authored-by: Erlend E. Aasland --- Lib/test/test_sqlite3/test_factory.py | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/Lib/test/test_sqlite3/test_factory.py b/Lib/test/test_sqlite3/test_factory.py index 2b70093bd9b68d..67e81f77add47d 100644 --- a/Lib/test/test_sqlite3/test_factory.py +++ b/Lib/test/test_sqlite3/test_factory.py @@ -111,6 +111,7 @@ def tearDown(self): class RowFactoryTests(unittest.TestCase): def setUp(self): self.con = sqlite.connect(":memory:") + self.con.row_factory = sqlite.Row def test_custom_factory(self): self.con.row_factory = lambda cur, row: list(row) @@ -118,7 +119,6 @@ def test_custom_factory(self): self.assertIsInstance(row, list) def test_sqlite_row_index(self): - self.con.row_factory = sqlite.Row row = self.con.execute("select 1 as a_1, 2 as b").fetchone() self.assertIsInstance(row, sqlite.Row) @@ -149,7 +149,6 @@ def test_sqlite_row_index(self): row[complex()] # index must be int or string def test_sqlite_row_index_unicode(self): - self.con.row_factory = sqlite.Row row = self.con.execute("select 1 as \xff").fetchone() self.assertEqual(row["\xff"], 1) with self.assertRaises(IndexError): @@ -159,7 +158,6 @@ def test_sqlite_row_index_unicode(self): def test_sqlite_row_slice(self): # A sqlite.Row can be sliced like a list. - self.con.row_factory = sqlite.Row row = self.con.execute("select 1, 2, 3, 4").fetchone() self.assertEqual(row[0:0], ()) self.assertEqual(row[0:1], (1,)) @@ -176,8 +174,7 @@ def test_sqlite_row_slice(self): self.assertEqual(row[3:0:-2], (4, 2)) def test_sqlite_row_iter(self): - """Checks if the row object is iterable""" - self.con.row_factory = sqlite.Row + # Checks if the row object is iterable. row = self.con.execute("select 1 as a, 2 as b").fetchone() # Is iterable in correct order and produces valid results: @@ -189,23 +186,20 @@ def test_sqlite_row_iter(self): self.assertEqual(items, [1, 2]) def test_sqlite_row_as_tuple(self): - """Checks if the row object can be converted to a tuple""" - self.con.row_factory = sqlite.Row + # Checks if the row object can be converted to a tuple. row = self.con.execute("select 1 as a, 2 as b").fetchone() t = tuple(row) self.assertEqual(t, (row['a'], row['b'])) def test_sqlite_row_as_dict(self): - """Checks if the row object can be correctly converted to a dictionary""" - self.con.row_factory = sqlite.Row + # Checks if the row object can be correctly converted to a dictionary. row = self.con.execute("select 1 as a, 2 as b").fetchone() d = dict(row) self.assertEqual(d["a"], row["a"]) self.assertEqual(d["b"], row["b"]) def test_sqlite_row_hash_cmp(self): - """Checks if the row object compares and hashes correctly""" - self.con.row_factory = sqlite.Row + # Checks if the row object compares and hashes correctly. row_1 = self.con.execute("select 1 as a, 2 as b").fetchone() row_2 = self.con.execute("select 1 as a, 2 as b").fetchone() row_3 = self.con.execute("select 1 as a, 3 as b").fetchone() @@ -238,21 +232,24 @@ def test_sqlite_row_hash_cmp(self): self.assertEqual(hash(row_1), hash(row_2)) def test_sqlite_row_as_sequence(self): - """ Checks if the row object can act like a sequence """ - self.con.row_factory = sqlite.Row + # Checks if the row object can act like a sequence. row = self.con.execute("select 1 as a, 2 as b").fetchone() as_tuple = tuple(row) self.assertEqual(list(reversed(row)), list(reversed(as_tuple))) self.assertIsInstance(row, Sequence) + def test_sqlite_row_keys(self): + # Checks if the row object can return a list of columns as strings. + row = self.con.execute("select 1 as a, 2 as b").fetchone() + self.assertEqual(row.keys(), ['a', 'b']) + def test_fake_cursor_class(self): # Issue #24257: Incorrect use of PyObject_IsInstance() caused # segmentation fault. # Issue #27861: Also applies for cursor factory. class FakeCursor(str): __class__ = sqlite.Cursor - self.con.row_factory = sqlite.Row self.assertRaises(TypeError, self.con.cursor, FakeCursor) self.assertRaises(TypeError, sqlite.Row, FakeCursor(), ()) From 34f84f2b9f60d35a142fcdf2d2b855914b69de6d Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 29 Aug 2023 08:47:54 -0700 Subject: [PATCH 226/632] [3.11] Use non alternate name for Kyiv (GH-108533) (GH-108641) tzdata provides Kiev as an alternative to Kyiv: https://sources.debian.org/src/tzdata/2023c-10/backward/?hl=314GH-L314 But Debian moved it to the tzdata-legacy package breaking the test: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1050530 This patch switches to the name provided by tzdata. (cherry picked from commit 7659128b9d7a30ddbcb063bc12e2ddb0f1f119e0) Co-authored-by: Jochen Sprickerhof --- Lib/test/test_email/test_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_email/test_utils.py b/Lib/test/test_email/test_utils.py index 78afb358035e81..800729a62d871f 100644 --- a/Lib/test/test_email/test_utils.py +++ b/Lib/test/test_email/test_utils.py @@ -148,7 +148,7 @@ def test_localtime_epoch_notz_daylight_false(self): @unittest.skipUnless(os.path.exists('/usr/share/zoneinfo') or os.path.exists('/usr/lib/zoneinfo'), "Can't find the Olson's TZ database") - @test.support.run_with_tz('Europe/Kiev') + @test.support.run_with_tz('Europe/Kyiv') def test_variable_tzname(self): t0 = datetime.datetime(1984, 1, 1, tzinfo=datetime.timezone.utc) t1 = utils.localtime(t0) From d79216d48fae171793868c03d1257c4e07957a0c Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Tue, 29 Aug 2023 21:57:49 +0200 Subject: [PATCH 227/632] [3.11] gh-107801: Improve the accuracy of io.IOBase.seek docs (#108268) (#108656) (cherry picked from commit 8178a88bd81edae87d6974483e4de9b32e808797) - Add param docstrings - Link to os.SEEK_* constants - Mention the return value in the initial paragraph Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/library/io.rst | 26 ++++++++++++++------------ Modules/_io/iobase.c | 21 ++++++++++++++------- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/Doc/library/io.rst b/Doc/library/io.rst index 25f4d0d5841333..01088879218cb4 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -403,20 +403,19 @@ I/O Base Classes Note that it's already possible to iterate on file objects using ``for line in file: ...`` without calling :meth:`!file.readlines`. - .. method:: seek(offset, whence=SEEK_SET, /) + .. method:: seek(offset, whence=os.SEEK_SET, /) - Change the stream position to the given byte *offset*. *offset* is - interpreted relative to the position indicated by *whence*. The default - value for *whence* is :data:`!SEEK_SET`. Values for *whence* are: + Change the stream position to the given byte *offset*, + interpreted relative to the position indicated by *whence*, + and return the new absolute position. + Values for *whence* are: - * :data:`!SEEK_SET` or ``0`` -- start of the stream (the default); + * :data:`os.SEEK_SET` or ``0`` -- start of the stream (the default); *offset* should be zero or positive - * :data:`!SEEK_CUR` or ``1`` -- current stream position; *offset* may - be negative - * :data:`!SEEK_END` or ``2`` -- end of the stream; *offset* is usually - negative - - Return the new absolute position. + * :data:`os.SEEK_CUR` or ``1`` -- current stream position; + *offset* may be negative + * :data:`os.SEEK_END` or ``2`` -- end of the stream; + *offset* is usually negative .. versionadded:: 3.1 The :data:`!SEEK_*` constants. @@ -1061,6 +1060,10 @@ Text I/O Any other argument combinations are invalid, and may raise exceptions. + .. seealso:: + + :data:`os.SEEK_SET`, :data:`os.SEEK_CUR`, and :data:`os.SEEK_END`. + .. method:: tell() Return the stream position as an opaque number. @@ -1068,7 +1071,6 @@ Text I/O to restore a previous stream position. - .. class:: StringIO(initial_value='', newline='\n') A text stream using an in-memory text buffer. It inherits diff --git a/Modules/_io/iobase.c b/Modules/_io/iobase.c index 6ae43a8b3bd626..8424fabca99b6c 100644 --- a/Modules/_io/iobase.c +++ b/Modules/_io/iobase.c @@ -82,15 +82,22 @@ iobase_unsupported(const char *message) /* Positioning */ PyDoc_STRVAR(iobase_seek_doc, - "Change stream position.\n" + "seek($self, offset, whence=os.SEEK_SET, /)\n" + "--\n" "\n" - "Change the stream position to the given byte offset. The offset is\n" - "interpreted relative to the position indicated by whence. Values\n" - "for whence are:\n" + "Change the stream position to the given byte offset.\n" "\n" - "* 0 -- start of stream (the default); offset should be zero or positive\n" - "* 1 -- current stream position; offset may be negative\n" - "* 2 -- end of stream; offset is usually negative\n" + " offset\n" + " The stream position, relative to \'whence\'.\n" + " whence\n" + " The relative position to seek from.\n" + "\n" + "The offset is interpreted relative to the position indicated by whence.\n" + "Values for whence are:\n" + "\n" + "* os.SEEK_SET or 0 -- start of stream (the default); offset should be zero or positive\n" + "* os.SEEK_CUR or 1 -- current stream position; offset may be negative\n" + "* os.SEEK_END or 2 -- end of stream; offset is usually negative\n" "\n" "Return the new absolute position."); From 2e4c3efec17da14d26cd512a7ec97ee8ddd8a9f7 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 29 Aug 2023 23:09:20 +0300 Subject: [PATCH 228/632] [3.11] Revert "[3.11] Use non alternate name for Kyiv (GH-108533) (GH-108641)" (GH-108650) This reverts commit 34f84f2b9f60d35a142fcdf2d2b855914b69de6d. It broke tests on the Debian and macOS buildbots. --- Lib/test/test_email/test_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_email/test_utils.py b/Lib/test/test_email/test_utils.py index 800729a62d871f..78afb358035e81 100644 --- a/Lib/test/test_email/test_utils.py +++ b/Lib/test/test_email/test_utils.py @@ -148,7 +148,7 @@ def test_localtime_epoch_notz_daylight_false(self): @unittest.skipUnless(os.path.exists('/usr/share/zoneinfo') or os.path.exists('/usr/lib/zoneinfo'), "Can't find the Olson's TZ database") - @test.support.run_with_tz('Europe/Kyiv') + @test.support.run_with_tz('Europe/Kiev') def test_variable_tzname(self): t0 = datetime.datetime(1984, 1, 1, tzinfo=datetime.timezone.utc) t1 = utils.localtime(t0) From a2c05a414dc049bd54dc265cfbe957789a20f499 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 29 Aug 2023 23:04:42 -0700 Subject: [PATCH 229/632] [3.11] Mention Ellipsis pickling in the docs (GH-103660) (#108662) Mention Ellipsis pickling in the docs (GH-103660) (cherry picked from commit 14ec0bb7c363def917f768b76f334146a3cddd84) Co-authored-by: sterliakov <50529348+sterliakov@users.noreply.github.com> --- Doc/library/pickle.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/library/pickle.rst b/Doc/library/pickle.rst index 79476b04cd914d..d6be4ba33fa80b 100644 --- a/Doc/library/pickle.rst +++ b/Doc/library/pickle.rst @@ -494,7 +494,8 @@ What can be pickled and unpickled? The following types can be pickled: -* ``None``, ``True``, and ``False``; +* built-in constants (``None``, ``True``, ``False``, ``Ellipsis``, and + ``NotImplemented``); * integers, floating-point numbers, complex numbers; From 5a6d1238bb423717ea81aed064aa13602547ca8e Mon Sep 17 00:00:00 2001 From: Corvin Date: Wed, 30 Aug 2023 06:29:33 -0400 Subject: [PATCH 230/632] [3.11] gh-108590: Fix sqlite3.iterdump for invalid Unicode in TEXT columns (GH-108657) (#108674) (cherry picked from commit 400a1cebc743515e40157ed7af86e48d654290ce) --- Lib/sqlite3/dump.py | 27 +++++++++++++++++-- Lib/test/test_sqlite3/test_dump.py | 15 +++++++++++ ...-08-29-22-53-48.gh-issue-108590.6k0pOl.rst | 1 + 3 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-08-29-22-53-48.gh-issue-108590.6k0pOl.rst diff --git a/Lib/sqlite3/dump.py b/Lib/sqlite3/dump.py index 1cf8759f8970f1..cd5fd79258555f 100644 --- a/Lib/sqlite3/dump.py +++ b/Lib/sqlite3/dump.py @@ -7,6 +7,28 @@ # future enhancements, you should normally quote any identifier that # is an English language word, even if you do not have to." + +from contextlib import contextmanager + + +def _force_decode(bs, *args, **kwargs): + # gh-108590: Don't fail if the database contains invalid Unicode data. + try: + return bs.decode(*args, **kwargs) + except UnicodeDecodeError: + return "".join([chr(c) for c in bs]) + + +@contextmanager +def _text_factory(con, factory): + saved_factory = con.text_factory + con.text_factory = factory + try: + yield + finally: + con.text_factory = saved_factory + + def _iterdump(connection): """ Returns an iterator to the dump of the database in an SQL text format. @@ -63,8 +85,9 @@ def _iterdump(connection): table_name_ident, ",".join("""'||quote("{0}")||'""".format(col.replace('"', '""')) for col in column_names)) query_res = cu.execute(q) - for row in query_res: - yield("{0};".format(row[0])) + with _text_factory(connection, bytes): + for row in query_res: + yield("{0};".format(_force_decode(row[0]))) # Now when the type is 'index', 'trigger', or 'view' q = """ diff --git a/Lib/test/test_sqlite3/test_dump.py b/Lib/test/test_sqlite3/test_dump.py index c3ed3aefef0445..3f6de925368e9e 100644 --- a/Lib/test/test_sqlite3/test_dump.py +++ b/Lib/test/test_sqlite3/test_dump.py @@ -137,6 +137,21 @@ def test_dump_virtual_tables(self): actual = list(self.cx.iterdump()) self.assertEqual(expected, actual) + def test_dump_unicode_invalid(self): + # gh-108590 + expected = [ + "BEGIN TRANSACTION;", + "CREATE TABLE foo (data TEXT);", + "INSERT INTO \"foo\" VALUES('a\x9f');", + "COMMIT;", + ] + self.cu.executescript(""" + CREATE TABLE foo (data TEXT); + INSERT INTO foo VALUES (CAST(X'619f' AS TEXT)); + """) + actual = list(self.cx.iterdump()) + self.assertEqual(expected, actual) + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2023-08-29-22-53-48.gh-issue-108590.6k0pOl.rst b/Misc/NEWS.d/next/Library/2023-08-29-22-53-48.gh-issue-108590.6k0pOl.rst new file mode 100644 index 00000000000000..50b41f2a94d9be --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-08-29-22-53-48.gh-issue-108590.6k0pOl.rst @@ -0,0 +1 @@ +Fixed an issue where :meth:`sqlite3.Connection.iterdump` would fail and leave an incomplete SQL dump if a table includes invalid Unicode sequences. Patch by Corvin McPherson From d06aaff19d3a5a3953f769aedc489048761e9399 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 30 Aug 2023 04:17:23 -0700 Subject: [PATCH 231/632] [3.11] Fix typo in multiprocessing docs (GH-108666) (#108676) Fix typo in multiprocessing docs (GH-108666) (cherry picked from commit 38ab0dba801884b0963ef0daa95e94e120a2b524) Co-authored-by: kato8966 <66937409+kato8966@users.noreply.github.com> --- Doc/library/multiprocessing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/multiprocessing.rst b/Doc/library/multiprocessing.rst index 6cbbcbf1b804a5..236b408499e235 100644 --- a/Doc/library/multiprocessing.rst +++ b/Doc/library/multiprocessing.rst @@ -2963,7 +2963,7 @@ Global variables Safe importing of main module Make sure that the main module can be safely imported by a new Python - interpreter without causing unintended side effects (such a starting a new + interpreter without causing unintended side effects (such as starting a new process). For example, using the *spawn* or *forkserver* start method From f8ab9751da4c1bfed350dcf17a6912241063f5af Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 30 Aug 2023 05:19:44 -0700 Subject: [PATCH 232/632] [3.11] gh-101100: Fix Sphinx warnings in the Logging Cookbook (GH-108678) (#108681) Co-authored-by: Hugo van Kemenade --- Doc/howto/logging-cookbook.rst | 15 +++++++++++---- Doc/tools/.nitignore | 1 - 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index ffb566c1e86db9..bf1e8636019100 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -761,7 +761,7 @@ printed on the console; on the server side, you should see something like: Note that there are some security issues with pickle in some scenarios. If these affect you, you can use an alternative serialization scheme by overriding -the :meth:`~handlers.SocketHandler.makePickle` method and implementing your +the :meth:`~SocketHandler.makePickle` method and implementing your alternative there, as well as adapting the above script to use your alternative serialization. @@ -835,6 +835,8 @@ To test these files, do the following in a POSIX environment: You may need to tweak the configuration files in the unlikely event that the configured ports clash with something else in your test environment. +.. currentmodule:: logging + .. _context-info: Adding contextual information to your logging output @@ -1546,7 +1548,7 @@ Sometimes you want to let a log file grow to a certain size, then open a new file and log to that. You may want to keep a certain number of these files, and when that many files have been created, rotate the files so that the number of files and the size of the files both remain bounded. For this usage pattern, the -logging package provides a :class:`~handlers.RotatingFileHandler`:: +logging package provides a :class:`RotatingFileHandler`:: import glob import logging @@ -1594,6 +1596,8 @@ and each time it reaches the size limit it is renamed with the suffix Obviously this example sets the log length much too small as an extreme example. You would want to set *maxBytes* to an appropriate value. +.. currentmodule:: logging + .. _format-styles: Use of alternative formatting styles @@ -1840,6 +1844,7 @@ However, it should be borne in mind that each link in the chain adds run-time overhead to all logging operations, and the technique should only be used when the use of a :class:`Filter` does not provide the desired result. +.. currentmodule:: logging.handlers .. _zeromq-handlers: @@ -1917,6 +1922,8 @@ of queues, for example a ZeroMQ 'subscribe' socket. Here's an example:: :ref:`A more advanced logging tutorial ` +.. currentmodule:: logging + An example dictionary-based configuration ----------------------------------------- @@ -3918,8 +3925,8 @@ that in other languages such as Java and C#, loggers are often static class attributes. However, this pattern doesn't make sense in Python, where the module (and not the class) is the unit of software decomposition. -Adding handlers other than :class:`NullHandler` to a logger in a library -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Adding handlers other than :class:`~logging.NullHandler` to a logger in a library +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Configuring logging by adding handlers, formatters and filters is the responsibility of the application developer, not the library developer. If you diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 42f1cc052d68af..59b572171f6a2d 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -26,7 +26,6 @@ Doc/glossary.rst Doc/howto/descriptor.rst Doc/howto/enum.rst Doc/howto/isolating-extensions.rst -Doc/howto/logging-cookbook.rst Doc/howto/logging.rst Doc/howto/urllib2.rst Doc/library/__future__.rst From 6f24420cbf1fe31d212497fea024965b36987307 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 30 Aug 2023 22:12:10 +0200 Subject: [PATCH 233/632] [3.11] gh-108590: Revert gh-108657 (commit 400a1cebc) (#108686) (#108694) (cherry picked from commit 2a3926fa51b7264787d5988abf083d8c4328f4ad) Reverted per Serhiy's request. --- Lib/sqlite3/dump.py | 27 ++----------------- Lib/test/test_sqlite3/test_dump.py | 15 ----------- ...-08-29-22-53-48.gh-issue-108590.6k0pOl.rst | 1 - 3 files changed, 2 insertions(+), 41 deletions(-) delete mode 100644 Misc/NEWS.d/next/Library/2023-08-29-22-53-48.gh-issue-108590.6k0pOl.rst diff --git a/Lib/sqlite3/dump.py b/Lib/sqlite3/dump.py index cd5fd79258555f..1cf8759f8970f1 100644 --- a/Lib/sqlite3/dump.py +++ b/Lib/sqlite3/dump.py @@ -7,28 +7,6 @@ # future enhancements, you should normally quote any identifier that # is an English language word, even if you do not have to." - -from contextlib import contextmanager - - -def _force_decode(bs, *args, **kwargs): - # gh-108590: Don't fail if the database contains invalid Unicode data. - try: - return bs.decode(*args, **kwargs) - except UnicodeDecodeError: - return "".join([chr(c) for c in bs]) - - -@contextmanager -def _text_factory(con, factory): - saved_factory = con.text_factory - con.text_factory = factory - try: - yield - finally: - con.text_factory = saved_factory - - def _iterdump(connection): """ Returns an iterator to the dump of the database in an SQL text format. @@ -85,9 +63,8 @@ def _iterdump(connection): table_name_ident, ",".join("""'||quote("{0}")||'""".format(col.replace('"', '""')) for col in column_names)) query_res = cu.execute(q) - with _text_factory(connection, bytes): - for row in query_res: - yield("{0};".format(_force_decode(row[0]))) + for row in query_res: + yield("{0};".format(row[0])) # Now when the type is 'index', 'trigger', or 'view' q = """ diff --git a/Lib/test/test_sqlite3/test_dump.py b/Lib/test/test_sqlite3/test_dump.py index 3f6de925368e9e..c3ed3aefef0445 100644 --- a/Lib/test/test_sqlite3/test_dump.py +++ b/Lib/test/test_sqlite3/test_dump.py @@ -137,21 +137,6 @@ def test_dump_virtual_tables(self): actual = list(self.cx.iterdump()) self.assertEqual(expected, actual) - def test_dump_unicode_invalid(self): - # gh-108590 - expected = [ - "BEGIN TRANSACTION;", - "CREATE TABLE foo (data TEXT);", - "INSERT INTO \"foo\" VALUES('a\x9f');", - "COMMIT;", - ] - self.cu.executescript(""" - CREATE TABLE foo (data TEXT); - INSERT INTO foo VALUES (CAST(X'619f' AS TEXT)); - """) - actual = list(self.cx.iterdump()) - self.assertEqual(expected, actual) - if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2023-08-29-22-53-48.gh-issue-108590.6k0pOl.rst b/Misc/NEWS.d/next/Library/2023-08-29-22-53-48.gh-issue-108590.6k0pOl.rst deleted file mode 100644 index 50b41f2a94d9be..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-08-29-22-53-48.gh-issue-108590.6k0pOl.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed an issue where :meth:`sqlite3.Connection.iterdump` would fail and leave an incomplete SQL dump if a table includes invalid Unicode sequences. Patch by Corvin McPherson From 385b1952f0817fd302ecebb8644f21b4e04a1462 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 30 Aug 2023 13:36:26 -0700 Subject: [PATCH 234/632] [3.11] gh-108520: Fix bad fork detection in nested multiprocessing use case (GH-108568) (#108692) gh-107275 introduced a regression where a SemLock would fail being passed along nested child processes, as the `is_fork_ctx` attribute would be left missing after the first deserialization. --------- (cherry picked from commit add8d45cbe46581b9748909fbbf60fdc8ee8f71e) Co-authored-by: albanD Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Antoine Pitrou --- Lib/multiprocessing/synchronize.py | 8 +++--- Lib/test/_test_multiprocessing.py | 26 +++++++++++++++++++ ...-08-30-15-41-47.gh-issue-108520.u0ZGP_.rst | 3 +++ 3 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-08-30-15-41-47.gh-issue-108520.u0ZGP_.rst diff --git a/Lib/multiprocessing/synchronize.py b/Lib/multiprocessing/synchronize.py index 2328d332123082..3ccbfe311c71f3 100644 --- a/Lib/multiprocessing/synchronize.py +++ b/Lib/multiprocessing/synchronize.py @@ -50,8 +50,8 @@ class SemLock(object): def __init__(self, kind, value, maxvalue, *, ctx): if ctx is None: ctx = context._default_context.get_context() - self.is_fork_ctx = ctx.get_start_method() == 'fork' - unlink_now = sys.platform == 'win32' or self.is_fork_ctx + self._is_fork_ctx = ctx.get_start_method() == 'fork' + unlink_now = sys.platform == 'win32' or self._is_fork_ctx for i in range(100): try: sl = self._semlock = _multiprocessing.SemLock( @@ -103,7 +103,7 @@ def __getstate__(self): if sys.platform == 'win32': h = context.get_spawning_popen().duplicate_for_child(sl.handle) else: - if self.is_fork_ctx: + if self._is_fork_ctx: raise RuntimeError('A SemLock created in a fork context is being ' 'shared with a process in a spawn context. This is ' 'not supported. Please use the same context to create ' @@ -115,6 +115,8 @@ def __setstate__(self, state): self._semlock = _multiprocessing.SemLock._rebuild(*state) util.debug('recreated blocker with handle %r' % state[0]) self._make_methods() + # Ensure that deserialized SemLock can be serialized again (gh-108520). + self._is_fork_ctx = False @staticmethod def _make_name(): diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 9bc8242cbfa26a..3476371f07f7ed 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -5367,6 +5367,32 @@ def test_mixed_startmethod(self): p.start() p.join() + @classmethod + def _put_one_in_queue(cls, queue): + queue.put(1) + + @classmethod + def _put_two_and_nest_once(cls, queue): + queue.put(2) + process = multiprocessing.Process(target=cls._put_one_in_queue, args=(queue,)) + process.start() + process.join() + + def test_nested_startmethod(self): + # gh-108520: Regression test to ensure that child process can send its + # arguments to another process + queue = multiprocessing.Queue() + + process = multiprocessing.Process(target=self._put_two_and_nest_once, args=(queue,)) + process.start() + process.join() + + results = [] + while not queue.empty(): + results.append(queue.get()) + + self.assertEqual(results, [2, 1]) + @unittest.skipIf(sys.platform == "win32", "test semantics don't make sense on Windows") diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-08-30-15-41-47.gh-issue-108520.u0ZGP_.rst b/Misc/NEWS.d/next/Core and Builtins/2023-08-30-15-41-47.gh-issue-108520.u0ZGP_.rst new file mode 100644 index 00000000000000..44131fb11f068c --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-08-30-15-41-47.gh-issue-108520.u0ZGP_.rst @@ -0,0 +1,3 @@ +Fix :meth:`multiprocessing.synchronize.SemLock.__setstate__` to properly initialize :attr:`multiprocessing.synchronize.SemLock._is_fork_ctx`. This fixes a regression when passing a SemLock accross nested processes. + +Rename :attr:`multiprocessing.synchronize.SemLock.is_fork_ctx` to :attr:`multiprocessing.synchronize.SemLock._is_fork_ctx` to avoid exposing it as public API. From e93f57208fe6655e7fcf9af76330b20d13ee11bd Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 31 Aug 2023 01:28:47 -0700 Subject: [PATCH 235/632] [3.11] gh-101100: Fix sphinx warnings in `threading.rst` (GH-108684) (#108708) Co-authored-by: Nikita Sobolev Co-authored-by: Hugo van Kemenade --- Doc/library/threading.rst | 16 ++++++++-------- Doc/tools/.nitignore | 1 - 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Doc/library/threading.rst b/Doc/library/threading.rst index 65416221d9751b..d2c896eacd2e8c 100644 --- a/Doc/library/threading.rst +++ b/Doc/library/threading.rst @@ -254,7 +254,7 @@ The instance's values will be different for separate threads. A class that represents thread-local data. For more details and extensive examples, see the documentation string of the - :mod:`_threading_local` module: :source:`Lib/_threading_local.py`. + :mod:`!_threading_local` module: :source:`Lib/_threading_local.py`. .. _thread-objects: @@ -267,7 +267,7 @@ thread of control. There are two ways to specify the activity: by passing a callable object to the constructor, or by overriding the :meth:`~Thread.run` method in a subclass. No other methods (except for the constructor) should be overridden in a subclass. In other words, *only* override the -:meth:`~Thread.__init__` and :meth:`~Thread.run` methods of this class. +``__init__()`` and :meth:`~Thread.run` methods of this class. Once a thread object is created, its activity must be started by calling the thread's :meth:`~Thread.start` method. This invokes the :meth:`~Thread.run` @@ -319,7 +319,7 @@ since it is impossible to detect the termination of alien threads. are: *group* should be ``None``; reserved for future extension when a - :class:`ThreadGroup` class is implemented. + :class:`!ThreadGroup` class is implemented. *target* is the callable object to be invoked by the :meth:`run` method. Defaults to ``None``, meaning nothing is called. @@ -991,7 +991,7 @@ This class represents an action that should be run only after a certain amount of time has passed --- a timer. :class:`Timer` is a subclass of :class:`Thread` and as such also functions as an example of creating custom threads. -Timers are started, as with threads, by calling their :meth:`~Timer.start` +Timers are started, as with threads, by calling their :meth:`Timer.start ` method. The timer can be stopped (before its action has begun) by calling the :meth:`~Timer.cancel` method. The interval the timer will wait before executing its action may not be exactly the same as the interval specified by @@ -1129,10 +1129,10 @@ As an example, here is a simple way to synchronize a client and server thread:: Using locks, conditions, and semaphores in the :keyword:`!with` statement ------------------------------------------------------------------------- -All of the objects provided by this module that have :meth:`acquire` and -:meth:`release` methods can be used as context managers for a :keyword:`with` -statement. The :meth:`acquire` method will be called when the block is -entered, and :meth:`release` will be called when the block is exited. Hence, +All of the objects provided by this module that have ``acquire`` and +``release`` methods can be used as context managers for a :keyword:`with` +statement. The ``acquire`` method will be called when the block is +entered, and ``release`` will be called when the block is exited. Hence, the following snippet:: with some_lock: diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 59b572171f6a2d..871a48282523c4 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -126,7 +126,6 @@ Doc/library/tarfile.rst Doc/library/tempfile.rst Doc/library/termios.rst Doc/library/test.rst -Doc/library/threading.rst Doc/library/time.rst Doc/library/tkinter.rst Doc/library/tkinter.scrolledtext.rst From b4784b0c5f1cab48b45df5481edc203e25688d0c Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 31 Aug 2023 15:27:27 -0700 Subject: [PATCH 236/632] [3.11] `ast` docs: Fix incorrect link on `keyword` (GH-108728) (#108738) `ast` docs: Fix incorrect link on `keyword` (GH-108728) In two places, Sphinx was erroneously adding links to the `keyword` module instead of the `ast.keyword` class (cherry picked from commit c1e2f3b2f70b8a72ea7e1bf792addf62a94ae65d) Co-authored-by: Alex Povel --- Doc/library/ast.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst index d136f016b9b17a..6392f7318d7a6d 100644 --- a/Doc/library/ast.rst +++ b/Doc/library/ast.rst @@ -585,7 +585,7 @@ Expressions :class:`Name` or :class:`Attribute` object. Of the arguments: * ``args`` holds a list of the arguments passed by position. - * ``keywords`` holds a list of :class:`keyword` objects representing + * ``keywords`` holds a list of :class:`.keyword` objects representing arguments passed by keyword. When creating a ``Call`` node, ``args`` and ``keywords`` are required, but @@ -1925,7 +1925,7 @@ Function and class definitions * ``name`` is a raw string for the class name * ``bases`` is a list of nodes for explicitly specified base classes. - * ``keywords`` is a list of :class:`keyword` nodes, principally for 'metaclass'. + * ``keywords`` is a list of :class:`.keyword` nodes, principally for 'metaclass'. Other keywords will be passed to the metaclass, as per `PEP-3115 `_. * ``body`` is a list of nodes representing the code within the class From 6ba1234c1c643743be15090ecf8da39137c8cbac Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Fri, 1 Sep 2023 01:53:06 -0700 Subject: [PATCH 237/632] [3.11] gh-104372: Drop the GIL around the vfork() call. (#104782) (#104958) gh-104372: Drop the GIL around the vfork() call. (#104782) On Linux where the `subprocess` module can use the `vfork` syscall for faster spawning, prevent the parent process from blocking other threads by dropping the GIL while it waits for the vfork'ed child process `exec` outcome. This prevents spawning a binary from a slow filesystem from blocking the rest of the application. Fixes #104372. (cherry picked from commit d08679212d9af52dd074cd4a6abb440edb944c9c) --- Doc/library/subprocess.rst | 13 +++++++----- ...-05-22-18-39-53.gh-issue-104372.7tDRaK.rst | 5 +++++ Modules/_posixsubprocess.c | 21 +++++++++++++++++-- 3 files changed, 32 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-05-22-18-39-53.gh-issue-104372.7tDRaK.rst diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst index 6ef3c34b4fb81d..377f6938701da2 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -57,10 +57,13 @@ underlying :class:`Popen` interface can be used directly. and combine both streams into one, use ``stdout=PIPE`` and ``stderr=STDOUT`` instead of *capture_output*. - The *timeout* argument is passed to :meth:`Popen.communicate`. If the timeout - expires, the child process will be killed and waited for. The - :exc:`TimeoutExpired` exception will be re-raised after the child process - has terminated. + A *timeout* may be specified in seconds, it is internally passed on to + :meth:`Popen.communicate`. If the timeout expires, the child process will be + killed and waited for. The :exc:`TimeoutExpired` exception will be + re-raised after the child process has terminated. The initial process + creation itself cannot be interrupted on many platform APIs so you are not + guaranteed to see a timeout exception until at least after however long + process creation takes. The *input* argument is passed to :meth:`Popen.communicate` and thus to the subprocess's stdin. If used it must be a byte sequence, or a string if @@ -736,7 +739,7 @@ arguments. code. All of the functions and methods that accept a *timeout* parameter, such as -:func:`call` and :meth:`Popen.communicate` will raise :exc:`TimeoutExpired` if +:func:`run` and :meth:`Popen.communicate` will raise :exc:`TimeoutExpired` if the timeout expires before the process exits. Exceptions defined in this module all inherit from :exc:`SubprocessError`. diff --git a/Misc/NEWS.d/next/Library/2023-05-22-18-39-53.gh-issue-104372.7tDRaK.rst b/Misc/NEWS.d/next/Library/2023-05-22-18-39-53.gh-issue-104372.7tDRaK.rst new file mode 100644 index 00000000000000..ea13ec85543ca2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-05-22-18-39-53.gh-issue-104372.7tDRaK.rst @@ -0,0 +1,5 @@ +On Linux where :mod:`subprocess` can use the ``vfork()`` syscall for faster +spawning, prevent the parent process from blocking other threads by dropping +the GIL while it waits for the vfork'ed child process ``exec()`` outcome. +This prevents spawning a binary from a slow filesystem from blocking the +rest of the application. diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c index ad9daaede430bc..072519c91a085d 100644 --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -537,7 +537,7 @@ reset_signal_handlers(const sigset_t *child_sigmask) * required by POSIX but not supported natively on Linux. Another reason to * avoid this family of functions is that sharing an address space between * processes running with different privileges is inherently insecure. - * See bpo-35823 for further discussion and references. + * See https://bugs.python.org/issue35823 for discussion and references. * * In some C libraries, setrlimit() has the same thread list/signalling * behavior since resource limits were per-thread attributes before @@ -774,6 +774,7 @@ do_fork_exec(char *const exec_array[], pid_t pid; #ifdef VFORK_USABLE + PyThreadState *vfork_tstate_save; if (child_sigmask) { /* These are checked by our caller; verify them in debug builds. */ assert(!call_setuid); @@ -781,8 +782,23 @@ do_fork_exec(char *const exec_array[], assert(!call_setgroups); assert(preexec_fn == Py_None); + /* Drop the GIL so that other threads can continue execution while this + * thread in the parent remains blocked per vfork-semantics on the + * child's exec syscall outcome. Exec does filesystem access which + * can take an arbitrarily long time. This addresses GH-104372. + * + * The vfork'ed child still runs in our address space. Per POSIX it + * must be limited to nothing but exec, but the Linux implementation + * is a little more usable. See the child_exec() comment - The child + * MUST NOT re-acquire the GIL. + */ + vfork_tstate_save = PyEval_SaveThread(); pid = vfork(); - if (pid == -1) { + if (pid != 0) { + // Not in the child process, reacquire the GIL. + PyEval_RestoreThread(vfork_tstate_save); + } + if (pid == (pid_t)-1) { /* If vfork() fails, fall back to using fork(). When it isn't * allowed in a process by the kernel, vfork can return -1 * with errno EINVAL. https://bugs.python.org/issue47151. */ @@ -795,6 +811,7 @@ do_fork_exec(char *const exec_array[], } if (pid != 0) { + // Parent process. return pid; } From 3b143468995ca9e6e1ecfa69b5dc8fd587fcc33a Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 1 Sep 2023 03:57:30 -0700 Subject: [PATCH 238/632] [3.11] gh-101100: Fix sphinx warnings in `tutorial/classes.rst` (GH-108746) (#108755) Co-authored-by: Nikita Sobolev --- Doc/tools/.nitignore | 1 - Doc/tutorial/classes.rst | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 871a48282523c4..ab7f4a8dc618b9 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -158,7 +158,6 @@ Doc/reference/expressions.rst Doc/reference/import.rst Doc/reference/simple_stmts.rst Doc/tutorial/appendix.rst -Doc/tutorial/classes.rst Doc/tutorial/controlflow.rst Doc/tutorial/datastructures.rst Doc/tutorial/introduction.rst diff --git a/Doc/tutorial/classes.rst b/Doc/tutorial/classes.rst index e4002cfa351936..46dfb378590ffb 100644 --- a/Doc/tutorial/classes.rst +++ b/Doc/tutorial/classes.rst @@ -276,7 +276,7 @@ definition looked like this:: then ``MyClass.i`` and ``MyClass.f`` are valid attribute references, returning an integer and a function object, respectively. Class attributes can also be assigned to, so you can change the value of ``MyClass.i`` by assignment. -:attr:`__doc__` is also a valid attribute, returning the docstring belonging to +:attr:`!__doc__` is also a valid attribute, returning the docstring belonging to the class: ``"A simple example class"``. Class *instantiation* uses function notation. Just pretend that the class From d3476dcb8488b4d3d0d4343c03bbadd9a329392d Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 1 Sep 2023 05:09:43 -0700 Subject: [PATCH 239/632] [3.11] gh-101100: Fix sphinx warnings in `tutorial/appendix.rst` (GH-108750) (#108757) Co-authored-by: Nikita Sobolev Co-authored-by: Hugo van Kemenade --- Doc/tools/.nitignore | 1 - Doc/tutorial/appendix.rst | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index ab7f4a8dc618b9..6dae72a9dfeff1 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -157,7 +157,6 @@ Doc/reference/datamodel.rst Doc/reference/expressions.rst Doc/reference/import.rst Doc/reference/simple_stmts.rst -Doc/tutorial/appendix.rst Doc/tutorial/controlflow.rst Doc/tutorial/datastructures.rst Doc/tutorial/introduction.rst diff --git a/Doc/tutorial/appendix.rst b/Doc/tutorial/appendix.rst index 241a812037469e..588591fcdb726f 100644 --- a/Doc/tutorial/appendix.rst +++ b/Doc/tutorial/appendix.rst @@ -101,8 +101,8 @@ in the script:: The Customization Modules ------------------------- -Python provides two hooks to let you customize it: :mod:`sitecustomize` and -:mod:`usercustomize`. To see how it works, you need first to find the location +Python provides two hooks to let you customize it: :index:`sitecustomize` and +:index:`usercustomize`. To see how it works, you need first to find the location of your user site-packages directory. Start Python and run this code:: >>> import site @@ -113,9 +113,9 @@ Now you can create a file named :file:`usercustomize.py` in that directory and put anything you want in it. It will affect every invocation of Python, unless it is started with the :option:`-s` option to disable the automatic import. -:mod:`sitecustomize` works in the same way, but is typically created by an +:index:`sitecustomize` works in the same way, but is typically created by an administrator of the computer in the global site-packages directory, and is -imported before :mod:`usercustomize`. See the documentation of the :mod:`site` +imported before :index:`usercustomize`. See the documentation of the :mod:`site` module for more details. From 0701f8cabd8c0a8197711c348f01ea4bbd9727da Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 1 Sep 2023 13:46:21 -0700 Subject: [PATCH 240/632] [3.11] gh-105563: reference DateType in datetime's documentation (GH-105946) (#108790) gh-105563: reference DateType in datetime's documentation (GH-105946) (cherry picked from commit 8f9ea43ee805f98391f857397daac9df7ffa71cd) Co-authored-by: TATHAGATA ROY --- Doc/library/datetime.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index d7b0401b7feef8..1c2abc8cf4e30e 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -37,6 +37,10 @@ on efficient attribute extraction for output formatting and manipulation. Package `dateutil `_ Third-party library with expanded time zone and parsing support. + Package `DateType `_ + Third-party library that introduces distinct static types to e.g. allow static type checkers + to differentiate between naive and aware datetimes. + .. _datetime-naive-aware: Aware and Naive Objects From 6538bcfa0b65aba480b7611e7096e45e6924c978 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 2 Sep 2023 07:30:32 +0300 Subject: [PATCH 241/632] [3.11] Improve some C API documentation (GH-108768) (GH-108786) * Express functions which take argument as a C string in terms of functions which take Python object. * Use "note" directive for PyMapping_HasKey() and PyMapping_HasKeyString() notes. (cherry picked from commit 6f97eeec222f81bd7ae836c149872a40b079e2a6) --- Doc/c-api/dict.rst | 16 ++++++---------- Doc/c-api/mapping.rst | 43 +++++++++++++++++++++++-------------------- Doc/c-api/object.rst | 28 ++++++++++++++-------------- 3 files changed, 43 insertions(+), 44 deletions(-) diff --git a/Doc/c-api/dict.rst b/Doc/c-api/dict.rst index c9233798c138f0..1575aa50eff5d6 100644 --- a/Doc/c-api/dict.rst +++ b/Doc/c-api/dict.rst @@ -70,12 +70,9 @@ Dictionary Objects .. c:function:: int PyDict_SetItemString(PyObject *p, const char *key, PyObject *val) - .. index:: single: PyUnicode_FromString() - - Insert *val* into the dictionary *p* using *key* as a key. *key* should - be a :c:expr:`const char*` UTF-8 encoded bytes string. The key object is created using - ``PyUnicode_FromString(key)``. Return ``0`` on success or ``-1`` on - failure. This function *does not* steal a reference to *val*. + This is the same as :c:func:`PyDict_SetItem`, but *key* is + specified as a :c:expr:`const char*` UTF-8 encoded bytes string, + rather than a :c:expr:`PyObject*`. .. c:function:: int PyDict_DelItem(PyObject *p, PyObject *key) @@ -88,10 +85,9 @@ Dictionary Objects .. c:function:: int PyDict_DelItemString(PyObject *p, const char *key) - Remove the entry in dictionary *p* which has a key specified by the UTF-8 - encoded bytes string *key*. - If *key* is not in the dictionary, :exc:`KeyError` is raised. - Return ``0`` on success or ``-1`` on failure. + This is the same as :c:func:`PyDict_DelItem`, but *key* is + specified as a :c:expr:`const char*` UTF-8 encoded bytes string, + rather than a :c:expr:`PyObject*`. .. c:function:: PyObject* PyDict_GetItem(PyObject *p, PyObject *key) diff --git a/Doc/c-api/mapping.rst b/Doc/c-api/mapping.rst index d94a9dc45b5ebe..0c42b9177bb56d 100644 --- a/Doc/c-api/mapping.rst +++ b/Doc/c-api/mapping.rst @@ -28,30 +28,28 @@ See also :c:func:`PyObject_GetItem`, :c:func:`PyObject_SetItem` and .. c:function:: PyObject* PyMapping_GetItemString(PyObject *o, const char *key) - Return element of *o* corresponding to the string *key* or ``NULL`` on failure. - This is the equivalent of the Python expression ``o[key]``. - See also :c:func:`PyObject_GetItem`. + This is the same as :c:func:`PyObject_GetItem`, but *key* is + specified as a :c:expr:`const char*` UTF-8 encoded bytes string, + rather than a :c:expr:`PyObject*`. .. c:function:: int PyMapping_SetItemString(PyObject *o, const char *key, PyObject *v) - Map the string *key* to the value *v* in object *o*. Returns ``-1`` on - failure. This is the equivalent of the Python statement ``o[key] = v``. - See also :c:func:`PyObject_SetItem`. This function *does not* steal a - reference to *v*. + This is the same as :c:func:`PyObject_SetItem`, but *key* is + specified as a :c:expr:`const char*` UTF-8 encoded bytes string, + rather than a :c:expr:`PyObject*`. .. c:function:: int PyMapping_DelItem(PyObject *o, PyObject *key) - Remove the mapping for the object *key* from the object *o*. Return ``-1`` - on failure. This is equivalent to the Python statement ``del o[key]``. This is an alias of :c:func:`PyObject_DelItem`. .. c:function:: int PyMapping_DelItemString(PyObject *o, const char *key) - Remove the mapping for the string *key* from the object *o*. Return ``-1`` - on failure. This is equivalent to the Python statement ``del o[key]``. + This is the same as :c:func:`PyObject_DelItem`, but *key* is + specified as a :c:expr:`const char*` UTF-8 encoded bytes string, + rather than a :c:expr:`PyObject*`. .. c:function:: int PyMapping_HasKey(PyObject *o, PyObject *key) @@ -60,20 +58,25 @@ See also :c:func:`PyObject_GetItem`, :c:func:`PyObject_SetItem` and This is equivalent to the Python expression ``key in o``. This function always succeeds. - Note that exceptions which occur while calling the :meth:`~object.__getitem__` - method will get suppressed. - To get error reporting use :c:func:`PyObject_GetItem()` instead. + .. note:: + + Exceptions which occur when this calls :meth:`~object.__getitem__` + method are silently ignored. + For proper error handling, use :c:func:`PyObject_GetItem()` instead. .. c:function:: int PyMapping_HasKeyString(PyObject *o, const char *key) - Return ``1`` if the mapping object has the key *key* and ``0`` otherwise. - This is equivalent to the Python expression ``key in o``. - This function always succeeds. + This is the same as :c:func:`PyMapping_HasKey`, but *key* is + specified as a :c:expr:`const char*` UTF-8 encoded bytes string, + rather than a :c:expr:`PyObject*`. + + .. note:: - Note that exceptions which occur while calling the :meth:`~object.__getitem__` - method and creating a temporary string object will get suppressed. - To get error reporting use :c:func:`PyMapping_GetItemString()` instead. + Exceptions that occur when this calls :meth:`~object.__getitem__` + method or while creating the temporary :class:`str` + object are silently ignored. + For proper error handling, use :c:func:`PyMapping_GetItemString` instead. .. c:function:: PyObject* PyMapping_Keys(PyObject *o) diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index bc67fb60dc8bec..54e9733b40c7b6 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -42,15 +42,15 @@ Object Protocol .. c:function:: int PyObject_HasAttrString(PyObject *o, const char *attr_name) - Returns ``1`` if *o* has the attribute *attr_name*, and ``0`` otherwise. This - is equivalent to the Python expression ``hasattr(o, attr_name)``. This function - always succeeds. + This is the same as :c:func:`PyObject_HasAttr`, but *attr_name* is + specified as a :c:expr:`const char*` UTF-8 encoded bytes string, + rather than a :c:expr:`PyObject*`. .. note:: Exceptions that occur when this calls :meth:`~object.__getattr__` and - :meth:`~object.__getattribute__` methods or while creating the temporary :class:`str` - object are silently ignored. + :meth:`~object.__getattribute__` methods or while creating the temporary + :class:`str` object are silently ignored. For proper error handling, use :c:func:`PyObject_GetAttrString` instead. @@ -63,9 +63,9 @@ Object Protocol .. c:function:: PyObject* PyObject_GetAttrString(PyObject *o, const char *attr_name) - Retrieve an attribute named *attr_name* from object *o*. Returns the attribute - value on success, or ``NULL`` on failure. This is the equivalent of the Python - expression ``o.attr_name``. + This is the same as :c:func:`PyObject_GetAttr`, but *attr_name* is + specified as a :c:expr:`const char*` UTF-8 encoded bytes string, + rather than a :c:expr:`PyObject*`. .. c:function:: PyObject* PyObject_GenericGetAttr(PyObject *o, PyObject *name) @@ -92,10 +92,9 @@ Object Protocol .. c:function:: int PyObject_SetAttrString(PyObject *o, const char *attr_name, PyObject *v) - Set the value of the attribute named *attr_name*, for object *o*, to the value - *v*. Raise an exception and return ``-1`` on failure; - return ``0`` on success. This is the equivalent of the Python statement - ``o.attr_name = v``. + This is the same as :c:func:`PyObject_SetAttr`, but *attr_name* is + specified as a :c:expr:`const char*` UTF-8 encoded bytes string, + rather than a :c:expr:`PyObject*`. If *v* is ``NULL``, the attribute is deleted, but this feature is deprecated in favour of using :c:func:`PyObject_DelAttrString`. @@ -121,8 +120,9 @@ Object Protocol .. c:function:: int PyObject_DelAttrString(PyObject *o, const char *attr_name) - Delete attribute named *attr_name*, for object *o*. Returns ``-1`` on failure. - This is the equivalent of the Python statement ``del o.attr_name``. + This is the same as :c:func:`PyObject_DelAttr`, but *attr_name* is + specified as a :c:expr:`const char*` UTF-8 encoded bytes string, + rather than a :c:expr:`PyObject*`. .. c:function:: PyObject* PyObject_GenericGetDict(PyObject *o, void *context) From fc114a72bcb460e0e702335db06907da735a44c1 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 1 Sep 2023 22:53:48 -0700 Subject: [PATCH 242/632] [3.11] gh-103186: assert in tests that UnsafeMailcapInput warnings are provided (GH-103217) (GH-108800) (cherry picked from commit 1724553e6e8baae655901488968a40df981f32da) Co-authored-by: Ijtaba Hussain --- Lib/test/test_mailcap.py | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_mailcap.py b/Lib/test/test_mailcap.py index 8185f4a7806615..e80e34063b2f98 100644 --- a/Lib/test/test_mailcap.py +++ b/Lib/test/test_mailcap.py @@ -128,7 +128,6 @@ def test_subst(self): (["", "audio/*", "foo.txt"], ""), (["echo foo", "audio/*", "foo.txt"], "echo foo"), (["echo %s", "audio/*", "foo.txt"], "echo foo.txt"), - (["echo %t", "audio/*", "foo.txt"], None), (["echo %t", "audio/wav", "foo.txt"], "echo audio/wav"), (["echo \\%t", "audio/*", "foo.txt"], "echo %t"), (["echo foo", "audio/*", "foo.txt", plist], "echo foo"), @@ -211,9 +210,6 @@ def test_findmatch(self): ([c, "audio/basic"], {"key": "description", "filename": fname}, ('"An audio fragment"', audio_basic_entry)), - ([c, "audio/*"], - {"filename": fname}, - (None, None)), ([c, "audio/wav"], {"filename": fname}, ("/usr/local/bin/showaudio audio/wav", audio_entry)), @@ -246,6 +242,30 @@ def test_test(self): ] self._run_cases(cases) + def test_unsafe_mailcap_input(self): + with self.assertWarnsRegex(mailcap.UnsafeMailcapInput, + 'Refusing to substitute parameter.*' + 'into a shell command'): + unsafe_param = mailcap.subst("echo %{total}", + "audio/wav", + "foo.txt", + ["total=*"]) + self.assertEqual(unsafe_param, None) + + with self.assertWarnsRegex(mailcap.UnsafeMailcapInput, + 'Refusing to substitute MIME type' + '.*into a shell'): + unsafe_mimetype = mailcap.subst("echo %t", "audio/*", "foo.txt") + self.assertEqual(unsafe_mimetype, None) + + with self.assertWarnsRegex(mailcap.UnsafeMailcapInput, + 'Refusing to use mailcap with filename.*' + 'Use a safe temporary filename.'): + unsafe_filename = mailcap.findmatch(MAILCAPDICT, + "audio/wav", + filename="foo*.txt") + self.assertEqual(unsafe_filename, (None, None)) + def _run_cases(self, cases): for c in cases: self.assertEqual(mailcap.findmatch(*c[0], **c[1]), c[2]) From fa8b9c147de87f12483cabd6220c4d40d96bf612 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 2 Sep 2023 06:05:07 -0700 Subject: [PATCH 243/632] [3.11] gh-101100: Fix sphinx warnings in `uuid.rst` (GH-108805) (#108808) (cherry picked from commit 21da4980f5916e8fd648f04367a9e60d141af366) Co-authored-by: Nikita Sobolev Co-authored-by: Hugo van Kemenade Co-authored-by: Alex Waygood --- Doc/library/uuid.rst | 51 ++++++++++++++++++++++++++------------------ Doc/tools/.nitignore | 1 - 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/Doc/library/uuid.rst b/Doc/library/uuid.rst index a71fe7abf5b547..c1e2288071320e 100644 --- a/Doc/library/uuid.rst +++ b/Doc/library/uuid.rst @@ -22,7 +22,7 @@ random UUID. Depending on support from the underlying platform, :func:`uuid1` may or may not return a "safe" UUID. A safe UUID is one which is generated using synchronization methods that ensure no two processes can obtain the same -UUID. All instances of :class:`UUID` have an :attr:`is_safe` attribute +UUID. All instances of :class:`UUID` have an :attr:`~UUID.is_safe` attribute which relays any information about the UUID's safety, using this enumeration: .. class:: SafeUUID @@ -95,25 +95,34 @@ which relays any information about the UUID's safety, using this enumeration: A tuple of the six integer fields of the UUID, which are also available as six individual attributes and two derived attributes: - +------------------------------+-------------------------------+ - | Field | Meaning | - +==============================+===============================+ - | :attr:`time_low` | the first 32 bits of the UUID | - +------------------------------+-------------------------------+ - | :attr:`time_mid` | the next 16 bits of the UUID | - +------------------------------+-------------------------------+ - | :attr:`time_hi_version` | the next 16 bits of the UUID | - +------------------------------+-------------------------------+ - | :attr:`clock_seq_hi_variant` | the next 8 bits of the UUID | - +------------------------------+-------------------------------+ - | :attr:`clock_seq_low` | the next 8 bits of the UUID | - +------------------------------+-------------------------------+ - | :attr:`node` | the last 48 bits of the UUID | - +------------------------------+-------------------------------+ - | :attr:`time` | the 60-bit timestamp | - +------------------------------+-------------------------------+ - | :attr:`clock_seq` | the 14-bit sequence number | - +------------------------------+-------------------------------+ +.. list-table:: + + * - Field + - Meaning + + * - .. attribute:: UUID.time_low + - The first 32 bits of the UUID. + + * - .. attribute:: UUID.time_mid + - The next 16 bits of the UUID. + + * - .. attribute:: UUID.time_hi_version + - The next 16 bits of the UUID. + + * - .. attribute:: UUID.clock_seq_hi_variant + - The next 8 bits of the UUID. + + * - .. attribute:: UUID.clock_seq_low + - The next 8 bits of the UUID. + + * - .. attribute:: UUID.node + - The last 48 bits of the UUID. + + * - .. attribute:: UUID.time + - The 60-bit timestamp. + + * - .. attribute:: UUID.clock_seq + - The 14-bit sequence number. .. attribute:: UUID.hex @@ -231,7 +240,7 @@ The :mod:`uuid` module defines the following namespace identifiers for use with text output format. The :mod:`uuid` module defines the following constants for the possible values -of the :attr:`variant` attribute: +of the :attr:`~UUID.variant` attribute: .. data:: RESERVED_NCS diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 6dae72a9dfeff1..435482185c2989 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -138,7 +138,6 @@ Doc/library/unittest.mock.rst Doc/library/unittest.rst Doc/library/urllib.parse.rst Doc/library/urllib.request.rst -Doc/library/uuid.rst Doc/library/weakref.rst Doc/library/wsgiref.rst Doc/library/xml.dom.minidom.rst From 662d236be23d9aec004f78e6055df412c8176894 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Sat, 2 Sep 2023 16:38:59 +0300 Subject: [PATCH 244/632] [3.11] gh-101100: Fix sphinx warnings in `unittest.mock-examples.rst` (GH-108810) (#108812) (cherry picked from commit 5141b1ebe07ad54279e0770b4704eaf76f24951d) --- Doc/library/unittest.mock-examples.rst | 13 +++++++------ Doc/tools/.nitignore | 1 - 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Doc/library/unittest.mock-examples.rst b/Doc/library/unittest.mock-examples.rst index 895b9f9f07671b..744fc9de63cd16 100644 --- a/Doc/library/unittest.mock-examples.rst +++ b/Doc/library/unittest.mock-examples.rst @@ -579,14 +579,14 @@ Partial mocking In some tests I wanted to mock out a call to :meth:`datetime.date.today` to return a known date, but I didn't want to prevent the code under test from creating new date objects. Unfortunately :class:`datetime.date` is written in C, and -so I couldn't just monkey-patch out the static :meth:`date.today` method. +so I couldn't just monkey-patch out the static :meth:`datetime.date.today` method. I found a simple way of doing this that involved effectively wrapping the date class with a mock, but passing through calls to the constructor to the real class (and returning real instances). The :func:`patch decorator ` is used here to -mock out the ``date`` class in the module under test. The :attr:`side_effect` +mock out the ``date`` class in the module under test. The :attr:`~Mock.side_effect` attribute on the mock date class is then set to a lambda function that returns a real date. When the mock date class is called a real date will be constructed and returned by ``side_effect``. :: @@ -766,8 +766,8 @@ mock has a nice API for making assertions about how your mock objects are used. >>> mock.foo_bar.assert_called_with('baz', spam='eggs') If your mock is only being called once you can use the -:meth:`assert_called_once_with` method that also asserts that the -:attr:`call_count` is one. +:meth:`~Mock.assert_called_once_with` method that also asserts that the +:attr:`~Mock.call_count` is one. >>> mock.foo_bar.assert_called_once_with('baz', spam='eggs') >>> mock.foo_bar() @@ -835,7 +835,7 @@ One possibility would be for mock to copy the arguments you pass in. This could then cause problems if you do assertions that rely on object identity for equality. -Here's one solution that uses the :attr:`side_effect` +Here's one solution that uses the :attr:`~Mock.side_effect` functionality. If you provide a ``side_effect`` function for a mock then ``side_effect`` will be called with the same args as the mock. This gives us an opportunity to copy the arguments and store them for later assertions. In this @@ -971,7 +971,8 @@ We can do this with :class:`MagicMock`, which will behave like a dictionary, and using :data:`~Mock.side_effect` to delegate dictionary access to a real underlying dictionary that is under our control. -When the :meth:`__getitem__` and :meth:`__setitem__` methods of our ``MagicMock`` are called +When the :meth:`~object.__getitem__` and :meth:`~object.__setitem__` methods +of our ``MagicMock`` are called (normal dictionary access) then ``side_effect`` is called with the key (and in the case of ``__setitem__`` the value too). We can also control what is returned. diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 435482185c2989..f4bb6315449402 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -133,7 +133,6 @@ Doc/library/tkinter.ttk.rst Doc/library/traceback.rst Doc/library/tty.rst Doc/library/turtle.rst -Doc/library/unittest.mock-examples.rst Doc/library/unittest.mock.rst Doc/library/unittest.rst Doc/library/urllib.parse.rst From 76f52196b90410286234f905cbac32c3c590de9e Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 3 Sep 2023 09:34:30 +0300 Subject: [PATCH 245/632] [3.11] gh-108416: Mark slow test methods with @requires_resource('cpu') (GH-108421) (GH-108799) Only mark tests which spend significant system or user time, by itself or in subprocesses. (cherry picked from commit f3ba0a74cd50274acdcd592d4ce8395b92492b7c) --- Lib/test/_test_multiprocessing.py | 2 ++ Lib/test/pickletester.py | 1 + Lib/test/test_ast.py | 1 + Lib/test/test_buffer.py | 2 ++ Lib/test/test_builtin.py | 1 + Lib/test/test_compile.py | 1 + Lib/test/test_compileall.py | 2 ++ Lib/test/test_concurrent_futures.py | 1 + Lib/test/test_context.py | 2 ++ Lib/test/test_cppext/__init__.py | 2 ++ Lib/test/test_descr.py | 1 + Lib/test/test_email/test_email.py | 2 ++ Lib/test/test_exceptions.py | 1 + Lib/test/test_gdb.py | 7 +++++++ Lib/test/test_itertools.py | 1 + Lib/test/test_largefile.py | 4 +++- Lib/test/test_multibytecodec.py | 1 + Lib/test/test_peepholer.py | 2 ++ Lib/test/test_runpy.py | 3 ++- Lib/test/test_selectors.py | 1 + Lib/test/test_source_encoding.py | 3 ++- Lib/test/test_statistics.py | 2 ++ Lib/test/test_subprocess.py | 1 + Lib/test/test_support.py | 1 + Lib/test/test_sys_settrace.py | 1 + Lib/test/test_tools/test_freeze.py | 1 + Lib/test/test_trace.py | 3 ++- Lib/test/test_unicodedata.py | 2 ++ Lib/test/test_venv.py | 4 +++- Lib/test/test_weakref.py | 2 ++ 30 files changed, 53 insertions(+), 5 deletions(-) diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 3476371f07f7ed..45ca80b08bf3ca 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -328,6 +328,7 @@ def test_set_executable(self): p.join() self.assertEqual(p.exitcode, 0) + @support.requires_resource('cpu') def test_args_argument(self): # bpo-45735: Using list or tuple as *args* in constructor could # achieve the same effect. @@ -4470,6 +4471,7 @@ def test_finalize(self): result = [obj for obj in iter(conn.recv, 'STOP')] self.assertEqual(result, ['a', 'b', 'd10', 'd03', 'd02', 'd01', 'e']) + @support.requires_resource('cpu') def test_thread_safety(self): # bpo-24484: _run_finalizers() should be thread-safe def cb(): diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py index 6e87370c2065ba..a687fe0629080a 100644 --- a/Lib/test/pickletester.py +++ b/Lib/test/pickletester.py @@ -2576,6 +2576,7 @@ def check_frame_opcodes(self, pickled): self.assertLess(pos - frameless_start, self.FRAME_SIZE_MIN) @support.skip_if_pgo_task + @support.requires_resource('cpu') def test_framing_many_objects(self): obj = list(range(10**5)) for proto in range(4, pickle.HIGHEST_PROTOCOL + 1): diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index 33c13ee29a2689..c8d2f916dbd5f0 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -1701,6 +1701,7 @@ def test_tuple(self): def test_nameconstant(self): self.expr(ast.NameConstant(4)) + @support.requires_resource('cpu') def test_stdlib_validates(self): stdlib = os.path.dirname(ast.__file__) tests = [fn for fn in os.listdir(stdlib) if fn.endswith(".py")] diff --git a/Lib/test/test_buffer.py b/Lib/test/test_buffer.py index 468c6ea9def923..e9149d1ac497c9 100644 --- a/Lib/test/test_buffer.py +++ b/Lib/test/test_buffer.py @@ -1019,6 +1019,7 @@ def match(req, flag): ndim=ndim, shape=shape, strides=strides, lst=lst, sliced=sliced) + @support.requires_resource('cpu') def test_ndarray_getbuf(self): requests = ( # distinct flags @@ -2750,6 +2751,7 @@ def iter_roundtrip(ex, m, items, fmt): m = memoryview(ex) iter_roundtrip(ex, m, items, fmt) + @support.requires_resource('cpu') def test_memoryview_cast_1D_ND(self): # Cast between C-contiguous buffers. At least one buffer must # be 1D, at least one format must be 'c', 'b' or 'B'. diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 9078c409cc44b2..2f56121616629e 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -918,6 +918,7 @@ def test_filter_pickle(self): f2 = filter(filter_char, "abcdeabcde") self.check_iter_pickle(f1, list(f2), proto) + @support.requires_resource('cpu') def test_filter_dealloc(self): # Tests recursive deallocation of nested filter objects using the # thrashcan mechanism. See gh-102356 for more details. diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index c756d43a3cf3b8..b286ab7cfe7e6b 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -741,6 +741,7 @@ def test_path_like_objects(self): # An implicit test for PyUnicode_FSDecoder(). compile("42", FakePath("test_compile_pathlike"), "single") + @support.requires_resource('cpu') def test_stack_overflow(self): # bpo-31113: Stack overflow when compile a long sequence of # complex statements. diff --git a/Lib/test/test_compileall.py b/Lib/test/test_compileall.py index 05154c8f1c6057..df7c5122b3b1f5 100644 --- a/Lib/test/test_compileall.py +++ b/Lib/test/test_compileall.py @@ -551,6 +551,7 @@ def test_no_args_compiles_path(self): self.assertNotCompiled(self.barfn) @without_source_date_epoch # timestamp invalidation test + @support.requires_resource('cpu') def test_no_args_respects_force_flag(self): bazfn = script_helper.make_script(self.directory, 'baz', '') with self.temporary_pycache_prefix() as env: @@ -568,6 +569,7 @@ def test_no_args_respects_force_flag(self): mtime2 = os.stat(pycpath).st_mtime self.assertNotEqual(mtime, mtime2) + @support.requires_resource('cpu') def test_no_args_respects_quiet_flag(self): script_helper.make_script(self.directory, 'baz', '') with self.temporary_pycache_prefix() as env: diff --git a/Lib/test/test_concurrent_futures.py b/Lib/test/test_concurrent_futures.py index 4558560e1a992f..a2a4d873b1389c 100644 --- a/Lib/test/test_concurrent_futures.py +++ b/Lib/test/test_concurrent_futures.py @@ -946,6 +946,7 @@ def test_idle_thread_reuse(self): executor.shutdown(wait=True) @unittest.skipUnless(hasattr(os, 'register_at_fork'), 'need os.register_at_fork') + @support.requires_resource('cpu') def test_hang_global_shutdown_lock(self): # bpo-45021: _global_shutdown_lock should be reinitialized in the child # process, otherwise it will never exit diff --git a/Lib/test/test_context.py b/Lib/test/test_context.py index b1aece4f5c9c49..dc6856509a40a0 100644 --- a/Lib/test/test_context.py +++ b/Lib/test/test_context.py @@ -6,6 +6,7 @@ import time import unittest import weakref +from test import support from test.support import threading_helper try: @@ -570,6 +571,7 @@ def test_hamt_collision_3(self): self.assertEqual({k.name for k in h.keys()}, {'C', 'D', 'E'}) + @support.requires_resource('cpu') def test_hamt_stress(self): COLLECTION_SIZE = 7000 TEST_ITERS_EVERY = 647 diff --git a/Lib/test/test_cppext/__init__.py b/Lib/test/test_cppext/__init__.py index 4ce29b7ff2c23e..37247e4ac01e71 100644 --- a/Lib/test/test_cppext/__init__.py +++ b/Lib/test/test_cppext/__init__.py @@ -15,9 +15,11 @@ @support.requires_subprocess() class TestCPPExt(unittest.TestCase): + @support.requires_resource('cpu') def test_build_cpp11(self): self.check_build(False, '_testcpp11ext') + @support.requires_resource('cpu') def test_build_cpp03(self): self.check_build(True, '_testcpp03ext') diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index f07d7d4596599c..2a12695031d34a 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -4453,6 +4453,7 @@ class Oops(object): o.whatever = Provoker(o) del o + @support.requires_resource('cpu') def test_wrapper_segfault(self): # SF 927248: deeply nested wrappers could cause stack overflow f = lambda:None diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py index 2bb651609f572b..677f2094b835f3 100644 --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -39,6 +39,7 @@ from email import quoprimime from email import utils +from test import support from test.support import threading_helper from test.support.os_helper import unlink from test.test_email import openfile, TestEmailBase @@ -3342,6 +3343,7 @@ def test_getaddresses_header_obj(self): self.assertEqual(addrs[0][1], 'aperson@dom.ain') @threading_helper.requires_working_threading() + @support.requires_resource('cpu') def test_make_msgid_collisions(self): # Test make_msgid uniqueness, even with multiple threads class MsgidsThread(Thread): diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index d7133adf058920..6f34e29e42f7c7 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -1304,6 +1304,7 @@ def g(): @cpython_only + @support.requires_resource('cpu') def test_trashcan_recursion(self): # See bpo-33930 diff --git a/Lib/test/test_gdb.py b/Lib/test/test_gdb.py index 0f39b8f45714ad..bba9b07c6708b5 100644 --- a/Lib/test/test_gdb.py +++ b/Lib/test/test_gdb.py @@ -317,6 +317,7 @@ def assertGdbRepr(self, val, exp_repr=None): ('%r did not equal expected %r; full output was:\n%s' % (gdb_repr, exp_repr, gdb_output))) + @support.requires_resource('cpu') def test_int(self): 'Verify the pretty-printing of various int values' self.assertGdbRepr(42) @@ -343,6 +344,7 @@ def test_lists(self): self.assertGdbRepr([]) self.assertGdbRepr(list(range(5))) + @support.requires_resource('cpu') def test_bytes(self): 'Verify the pretty-printing of bytes' self.assertGdbRepr(b'') @@ -357,6 +359,7 @@ def test_bytes(self): self.assertGdbRepr(bytes([b for b in range(255)])) + @support.requires_resource('cpu') def test_strings(self): 'Verify the pretty-printing of unicode strings' # We cannot simply call locale.getpreferredencoding() here, @@ -407,6 +410,7 @@ def test_tuples(self): self.assertGdbRepr((1,), '(1,)') self.assertGdbRepr(('foo', 'bar', 'baz')) + @support.requires_resource('cpu') def test_sets(self): 'Verify the pretty-printing of sets' if (gdb_major_version, gdb_minor_version) < (7, 3): @@ -425,6 +429,7 @@ def test_sets(self): id(s)''') self.assertEqual(gdb_repr, "{'b'}") + @support.requires_resource('cpu') def test_frozensets(self): 'Verify the pretty-printing of frozensets' if (gdb_major_version, gdb_minor_version) < (7, 3): @@ -828,6 +833,7 @@ def test_bt_full(self): @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") + @support.requires_resource('cpu') def test_threads(self): 'Verify that "py-bt" indicates threads that are waiting for the GIL' cmd = ''' @@ -889,6 +895,7 @@ def test_gc(self): @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") + @support.requires_resource('cpu') # Some older versions of gdb will fail with # "Cannot find new threads: generic error" # unless we add LD_PRELOAD=PATH-TO-libpthread.so.1 as a workaround diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py index 311c2a3288dc8c..0d0030e4160995 100644 --- a/Lib/test/test_itertools.py +++ b/Lib/test/test_itertools.py @@ -2229,6 +2229,7 @@ def gen2(x): self.assertEqual(hist, [0,1]) @support.skip_if_pgo_task + @support.requires_resource('cpu') def test_long_chain_of_empty_iterables(self): # Make sure itertools.chain doesn't run into recursion limits when # dealing with long chains of empty iterables. Even with a high diff --git a/Lib/test/test_largefile.py b/Lib/test/test_largefile.py index 3c11c59baef6e5..3b0930fe69e30e 100644 --- a/Lib/test/test_largefile.py +++ b/Lib/test/test_largefile.py @@ -8,7 +8,7 @@ import socket import shutil import threading -from test.support import requires, bigmemtest +from test.support import requires, bigmemtest, requires_resource from test.support import SHORT_TIMEOUT from test.support import socket_helper from test.support.os_helper import TESTFN, unlink @@ -173,6 +173,7 @@ class TestCopyfile(LargeFileTest, unittest.TestCase): # Exact required disk space would be (size * 2), but let's give it a # bit more tolerance. @skip_no_disk_space(TESTFN, size * 2.5) + @requires_resource('cpu') def test_it(self): # Internally shutil.copyfile() can use "fast copy" methods like # os.sendfile(). @@ -222,6 +223,7 @@ def run(sock): # Exact required disk space would be (size * 2), but let's give it a # bit more tolerance. @skip_no_disk_space(TESTFN, size * 2.5) + @requires_resource('cpu') def test_it(self): port = socket_helper.find_unused_port() with socket.create_server(("", port)) as sock: diff --git a/Lib/test/test_multibytecodec.py b/Lib/test/test_multibytecodec.py index cf8bb5e3a0520d..6451df14696933 100644 --- a/Lib/test/test_multibytecodec.py +++ b/Lib/test/test_multibytecodec.py @@ -363,6 +363,7 @@ def test_iso2022_jp_g0(self): e = '\u3406'.encode(encoding) self.assertFalse(any(x > 0x80 for x in e)) + @support.requires_resource('cpu') def test_bug1572832(self): for x in range(0x10000, 0x110000): # Any ISO 2022 codec will cause the segfault diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 241644ad7d2ff0..d06a70bd296deb 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -3,6 +3,7 @@ import textwrap import unittest +from test import support from test.support.bytecode_helper import BytecodeTestCase @@ -523,6 +524,7 @@ def genexpr(): return (y for x in a for y in [f(x)]) self.assertEqual(count_instr_recursively(genexpr, 'FOR_ITER'), 1) + @support.requires_resource('cpu') def test_format_combinations(self): flags = '-+ #0' testcases = [ diff --git a/Lib/test/test_runpy.py b/Lib/test/test_runpy.py index 6aaa288c14e1d7..628c8cae38a751 100644 --- a/Lib/test/test_runpy.py +++ b/Lib/test/test_runpy.py @@ -12,7 +12,7 @@ import textwrap import unittest import warnings -from test.support import no_tracing, verbose, requires_subprocess +from test.support import no_tracing, verbose, requires_subprocess, requires_resource from test.support.import_helper import forget, make_legacy_pyc, unload from test.support.os_helper import create_empty_file, temp_dir from test.support.script_helper import make_script, make_zip_script @@ -733,6 +733,7 @@ def test_zipfile_error(self): self._check_import_error(zip_name, msg) @no_tracing + @requires_resource('cpu') def test_main_recursion_error(self): with temp_dir() as script_dir, temp_dir() as dummy_dir: mod_name = '__main__' diff --git a/Lib/test/test_selectors.py b/Lib/test/test_selectors.py index c2db88c203920a..12ecc50d550c4f 100644 --- a/Lib/test/test_selectors.py +++ b/Lib/test/test_selectors.py @@ -449,6 +449,7 @@ class ScalableSelectorMixIn: # see issue #18963 for why it's skipped on older OS X versions @support.requires_mac_ver(10, 5) @unittest.skipUnless(resource, "Test needs resource module") + @support.requires_resource('cpu') def test_above_fd_setsize(self): # A scalable implementation should have no problem with more than # FD_SETSIZE file descriptors. Since we don't know the value, we just diff --git a/Lib/test/test_source_encoding.py b/Lib/test/test_source_encoding.py index 5fe0f3124444ba..ae680c516400a3 100644 --- a/Lib/test/test_source_encoding.py +++ b/Lib/test/test_source_encoding.py @@ -1,7 +1,7 @@ # -*- coding: koi8-r -*- import unittest -from test.support import script_helper, captured_stdout, requires_subprocess +from test.support import script_helper, captured_stdout, requires_subprocess, requires_resource from test.support.os_helper import TESTFN, unlink, rmtree from test.support.import_helper import unload import importlib @@ -251,6 +251,7 @@ def test_crcrcrlf2(self): class UTF8ValidatorTest(unittest.TestCase): @unittest.skipIf(not sys.platform.startswith("linux"), "Too slow to run on non-Linux platforms") + @requires_resource('cpu') def test_invalid_utf8(self): # This is a port of test_utf8_decode_invalid_sequences in # test_unicode.py to exercise the separate utf8 validator in diff --git a/Lib/test/test_statistics.py b/Lib/test/test_statistics.py index 3e172e974e125d..9e69d918e37a69 100644 --- a/Lib/test/test_statistics.py +++ b/Lib/test/test_statistics.py @@ -2139,6 +2139,7 @@ def test_integer_sqrt_of_frac_rto(self): self.assertTrue(m * (r - 1)**2 < n < m * (r + 1)**2) @requires_IEEE_754 + @support.requires_resource('cpu') def test_float_sqrt_of_frac(self): def is_root_correctly_rounded(x: Fraction, root: float) -> bool: @@ -2744,6 +2745,7 @@ def test_cdf(self): self.assertTrue(math.isnan(X.cdf(float('NaN')))) @support.skip_if_pgo_task + @support.requires_resource('cpu') def test_inv_cdf(self): NormalDist = self.module.NormalDist diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 8e9ad06c6eb80c..7217508be8c029 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1290,6 +1290,7 @@ def test_bufsize_equal_one_binary_mode(self): with self.assertWarnsRegex(RuntimeWarning, 'line buffering'): self._test_bufsize_equal_one(line, b'', universal_newlines=False) + @support.requires_resource('cpu') def test_leaking_fds_on_error(self): # see bug #5179: Popen leaks file descriptors to PIPEs if # the child fails to execute; this will eventually exhaust diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index 23bcceedd71b2a..2a33889b7e97ac 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -510,6 +510,7 @@ def check_options(self, args, func, expected=None): self.assertEqual(proc.stdout.rstrip(), repr(expected)) self.assertEqual(proc.returncode, 0) + @support.requires_resource('cpu') def test_args_from_interpreter_flags(self): # Test test.support.args_from_interpreter_flags() for opts in ( diff --git a/Lib/test/test_sys_settrace.py b/Lib/test/test_sys_settrace.py index 96242bb260a612..14781bbb8622fa 100644 --- a/Lib/test/test_sys_settrace.py +++ b/Lib/test/test_sys_settrace.py @@ -2769,6 +2769,7 @@ def test_jump_extended_args_unpack_ex_tricky(output): ) = output.append(4) or "Spam" output.append(5) + @support.requires_resource('cpu') def test_jump_extended_args_for_iter(self): # In addition to failing when extended arg handling is broken, this can # also hang for a *very* long time: diff --git a/Lib/test/test_tools/test_freeze.py b/Lib/test/test_tools/test_freeze.py index 2ba36ca208f967..922e74b441457a 100644 --- a/Lib/test/test_tools/test_freeze.py +++ b/Lib/test/test_tools/test_freeze.py @@ -17,6 +17,7 @@ @support.skip_if_buildbot('not all buildbots have enough space') class TestFreeze(unittest.TestCase): + @support.requires_resource('cpu') # Building Python is slow def test_freeze_simple_script(self): script = textwrap.dedent(""" import sys diff --git a/Lib/test/test_trace.py b/Lib/test/test_trace.py index fad2b3b8379ffc..94b314381bfe21 100644 --- a/Lib/test/test_trace.py +++ b/Lib/test/test_trace.py @@ -1,7 +1,7 @@ import os from pickle import dump import sys -from test.support import captured_stdout +from test.support import captured_stdout, requires_resource from test.support.os_helper import (TESTFN, rmtree, unlink) from test.support.script_helper import assert_python_ok, assert_python_failure import textwrap @@ -369,6 +369,7 @@ def _coverage(self, tracer, r = tracer.results() r.write_results(show_missing=True, summary=True, coverdir=TESTFN) + @requires_resource('cpu') def test_coverage(self): tracer = trace.Trace(trace=0, count=1) with captured_stdout() as stdout: diff --git a/Lib/test/test_unicodedata.py b/Lib/test/test_unicodedata.py index 9e0097c892e7f7..c02d15d54b5f14 100644 --- a/Lib/test/test_unicodedata.py +++ b/Lib/test/test_unicodedata.py @@ -295,6 +295,7 @@ def test_ucd_510(self): self.assertTrue("\u1d79".upper()=='\ua77d') self.assertTrue(".".upper()=='.') + @requires_resource('cpu') def test_bug_5828(self): self.assertEqual("\u1d79".lower(), "\u1d79") # Only U+0000 should have U+0000 as its upper/lower/titlecase variant @@ -335,6 +336,7 @@ def unistr(data): return "".join([chr(x) for x in data]) @requires_resource('network') + @requires_resource('cpu') def test_normalization(self): TESTDATAFILE = "NormalizationTest.txt" TESTDATAURL = f"http://www.pythontest.net/unicode/{unicodedata.unidata_version}/{TESTDATAFILE}" diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index 86ce60fef13975..c7efff549b8631 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -20,7 +20,8 @@ from test.support import (captured_stdout, captured_stderr, requires_zlib, skip_if_broken_multiprocessing_synchronize, verbose, requires_subprocess, is_emscripten, is_wasi, - requires_venv_with_pip, TEST_HOME_DIR) + requires_venv_with_pip, TEST_HOME_DIR, + requires_resource) from test.support.os_helper import (can_symlink, EnvironmentVarGuard, rmtree) import unittest import venv @@ -756,6 +757,7 @@ def nicer_error(self): ) @requires_venv_with_pip() + @requires_resource('cpu') def test_with_pip(self): self.do_test_with_pip(False) self.do_test_with_pip(True) diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index 1bc1d05f7daba9..4cdd66d3769e0c 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -1933,6 +1933,7 @@ def test_threaded_weak_key_dict_copy(self): self.check_threaded_weak_dict_copy(weakref.WeakKeyDictionary, False) @threading_helper.requires_working_threading() + @support.requires_resource('cpu') def test_threaded_weak_key_dict_deepcopy(self): # Issue #35615: Weakref keys or values getting GC'ed during dict # copying should not result in a crash. @@ -1945,6 +1946,7 @@ def test_threaded_weak_value_dict_copy(self): self.check_threaded_weak_dict_copy(weakref.WeakValueDictionary, False) @threading_helper.requires_working_threading() + @support.requires_resource('cpu') def test_threaded_weak_value_dict_deepcopy(self): # Issue #35615: Weakref keys or values getting GC'ed during dict # copying should not result in a crash. From b30c83eb21e665fac7e5cb240943e6a4a70b3ec7 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 2 Sep 2023 23:53:02 -0700 Subject: [PATCH 246/632] [3.11] [3.12] gh-63760: Don't declare gethostname() on Solaris (GH-108817) (GH-108824) (#108832) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [3.12] gh-63760: Don't declare gethostname() on Solaris (GH-108817) (GH-108824) gh-63760: Don't declare gethostname() on Solaris (GH-108817) Since 2005, Solaris defines gethostname(). socketmodule.c no longer has to define gethostname() for Solaris. Oracle Solaris and OpenSolaris have patches to remove the gethostname() definition in Python: * https://github.com/oracle/solaris-userland/blob/master/components/python/python37/patches/15-gethostname.patch * https://github.com/OpenIndiana/oi-userland/blob/oi/hipster/components/python/python37/patches/15-gethostname.patch * https://github.com/omniosorg/omnios-build/blob/master/build/python27/patches/24-gethostname.patch (cherry picked from commit 7269916cd7b89b5e6f20bfe83ebe1038bda56b4b) Co-authored-by: Victor Stinner Co-authored-by: Jakub Kulík (cherry picked from commit 0e6d582b3b73a88e71cae04327b31a1ee203722c) Co-authored-by: Victor Stinner --- Include/pyport.h | 5 ----- .../next/Build/2023-09-02-18-04-15.gh-issue-63760.r8hJ6q.rst | 3 +++ 2 files changed, 3 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2023-09-02-18-04-15.gh-issue-63760.r8hJ6q.rst diff --git a/Include/pyport.h b/Include/pyport.h index 93250f4eb1d7a2..b3b8b6f09af3f0 100644 --- a/Include/pyport.h +++ b/Include/pyport.h @@ -439,11 +439,6 @@ Please be conservative with adding new ones, document them and enclose them in platform-specific #ifdefs. **************************************************************************/ -#ifdef SOLARIS -/* Unchecked */ -extern int gethostname(char *, int); -#endif - #ifdef HAVE__GETPTY #include /* we need to import mode_t */ extern char * _getpty(int *, int, mode_t, int); diff --git a/Misc/NEWS.d/next/Build/2023-09-02-18-04-15.gh-issue-63760.r8hJ6q.rst b/Misc/NEWS.d/next/Build/2023-09-02-18-04-15.gh-issue-63760.r8hJ6q.rst new file mode 100644 index 00000000000000..9a7249e923e0c7 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2023-09-02-18-04-15.gh-issue-63760.r8hJ6q.rst @@ -0,0 +1,3 @@ +Fix Solaris build: no longer redefine the ``gethostname()`` function. Solaris +defines the function since 2005. Patch by Victor Stinner, original patch by +Jakub Kulík. From ba47d872824212ff87ee6f62f4ae15b65ebb8a53 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 3 Sep 2023 06:01:49 -0700 Subject: [PATCH 247/632] [3.11] Reorder some test's decorators (GH-108804) (GH-108845) For example, do not demand the 'cpu' resource if the test cannot be run due to non-working threads. (cherry picked from commit 509bb61977cc8a4487efd3f9cdd63d9f7b86be62) Co-authored-by: Serhiy Storchaka --- Lib/test/test_io.py | 4 ++-- Lib/test/test_site.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 79aa2da58622b2..0a5f82a8bed114 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -1459,8 +1459,8 @@ def test_read_all(self): self.assertEqual(b"abcdefg", bufio.read()) - @support.requires_resource('cpu') @threading_helper.requires_working_threading() + @support.requires_resource('cpu') def test_threads(self): try: # Write out many bytes with exactly the same number of 0's, @@ -1834,8 +1834,8 @@ def test_truncate_after_write(self): f.truncate() self.assertEqual(f.tell(), buffer_size + 2) - @support.requires_resource('cpu') @threading_helper.requires_working_threading() + @support.requires_resource('cpu') def test_threads(self): try: # Write out many bytes from many threads and test they were diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index b5dc381a2f3bba..12a2b73a3fe2fb 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -466,10 +466,10 @@ def test_sitecustomize_executed(self): else: self.fail("sitecustomize not imported automatically") - @test.support.requires_resource('network') - @test.support.system_must_validate_cert @unittest.skipUnless(hasattr(urllib.request, "HTTPSHandler"), 'need SSL support to download license') + @test.support.requires_resource('network') + @test.support.system_must_validate_cert def test_license_exists_at_url(self): # This test is a bit fragile since it depends on the format of the # string displayed by license in the absence of a LICENSE file. From 79f7a4c0a4ae6c39e6a893b060b4557e09d0ac18 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sun, 3 Sep 2023 19:21:53 +0200 Subject: [PATCH 248/632] [3.11] gh-108822: Backport libregrtest changes from the main branch (#108820) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Revert "[3.11] gh-101634: regrtest reports decoding error as failed test (#106169) (#106175)" This reverts commit d5418e97fc524420011a370ba3c2c3cf6a89a74f. * Revert "[3.11] bpo-46523: fix tests rerun when `setUp[Class|Module]` fails (GH-30895) (GH-103342)" This reverts commit ecb09a849689764193e0115d27e220f82b5f6d9f. * Revert "gh-95027: Fix regrtest stdout encoding on Windows (GH-98492)" This reverts commit b2aa28eec56d07b9c6777b02b7247cf21839de9f. * Revert "[3.11] gh-94026: Buffer regrtest worker stdout in temporary file (GH-94253) (GH-94408)" This reverts commit 0122ab235b5acb52dd99fd05d8802a00f438b828. * Revert "Run Tools/scripts/reindent.py (GH-94225)" This reverts commit f0f3a424afb00a15ce8c0140dd218f5b33929be6. * Revert "gh-94052: Don't re-run failed tests with --python option (GH-94054)" This reverts commit 1347607db12012f6458ffcba48d8ad797083812e. * Revert "[3.11] gh-84461: Fix Emscripten umask and permission issues (GH-94002) (GH-94006)" This reverts commit 10731849184a3101ed18683b0128d689f1671c3f. * gh-93353: regrtest checks for leaked temporary files (#93776) When running tests with -jN, create a temporary directory per process and mark a test as "environment changed" if a test leaks a temporary file or directory. (cherry picked from commit e566ce5496f1bad81c431aaee65e36d5e44771c5) * gh-93353: Fix regrtest for -jN with N >= 2 (GH-93813) (cherry picked from commit 36934a16e86f34d69ba2d41630fb5b4d06d59cff) * gh-93353: regrtest supports checking tmp files with -j2 (#93909) regrtest now also implements checking for leaked temporary files and directories when using -jN for N >= 2. Use tempfile.mkdtemp() to create the temporary directory. Skip this check on WASI. (cherry picked from commit 4f85cec9e2077681b3dacc3108e646d509b720bf) * gh-84461: Fix Emscripten umask and permission issues (GH-94002) - Emscripten's default umask is too strict, see https://github.com/emscripten-core/emscripten/issues/17269 - getuid/getgid and geteuid/getegid are stubs that always return 0 (root). Disable effective uid/gid syscalls and fix tests that use chmod() current user. - Cannot drop X bit from directory. (cherry picked from commit 2702e408fd0e0dd7aec396b4cf8c7ce9caae81d8) * gh-94052: Don't re-run failed tests with --python option (#94054) (cherry picked from commit 0ff7b996f5d836e63cdaf652c7aa734285261096) * Run Tools/scripts/reindent.py (#94225) Reindent files which were not properly formatted (PEP 8: 4 spaces). Remove also some trailing spaces. (cherry picked from commit e87ada48a9e5d9d03f9759138869216df0d7383a) * gh-94026: Buffer regrtest worker stdout in temporary file (GH-94253) Co-authored-by: Victor Stinner (cherry picked from commit 199ba233248ab279f445e0809c2077976f0711bc) * gh-96465: Clear fractions hash lru_cache under refleak testing (GH-96689) Automerge-Triggered-By: GH:zware (cherry picked from commit 9c8f3794337457b1d905a9fa0f38c2978fe32abd) * gh-95027: Fix regrtest stdout encoding on Windows (#98492) On Windows, when the Python test suite is run with the -jN option, the ANSI code page is now used as the encoding for the stdout temporary file, rather than using UTF-8 which can lead to decoding errors. (cherry picked from commit ec1f6f5f139868dc2c1116a7c7c878c38c668d53) * gh-98903: Test suite fails with exit code 4 if no tests ran (#98904) The Python test suite now fails wit exit code 4 if no tests ran. It should help detecting typos in test names and test methods. * Add "EXITCODE_" constants to Lib/test/libregrtest/main.py. * Fix a typo: "NO TEST RUN" becomes "NO TESTS RAN" (cherry picked from commit c76db37c0d23174cbffd6fa978d39693890ef020) * gh-100086: Add build info to test.libregrtest (#100093) The Python test runner (libregrtest) now logs Python build information like "debug" vs "release" build, or LTO and PGO optimizations. (cherry picked from commit 3c892022472eb975360fb3f0caa6f6fcc6fbf220) * bpo-46523: fix tests rerun when `setUp[Class|Module]` fails (#30895) Co-authored-by: Jelle Zijlstra Co-authored-by: Łukasz Langa (cherry picked from commit 995386071f96e4cfebfa027a71ca9134e4651d2a) * gh-82054: allow test runner to split test_asyncio to execute in parallel by sharding. (#103927) This runs test_asyncio sub-tests in parallel using sharding from Cinder. This suite is typically the longest-pole in runs because it is a test package with a lot of further sub-tests otherwise run serially. By breaking out the sub-tests as independent modules we can run a lot more in parallel. After porting we can see the direct impact on a multicore system. Without this change: Running make test is 5 min 26 seconds With this change: Running make test takes 3 min 39 seconds That'll vary based on system and parallelism. On a `-j 4` run similar to what CI and buildbot systems often do, it reduced the overall test suite completion latency by 10%. The drawbacks are that this implementation is hacky and due to the sorting of the tests it obscures when the asyncio tests occur and involves changing CPython test infrastructure but, the wall time saved it is worth it, especially in low-core count CI runs as it pulls a long tail. The win for productivity and reserved CI resource usage is significant. Future tests that deserve to be refactored into split up suites to benefit from are test_concurrent_futures and the way the _test_multiprocessing suite gets run for all start methods. As exposed by passing the -o flag to python -m test to get a list of the 10 longest running tests. --------- Co-authored-by: Carl Meyer Co-authored-by: Gregory P. Smith [Google, LLC] (cherry picked from commit 9e011e7c77dad7d0bbb944c44891531606caeb21) * Display the sanitizer config in the regrtest header. (#105301) Display the sanitizers present in libregrtest. Having this in the CI output for tests with the relevant environment variable displayed will help make it easier to do what we need to create an equivalent local test run. (cherry picked from commit 852348ab65783601e0844b6647ea033668b45c11) * gh-101634: regrtest reports decoding error as failed test (#106169) When running the Python test suite with -jN option, if a worker stdout cannot be decoded from the locale encoding report a failed testn so the exitcode is non-zero. (cherry picked from commit 2ac3eec103cf450aaaebeb932e51155d2e7fb37b) * gh-108223: test.pythoninfo and libregrtest log Py_NOGIL (#108238) Enable with --disable-gil --without-pydebug: $ make pythoninfo|grep NOGIL sysconfig[Py_NOGIL]: 1 $ ./python -m test ... == Python build: nogil debug ... (cherry picked from commit 5afe0c17ca14df430736e549542a4b85e7e7c7ac) * gh-90791: test.pythoninfo logs ASAN_OPTIONS env var (#108289) * Cleanup libregrtest code logging ASAN_OPTIONS. * Fix a typo on "ASAN_OPTIONS" vs "MSAN_OPTIONS". (cherry picked from commit 3a1ac87f8f89d3206b46a0df4908afae629d669d) * gh-108388: regrtest splits test_asyncio package (#108393) Currently, test_asyncio package is only splitted into sub-tests when using command "./python -m test". With this change, it's also splitted when passing it on the command line: "./python -m test test_asyncio". Remove the concept of "STDTESTS". Python is now mature enough to not have to bother with that anymore. Removing STDTESTS simplify the code. (cherry picked from commit 174e9da0836844a2138cc8915dd305cb2cd7a583) * regrtest computes statistics (#108793) test_netrc, test_pep646_syntax and test_xml_etree now return results in the test_main() function. Changes: * Rewrite TestResult as a dataclass with a new State class. * Add test.support.TestStats class and Regrtest.stats_dict attribute. * libregrtest.runtest functions now modify a TestResult instance in-place. * libregrtest summary lists the number of run tests and skipped tests, and denied resources. * Add TestResult.has_meaningful_duration() method. * Compute TestResult duration in the upper function. * Use time.perf_counter() instead of time.monotonic(). * Regrtest: rename 'resource_denieds' attribute to 'resource_denied'. * Rename CHILD_ERROR to MULTIPROCESSING_ERROR. * Use match/case syntadx to have different code depending on the test state. Co-authored-by: Alex Waygood (cherry picked from commit d4e534cbb35678c82b3a1276826af55d7bfc23b6) * gh-108822: Add Changelog entry for regrtest statistics (#108821) --------- Co-authored-by: Christian Heimes Co-authored-by: Zachary Ware Co-authored-by: Nikita Sobolev Co-authored-by: Joshua Herman Co-authored-by: Gregory P. Smith --- Lib/test/libregrtest/main.py | 195 ++++++--- Lib/test/libregrtest/refleak.py | 5 +- Lib/test/libregrtest/runtest.py | 380 ++++++++++-------- Lib/test/libregrtest/runtest_mp.py | 117 +++--- Lib/test/libregrtest/save_env.py | 8 +- Lib/test/libregrtest/utils.py | 97 +++++ Lib/test/pythoninfo.py | 8 + Lib/test/support/__init__.py | 76 +++- Lib/test/test_netrc.py | 2 +- Lib/test/test_pep646_syntax.py | 2 +- Lib/test/test_regrtest.py | 277 +++++++++---- Lib/test/test_xml_etree.py | 2 +- ...2-06-16-17-50-58.gh-issue-93353.JdpATx.rst | 2 + ...2-10-20-17-49-50.gh-issue-95027.viRpJB.rst | 4 + ...2-10-31-14-47-49.gh-issue-98903.7KinCV.rst | 2 + ...-12-08-00-03-37.gh-issue-100086.1zYpto.rst | 3 + ...-06-28-02-51-08.gh-issue-101634.Rayczr.rst | 3 + ...-09-02-19-06-52.gh-issue-108822.arTbBI.rst | 4 + 18 files changed, 812 insertions(+), 375 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2022-06-16-17-50-58.gh-issue-93353.JdpATx.rst create mode 100644 Misc/NEWS.d/next/Tests/2022-10-20-17-49-50.gh-issue-95027.viRpJB.rst create mode 100644 Misc/NEWS.d/next/Tests/2022-10-31-14-47-49.gh-issue-98903.7KinCV.rst create mode 100644 Misc/NEWS.d/next/Tests/2022-12-08-00-03-37.gh-issue-100086.1zYpto.rst create mode 100644 Misc/NEWS.d/next/Tests/2023-06-28-02-51-08.gh-issue-101634.Rayczr.rst create mode 100644 Misc/NEWS.d/next/Tests/2023-09-02-19-06-52.gh-issue-108822.arTbBI.rst diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index 0125227bf11755..6e6423e156781b 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -11,14 +11,14 @@ import unittest from test.libregrtest.cmdline import _parse_args from test.libregrtest.runtest import ( - findtests, runtest, get_abs_module, is_failed, - STDTESTS, NOTTESTS, PROGRESS_MIN_TIME, - Passed, Failed, EnvChanged, Skipped, ResourceDenied, Interrupted, - ChildError, DidNotRun) + findtests, split_test_packages, runtest, get_abs_module, + PROGRESS_MIN_TIME, State) from test.libregrtest.setup import setup_tests from test.libregrtest.pgo import setup_pgo_tests -from test.libregrtest.utils import removepy, count, format_duration, printlist +from test.libregrtest.utils import (removepy, count, format_duration, + printlist, get_build_info) from test import support +from test.support import TestStats from test.support import os_helper from test.support import threading_helper @@ -77,13 +77,14 @@ def __init__(self): self.good = [] self.bad = [] self.skipped = [] - self.resource_denieds = [] + self.resource_denied = [] self.environment_changed = [] self.run_no_tests = [] self.need_rerun = [] self.rerun = [] self.first_result = None self.interrupted = False + self.stats_dict: dict[str, TestStats] = {} # used by --slow self.test_times = [] @@ -92,7 +93,7 @@ def __init__(self): self.tracer = None # used to display the progress bar "[ 3/100]" - self.start_time = time.monotonic() + self.start_time = time.perf_counter() self.test_count = '' self.test_count_width = 1 @@ -110,36 +111,41 @@ def __init__(self): def get_executed(self): return (set(self.good) | set(self.bad) | set(self.skipped) - | set(self.resource_denieds) | set(self.environment_changed) + | set(self.resource_denied) | set(self.environment_changed) | set(self.run_no_tests)) def accumulate_result(self, result, rerun=False): - test_name = result.name - - if not isinstance(result, (ChildError, Interrupted)) and not rerun: - self.test_times.append((result.duration_sec, test_name)) - - if isinstance(result, Passed): - self.good.append(test_name) - elif isinstance(result, ResourceDenied): - self.skipped.append(test_name) - self.resource_denieds.append(test_name) - elif isinstance(result, Skipped): - self.skipped.append(test_name) - elif isinstance(result, EnvChanged): - self.environment_changed.append(test_name) - elif isinstance(result, Failed): - if not rerun: - self.bad.append(test_name) - self.need_rerun.append(result) - elif isinstance(result, DidNotRun): - self.run_no_tests.append(test_name) - elif isinstance(result, Interrupted): - self.interrupted = True - else: - raise ValueError("invalid test result: %r" % result) + test_name = result.test_name + + if result.has_meaningful_duration() and not rerun: + self.test_times.append((result.duration, test_name)) - if rerun and not isinstance(result, (Failed, Interrupted)): + match result.state: + case State.PASSED: + self.good.append(test_name) + case State.ENV_CHANGED: + self.environment_changed.append(test_name) + case State.SKIPPED: + self.skipped.append(test_name) + case State.RESOURCE_DENIED: + self.skipped.append(test_name) + self.resource_denied.append(test_name) + case State.INTERRUPTED: + self.interrupted = True + case State.DID_NOT_RUN: + self.run_no_tests.append(test_name) + case _: + if result.is_failed(self.ns.fail_env_changed): + if not rerun: + self.bad.append(test_name) + self.need_rerun.append(result) + else: + raise ValueError(f"invalid test state: {state!r}") + + if result.stats is not None: + self.stats_dict[result.test_name] = result.stats + + if rerun and not(result.is_failed(False) or result.state == State.INTERRUPTED): self.bad.remove(test_name) xml_data = result.xml_data @@ -161,7 +167,7 @@ def log(self, line=''): line = f"load avg: {load_avg:.2f} {line}" # add the timestamp prefix: "0:01:05 " - test_time = time.monotonic() - self.start_time + test_time = time.perf_counter() - self.start_time mins, secs = divmod(int(test_time), 60) hours, mins = divmod(mins, 60) @@ -245,26 +251,23 @@ def find_tests(self, tests): # add default PGO tests if no tests are specified setup_pgo_tests(self.ns) - stdtests = STDTESTS[:] - nottests = NOTTESTS.copy() + exclude = set() if self.ns.exclude: for arg in self.ns.args: - if arg in stdtests: - stdtests.remove(arg) - nottests.add(arg) + exclude.add(arg) self.ns.args = [] - # if testdir is set, then we are not running the python tests suite, so - # don't add default tests to be executed or skipped (pass empty values) - if self.ns.testdir: - alltests = findtests(self.ns.testdir, list(), set()) - else: - alltests = findtests(self.ns.testdir, stdtests, nottests) + alltests = findtests(testdir=self.ns.testdir, exclude=exclude) if not self.ns.fromfile: - self.selected = self.tests or self.ns.args or alltests + self.selected = self.tests or self.ns.args + if self.selected: + self.selected = split_test_packages(self.selected) + else: + self.selected = alltests else: self.selected = self.tests + if self.ns.single: self.selected = self.selected[:1] try: @@ -339,7 +342,7 @@ def rerun_failed_tests(self): rerun_list = list(self.need_rerun) self.need_rerun.clear() for result in rerun_list: - test_name = result.name + test_name = result.test_name self.rerun.append(test_name) errors = result.errors or [] @@ -366,7 +369,7 @@ def rerun_failed_tests(self): self.accumulate_result(result, rerun=True) - if isinstance(result, Interrupted): + if result.state == State.INTERRUPTED: break if self.bad: @@ -463,7 +466,7 @@ def run_tests_sequential(self): previous_test = None for test_index, test_name in enumerate(self.tests, 1): - start_time = time.monotonic() + start_time = time.perf_counter() text = test_name if previous_test: @@ -482,14 +485,14 @@ def run_tests_sequential(self): result = runtest(self.ns, test_name) self.accumulate_result(result) - if isinstance(result, Interrupted): + if result.state == State.INTERRUPTED: break previous_test = str(result) - test_time = time.monotonic() - start_time + test_time = time.perf_counter() - start_time if test_time >= PROGRESS_MIN_TIME: previous_test = "%s in %s" % (previous_test, format_duration(test_time)) - elif isinstance(result, Passed): + elif result.state == State.PASSED: # be quiet: say nothing if the test passed shortly previous_test = None @@ -498,7 +501,7 @@ def run_tests_sequential(self): if module not in save_modules and module.startswith("test."): support.unload(module) - if self.ns.failfast and is_failed(result, self.ns): + if self.ns.failfast and result.is_failed(self.ns.fail_env_changed): break if previous_test: @@ -518,12 +521,44 @@ def display_header(self): print("==", platform.python_implementation(), *sys.version.split()) print("==", platform.platform(aliased=True), "%s-endian" % sys.byteorder) + print("== Python build:", ' '.join(get_build_info())) print("== cwd:", os.getcwd()) cpu_count = os.cpu_count() if cpu_count: print("== CPU count:", cpu_count) print("== encodings: locale=%s, FS=%s" % (locale.getencoding(), sys.getfilesystemencoding())) + self.display_sanitizers() + + def display_sanitizers(self): + # This makes it easier to remember what to set in your local + # environment when trying to reproduce a sanitizer failure. + asan = support.check_sanitizer(address=True) + msan = support.check_sanitizer(memory=True) + ubsan = support.check_sanitizer(ub=True) + sanitizers = [] + if asan: + sanitizers.append("address") + if msan: + sanitizers.append("memory") + if ubsan: + sanitizers.append("undefined behavior") + if not sanitizers: + return + + print(f"== sanitizers: {', '.join(sanitizers)}") + for sanitizer, env_var in ( + (asan, "ASAN_OPTIONS"), + (msan, "MSAN_OPTIONS"), + (ubsan, "UBSAN_OPTIONS"), + ): + options= os.environ.get(env_var) + if sanitizer and options is not None: + print(f"== {env_var}={options!r}") + + def no_tests_run(self): + return not any((self.good, self.bad, self.skipped, self.interrupted, + self.environment_changed)) def get_tests_result(self): result = [] @@ -531,9 +566,8 @@ def get_tests_result(self): result.append("FAILURE") elif self.ns.fail_env_changed and self.environment_changed: result.append("ENV CHANGED") - elif not any((self.good, self.bad, self.skipped, self.interrupted, - self.environment_changed)): - result.append("NO TEST RUN") + elif self.no_tests_run(): + result.append("NO TESTS RAN") if self.interrupted: result.append("INTERRUPTED") @@ -609,13 +643,48 @@ def finalize(self): coverdir=self.ns.coverdir) print() - duration = time.monotonic() - self.start_time - print("Total duration: %s" % format_duration(duration)) - print("Tests result: %s" % self.get_tests_result()) + self.display_summary() if self.ns.runleaks: os.system("leaks %d" % os.getpid()) + def display_summary(self): + duration = time.perf_counter() - self.start_time + + # Total duration + print("Total duration: %s" % format_duration(duration)) + + # Total tests + total = TestStats() + for stats in self.stats_dict.values(): + total.accumulate(stats) + stats = [f'run={total.tests_run:,}'] + if total.failures: + stats.append(f'failures={total.failures:,}') + if total.skipped: + stats.append(f'skipped={total.skipped:,}') + print(f"Total tests: {' '.join(stats)}") + + # Total test files + report = [f'success={len(self.good)}'] + if self.bad: + report.append(f'failed={len(self.bad)}') + if self.environment_changed: + report.append(f'env_changed={len(self.environment_changed)}') + if self.skipped: + report.append(f'skipped={len(self.skipped)}') + if self.resource_denied: + report.append(f'resource_denied={len(self.resource_denied)}') + if self.rerun: + report.append(f'rerun={len(self.rerun)}') + if self.run_no_tests: + report.append(f'run_no_tests={len(self.run_no_tests)}') + print(f"Total test files: {' '.join(report)}") + + # Result + result = self.get_tests_result() + print(f"Result: {result}") + def save_xml_result(self): if not self.ns.xmlpath and not self.testsuite_xml: return @@ -782,11 +851,13 @@ def _main(self, tests, kwargs): self.save_xml_result() if self.bad: - sys.exit(2) + sys.exit(EXITCODE_BAD_TEST) if self.interrupted: - sys.exit(130) + sys.exit(EXITCODE_INTERRUPTED) if self.ns.fail_env_changed and self.environment_changed: - sys.exit(3) + sys.exit(EXITCODE_ENV_CHANGED) + if self.no_tests_run(): + sys.exit(EXITCODE_NO_TESTS_RAN) sys.exit(0) diff --git a/Lib/test/libregrtest/refleak.py b/Lib/test/libregrtest/refleak.py index a0538cbb3c3772..58a1419e0501df 100644 --- a/Lib/test/libregrtest/refleak.py +++ b/Lib/test/libregrtest/refleak.py @@ -83,11 +83,12 @@ def get_pooled_int(value): print(("1234567890"*(repcount//10 + 1))[:repcount], file=sys.stderr, flush=True) + results = None dash_R_cleanup(fs, ps, pic, zdc, abcs) support.gc_collect() for i in rep_range: - test_func() + results = test_func() dash_R_cleanup(fs, ps, pic, zdc, abcs) support.gc_collect() @@ -146,7 +147,7 @@ def check_fd_deltas(deltas): print(msg, file=refrep) refrep.flush() failed = True - return failed + return (failed, results) def dash_R_cleanup(fs, ps, pic, zdc, abcs): diff --git a/Lib/test/libregrtest/runtest.py b/Lib/test/libregrtest/runtest.py index 62cf1a3f1175ee..8ca7b8bb6526ca 100644 --- a/Lib/test/libregrtest/runtest.py +++ b/Lib/test/libregrtest/runtest.py @@ -1,3 +1,5 @@ +import dataclasses +import doctest import faulthandler import functools import gc @@ -10,6 +12,7 @@ import unittest from test import support +from test.support import TestStats from test.support import os_helper from test.support import threading_helper from test.libregrtest.cmdline import Namespace @@ -17,153 +20,169 @@ from test.libregrtest.utils import clear_caches, format_duration, print_warning +# Avoid enum.Enum to reduce the number of imports when tests are run +class State: + PASSED = "PASSED" + FAILED = "FAILED" + SKIPPED = "SKIPPED" + UNCAUGHT_EXC = "UNCAUGHT_EXC" + REFLEAK = "REFLEAK" + ENV_CHANGED = "ENV_CHANGED" + RESOURCE_DENIED = "RESOURCE_DENIED" + INTERRUPTED = "INTERRUPTED" + MULTIPROCESSING_ERROR = "MULTIPROCESSING_ERROR" + DID_NOT_RUN = "DID_NOT_RUN" + TIMEOUT = "TIMEOUT" + + @staticmethod + def is_failed(state): + return state in { + State.FAILED, + State.UNCAUGHT_EXC, + State.REFLEAK, + State.MULTIPROCESSING_ERROR, + State.TIMEOUT} + + @staticmethod + def has_meaningful_duration(state): + # Consider that the duration is meaningless for these cases. + # For example, if a whole test file is skipped, its duration + # is unlikely to be the duration of executing its tests, + # but just the duration to execute code which skips the test. + return state not in { + State.SKIPPED, + State.RESOURCE_DENIED, + State.INTERRUPTED, + State.MULTIPROCESSING_ERROR, + State.DID_NOT_RUN} + + +@dataclasses.dataclass(slots=True) class TestResult: - def __init__( - self, - name: str, - duration_sec: float = 0.0, - xml_data: list[str] | None = None, - ) -> None: - self.name = name - self.duration_sec = duration_sec - self.xml_data = xml_data - - def __str__(self) -> str: - return f"{self.name} finished" - - -class Passed(TestResult): - def __str__(self) -> str: - return f"{self.name} passed" - - -class Failed(TestResult): - def __init__( - self, - name: str, - duration_sec: float = 0.0, - xml_data: list[str] | None = None, - errors: list[tuple[str, str]] | None = None, - failures: list[tuple[str, str]] | None = None, - ) -> None: - super().__init__(name, duration_sec=duration_sec, xml_data=xml_data) - self.errors = errors - self.failures = failures - - def __str__(self) -> str: + test_name: str + state: str | None = None + # Test duration in seconds + duration: float | None = None + xml_data: list[str] | None = None + stats: TestStats | None = None + + # errors and failures copied from support.TestFailedWithDetails + errors: list[tuple[str, str]] | None = None + failures: list[tuple[str, str]] | None = None + + def is_failed(self, fail_env_changed: bool) -> bool: + if self.state == State.ENV_CHANGED: + return fail_env_changed + return State.is_failed(self.state) + + def _format_failed(self): if self.errors and self.failures: le = len(self.errors) lf = len(self.failures) error_s = "error" + ("s" if le > 1 else "") failure_s = "failure" + ("s" if lf > 1 else "") - return f"{self.name} failed ({le} {error_s}, {lf} {failure_s})" + return f"{self.test_name} failed ({le} {error_s}, {lf} {failure_s})" if self.errors: le = len(self.errors) error_s = "error" + ("s" if le > 1 else "") - return f"{self.name} failed ({le} {error_s})" + return f"{self.test_name} failed ({le} {error_s})" if self.failures: lf = len(self.failures) failure_s = "failure" + ("s" if lf > 1 else "") - return f"{self.name} failed ({lf} {failure_s})" - - return f"{self.name} failed" - - -class UncaughtException(Failed): - def __str__(self) -> str: - return f"{self.name} failed (uncaught exception)" - - -class EnvChanged(Failed): - def __str__(self) -> str: - return f"{self.name} failed (env changed)" - - -class RefLeak(Failed): - def __str__(self) -> str: - return f"{self.name} failed (reference leak)" - - -class Skipped(TestResult): - def __str__(self) -> str: - return f"{self.name} skipped" - - -class ResourceDenied(Skipped): - def __str__(self) -> str: - return f"{self.name} skipped (resource denied)" - - -class Interrupted(TestResult): - def __str__(self) -> str: - return f"{self.name} interrupted" - - -class ChildError(Failed): - def __str__(self) -> str: - return f"{self.name} crashed" - - -class DidNotRun(TestResult): - def __str__(self) -> str: - return f"{self.name} ran no tests" + return f"{self.test_name} failed ({lf} {failure_s})" + return f"{self.test_name} failed" -class Timeout(Failed): def __str__(self) -> str: - return f"{self.name} timed out ({format_duration(self.duration_sec)})" + match self.state: + case State.PASSED: + return f"{self.test_name} passed" + case State.FAILED: + return self._format_failed() + case State.SKIPPED: + return f"{self.test_name} skipped" + case State.UNCAUGHT_EXC: + return f"{self.test_name} failed (uncaught exception)" + case State.REFLEAK: + return f"{self.test_name} failed (reference leak)" + case State.ENV_CHANGED: + return f"{self.test_name} failed (env changed)" + case State.RESOURCE_DENIED: + return f"{self.test_name} skipped (resource denied)" + case State.INTERRUPTED: + return f"{self.test_name} interrupted" + case State.MULTIPROCESSING_ERROR: + return f"{self.test_name} process crashed" + case State.DID_NOT_RUN: + return f"{self.test_name} ran no tests" + case State.TIMEOUT: + return f"{self.test_name} timed out ({format_duration(self.duration)})" + case _: + raise ValueError("unknown result state: {state!r}") + + def has_meaningful_duration(self): + return State.has_meaningful_duration(self.state) + + def set_env_changed(self): + if self.state is None or self.state == State.PASSED: + self.state = State.ENV_CHANGED # Minimum duration of a test to display its duration or to mention that # the test is running in background PROGRESS_MIN_TIME = 30.0 # seconds -# small set of tests to determine if we have a basically functioning interpreter -# (i.e. if any of these fail, then anything else is likely to follow) -STDTESTS = [ - 'test_grammar', - 'test_opcodes', - 'test_dict', - 'test_builtin', - 'test_exceptions', - 'test_types', - 'test_unittest', - 'test_doctest', - 'test_doctest2', - 'test_support' -] - -# set of tests that we don't want to be executed when using regrtest -NOTTESTS = set() +#If these test directories are encountered recurse into them and treat each +# test_ .py or dir as a separate test module. This can increase parallelism. +# Beware this can't generally be done for any directory with sub-tests as the +# __init__.py may do things which alter what tests are to be run. +SPLITTESTDIRS = { + "test_asyncio", +} # Storage of uncollectable objects FOUND_GARBAGE = [] -def is_failed(result: TestResult, ns: Namespace) -> bool: - if isinstance(result, EnvChanged): - return ns.fail_env_changed - return isinstance(result, Failed) - - def findtestdir(path=None): return path or os.path.dirname(os.path.dirname(__file__)) or os.curdir -def findtests(testdir=None, stdtests=STDTESTS, nottests=NOTTESTS): +def findtests(*, testdir=None, exclude=(), + split_test_dirs=SPLITTESTDIRS, base_mod=""): """Return a list of all applicable test modules.""" testdir = findtestdir(testdir) - names = os.listdir(testdir) tests = [] - others = set(stdtests) | nottests - for name in names: + for name in os.listdir(testdir): mod, ext = os.path.splitext(name) - if mod[:5] == "test_" and ext in (".py", "") and mod not in others: - tests.append(mod) - return stdtests + sorted(tests) + if (not mod.startswith("test_")) or (mod in exclude): + continue + if mod in split_test_dirs: + subdir = os.path.join(testdir, mod) + mod = f"{base_mod or 'test'}.{mod}" + tests.extend(findtests(testdir=subdir, exclude=exclude, + split_test_dirs=split_test_dirs, base_mod=mod)) + elif ext in (".py", ""): + tests.append(f"{base_mod}.{mod}" if base_mod else mod) + return sorted(tests) + + +def split_test_packages(tests, *, testdir=None, exclude=(), + split_test_dirs=SPLITTESTDIRS): + testdir = findtestdir(testdir) + splitted = [] + for name in tests: + if name in split_test_dirs: + subdir = os.path.join(testdir, name) + splitted.extend(findtests(testdir=subdir, exclude=exclude, + split_test_dirs=split_test_dirs, + base_mod=name)) + else: + splitted.append(name) + return splitted def get_abs_module(ns: Namespace, test_name: str) -> str: @@ -174,9 +193,9 @@ def get_abs_module(ns: Namespace, test_name: str) -> str: return 'test.' + test_name -def _runtest(ns: Namespace, test_name: str) -> TestResult: - # Handle faulthandler timeout, capture stdout+stderr, XML serialization - # and measure time. +def _runtest_capture_output_timeout_junit(result: TestResult, ns: Namespace) -> None: + # Capture stdout and stderr, set faulthandler timeout, + # and create JUnit XML report. output_on_failure = ns.verbose3 @@ -186,7 +205,6 @@ def _runtest(ns: Namespace, test_name: str) -> TestResult: if use_timeout: faulthandler.dump_traceback_later(ns.timeout, exit=True) - start_time = time.perf_counter() try: support.set_match_tests(ns.match_tests, ns.ignore_tests) support.junit_xml_list = xml_list = [] if ns.xmlpath else None @@ -211,9 +229,9 @@ def _runtest(ns: Namespace, test_name: str) -> TestResult: # warnings will be written to sys.stderr below. print_warning.orig_stderr = stream - result = _runtest_inner(ns, test_name, - display_failure=False) - if not isinstance(result, Passed): + _runtest_env_changed_exc(result, ns, display_failure=False) + # Ignore output if the test passed successfully + if result.state != State.PASSED: output = stream.getvalue() finally: sys.stdout = orig_stdout @@ -227,18 +245,13 @@ def _runtest(ns: Namespace, test_name: str) -> TestResult: # Tell tests to be moderately quiet support.verbose = ns.verbose - result = _runtest_inner(ns, test_name, - display_failure=not ns.verbose) + _runtest_env_changed_exc(result, ns, + display_failure=not ns.verbose) if xml_list: import xml.etree.ElementTree as ET - result.xml_data = [ - ET.tostring(x).decode('us-ascii') - for x in xml_list - ] - - result.duration_sec = time.perf_counter() - start_time - return result + result.xml_data = [ET.tostring(x).decode('us-ascii') + for x in xml_list] finally: if use_timeout: faulthandler.cancel_dump_traceback_later() @@ -251,19 +264,23 @@ def runtest(ns: Namespace, test_name: str) -> TestResult: ns -- regrtest namespace of options test_name -- the name of the test - Returns a TestResult sub-class depending on the kind of result received. + Returns a TestResult. If ns.xmlpath is not None, xml_data is a list containing each generated testsuite element. """ + start_time = time.perf_counter() + result = TestResult(test_name) try: - return _runtest(ns, test_name) + _runtest_capture_output_timeout_junit(result, ns) except: if not ns.pgo: msg = traceback.format_exc() print(f"test {test_name} crashed -- {msg}", file=sys.stderr, flush=True) - return Failed(test_name) + result.state = State.UNCAUGHT_EXC + result.duration = time.perf_counter() - start_time + return result def _test_module(the_module): @@ -273,18 +290,48 @@ def _test_module(the_module): print(error, file=sys.stderr) if loader.errors: raise Exception("errors while loading tests") - support.run_unittest(tests) + return support.run_unittest(tests) def save_env(ns: Namespace, test_name: str): return saved_test_environment(test_name, ns.verbose, ns.quiet, pgo=ns.pgo) -def _runtest_inner2(ns: Namespace, test_name: str) -> bool: - # Load the test function, run the test function, handle huntrleaks - # to detect leaks. +def regrtest_runner(result, test_func, ns) -> None: + # Run test_func(), collect statistics, and detect reference and memory + # leaks. + + if ns.huntrleaks: + from test.libregrtest.refleak import dash_R + refleak, test_result = dash_R(ns, result.test_name, test_func) + else: + test_result = test_func() + refleak = False + + if refleak: + result.state = State.REFLEAK + + match test_result: + case TestStats(): + stats = test_result + case unittest.TestResult(): + stats = TestStats.from_unittest(test_result) + case doctest.TestResults(): + stats = TestStats.from_doctest(test_result) + case None: + print_warning(f"{result.test_name} test runner returned None: {test_func}") + stats = None + case _: + print_warning(f"Unknown test result type: {type(test_result)}") + stats = None + + result.stats = stats + - abstest = get_abs_module(ns, test_name) +def _load_run_test(result: TestResult, ns: Namespace) -> None: + # Load the test function, run the test function. + + abstest = get_abs_module(ns, result.test_name) # remove the module from sys.module to reload it if it was already imported try: @@ -294,23 +341,15 @@ def _runtest_inner2(ns: Namespace, test_name: str) -> bool: the_module = importlib.import_module(abstest) - if ns.huntrleaks: - from test.libregrtest.refleak import dash_R - # If the test has a test_main, that will run the appropriate # tests. If not, use normal unittest test loading. - test_runner = getattr(the_module, "test_main", None) - if test_runner is None: - test_runner = functools.partial(_test_module, the_module) + test_func = getattr(the_module, "test_main", None) + if test_func is None: + test_func = functools.partial(_test_module, the_module) try: - with save_env(ns, test_name): - if ns.huntrleaks: - # Return True if the test leaked references - refleak = dash_R(ns, test_name, test_runner) - else: - test_runner() - refleak = False + with save_env(ns, result.test_name): + regrtest_runner(result, test_func, ns) finally: # First kill any dangling references to open files etc. # This can also issue some ResourceWarnings which would otherwise get @@ -318,11 +357,11 @@ def _runtest_inner2(ns: Namespace, test_name: str) -> bool: # failures. support.gc_collect() - cleanup_test_droppings(test_name, ns.verbose) + cleanup_test_droppings(result.test_name, ns.verbose) if gc.garbage: support.environment_altered = True - print_warning(f"{test_name} created {len(gc.garbage)} " + print_warning(f"{result.test_name} created {len(gc.garbage)} " f"uncollectable object(s).") # move the uncollectable objects somewhere, @@ -332,12 +371,9 @@ def _runtest_inner2(ns: Namespace, test_name: str) -> bool: support.reap_children() - return refleak - -def _runtest_inner( - ns: Namespace, test_name: str, display_failure: bool = True -) -> TestResult: +def _runtest_env_changed_exc(result: TestResult, ns: Namespace, + display_failure: bool = True) -> None: # Detect environment changes, handle exceptions. # Reset the environment_altered flag to detect if a test altered @@ -347,49 +383,61 @@ def _runtest_inner( if ns.pgo: display_failure = False + test_name = result.test_name try: clear_caches() support.gc_collect() with save_env(ns, test_name): - refleak = _runtest_inner2(ns, test_name) + _load_run_test(result, ns) except support.ResourceDenied as msg: if not ns.quiet and not ns.pgo: print(f"{test_name} skipped -- {msg}", flush=True) - return ResourceDenied(test_name) + result.state = State.RESOURCE_DENIED + return except unittest.SkipTest as msg: if not ns.quiet and not ns.pgo: print(f"{test_name} skipped -- {msg}", flush=True) - return Skipped(test_name) + result.state = State.SKIPPED + return except support.TestFailedWithDetails as exc: msg = f"test {test_name} failed" if display_failure: msg = f"{msg} -- {exc}" print(msg, file=sys.stderr, flush=True) - return Failed(test_name, errors=exc.errors, failures=exc.failures) + result.state = State.FAILED + result.errors = exc.errors + result.failures = exc.failures + result.stats = exc.stats + return except support.TestFailed as exc: msg = f"test {test_name} failed" if display_failure: msg = f"{msg} -- {exc}" print(msg, file=sys.stderr, flush=True) - return Failed(test_name) + result.state = State.FAILED + result.stats = exc.stats + return except support.TestDidNotRun: - return DidNotRun(test_name) + result.state = State.DID_NOT_RUN + return except KeyboardInterrupt: print() - return Interrupted(test_name) + result.state = State.INTERRUPTED + return except: if not ns.pgo: msg = traceback.format_exc() print(f"test {test_name} crashed -- {msg}", file=sys.stderr, flush=True) - return UncaughtException(test_name) + result.state = State.UNCAUGHT_EXC + return - if refleak: - return RefLeak(test_name) if support.environment_altered: - return EnvChanged(test_name) - return Passed(test_name) + result.set_env_changed() + # Don't override the state if it was already set (REFLEAK or ENV_CHANGED) + if result.state is None: + result.state = State.PASSED def cleanup_test_droppings(test_name: str, verbose: int) -> None: diff --git a/Lib/test/libregrtest/runtest_mp.py b/Lib/test/libregrtest/runtest_mp.py index 8587e64e5153e6..fb1f80b0c054e3 100644 --- a/Lib/test/libregrtest/runtest_mp.py +++ b/Lib/test/libregrtest/runtest_mp.py @@ -1,6 +1,7 @@ +import dataclasses import faulthandler import json -import os +import os.path import queue import signal import subprocess @@ -13,11 +14,13 @@ from test import support from test.support import os_helper +from test.support import TestStats from test.libregrtest.cmdline import Namespace from test.libregrtest.main import Regrtest from test.libregrtest.runtest import ( - runtest, is_failed, TestResult, Interrupted, Timeout, ChildError, PROGRESS_MIN_TIME) + runtest, TestResult, State, + PROGRESS_MIN_TIME) from test.libregrtest.setup import setup_tests from test.libregrtest.utils import format_duration, print_warning @@ -42,9 +45,9 @@ def must_stop(result: TestResult, ns: Namespace) -> bool: - if isinstance(result, Interrupted): + if result.state == State.INTERRUPTED: return True - if ns.failfast and is_failed(result, ns): + if ns.failfast and result.is_failed(ns.fail_env_changed): return True return False @@ -55,7 +58,7 @@ def parse_worker_args(worker_args) -> tuple[Namespace, str]: return (ns, test_name) -def run_test_in_subprocess(testname: str, ns: Namespace, stdout_fh: TextIO) -> subprocess.Popen: +def run_test_in_subprocess(testname: str, ns: Namespace, tmp_dir: str, stdout_fh: TextIO) -> subprocess.Popen: ns_dict = vars(ns) worker_args = (ns_dict, testname) worker_args = json.dumps(worker_args) @@ -68,10 +71,17 @@ def run_test_in_subprocess(testname: str, ns: Namespace, stdout_fh: TextIO) -> s '-m', 'test.regrtest', '--worker-args', worker_args] + env = dict(os.environ) + if tmp_dir is not None: + env['TMPDIR'] = tmp_dir + env['TEMP'] = tmp_dir + env['TMP'] = tmp_dir + # Running the child from the same working directory as regrtest's original # invocation ensures that TEMPDIR for the child is the same when # sysconfig.is_python_build() is true. See issue 15300. kw = dict( + env=env, stdout=stdout_fh, # bpo-45410: Write stderr into stdout to keep messages order stderr=stdout_fh, @@ -122,8 +132,8 @@ def stop(self): class MultiprocessResult(NamedTuple): result: TestResult # bpo-45410: stderr is written into stdout to keep messages order - stdout: str - error_msg: str + worker_stdout: str | None = None + err_msg: str | None = None ExcStr = str @@ -201,18 +211,15 @@ def stop(self) -> None: def mp_result_error( self, test_result: TestResult, - stdout: str = '', + stdout: str | None = None, err_msg=None ) -> MultiprocessResult: - test_result.duration_sec = time.monotonic() - self.start_time return MultiprocessResult(test_result, stdout, err_msg) - def _run_process(self, test_name: str, stdout_fh: TextIO) -> int: - self.start_time = time.monotonic() - + def _run_process(self, test_name: str, tmp_dir: str, stdout_fh: TextIO) -> int: self.current_test_name = test_name try: - popen = run_test_in_subprocess(test_name, self.ns, stdout_fh) + popen = run_test_in_subprocess(test_name, self.ns, tmp_dir, stdout_fh) self._killed = False self._popen = popen @@ -276,7 +283,20 @@ def _runtest(self, test_name: str) -> MultiprocessResult: # gh-93353: Check for leaked temporary files in the parent process, # since the deletion of temporary files can happen late during # Python finalization: too late for libregrtest. - retcode = self._run_process(test_name, stdout_fh) + if not support.is_wasi: + # Don't check for leaked temporary files and directories if Python is + # run on WASI. WASI don't pass environment variables like TMPDIR to + # worker processes. + tmp_dir = tempfile.mkdtemp(prefix="test_python_") + tmp_dir = os.path.abspath(tmp_dir) + try: + retcode = self._run_process(test_name, tmp_dir, stdout_fh) + finally: + tmp_files = os.listdir(tmp_dir) + os_helper.rmtree(tmp_dir) + else: + retcode = self._run_process(test_name, None, stdout_fh) + tmp_files = () stdout_fh.seek(0) try: @@ -285,30 +305,41 @@ def _runtest(self, test_name: str) -> MultiprocessResult: # gh-101634: Catch UnicodeDecodeError if stdout cannot be # decoded from encoding err_msg = f"Cannot read process stdout: {exc}" - return self.mp_result_error(ChildError(test_name), '', err_msg) + result = TestResult(test_name, state=State.MULTIPROCESSING_ERROR) + return self.mp_result_error(result, err_msg=err_msg) if retcode is None: - return self.mp_result_error(Timeout(test_name), stdout) + result = TestResult(test_name, state=State.TIMEOUT) + return self.mp_result_error(result, stdout) err_msg = None if retcode != 0: err_msg = "Exit code %s" % retcode else: - stdout, _, result = stdout.rpartition("\n") + stdout, _, worker_json = stdout.rpartition("\n") stdout = stdout.rstrip() - if not result: + if not worker_json: err_msg = "Failed to parse worker stdout" else: try: # deserialize run_tests_worker() output - result = json.loads(result, object_hook=decode_test_result) + result = json.loads(worker_json, + object_hook=decode_test_result) except Exception as exc: err_msg = "Failed to parse worker JSON: %s" % exc - if err_msg is not None: - return self.mp_result_error(ChildError(test_name), stdout, err_msg) + if err_msg: + result = TestResult(test_name, state=State.MULTIPROCESSING_ERROR) + return self.mp_result_error(result, stdout, err_msg) + + if tmp_files: + msg = (f'\n\n' + f'Warning -- {test_name} leaked temporary files ' + f'({len(tmp_files)}): {", ".join(sorted(tmp_files))}') + stdout += msg + result.set_env_changed() - return MultiprocessResult(result, stdout, err_msg) + return MultiprocessResult(result, stdout) def run(self) -> None: while not self._stopped: @@ -318,7 +349,9 @@ def run(self) -> None: except StopIteration: break + self.start_time = time.monotonic() mp_result = self._runtest(test_name) + mp_result.result.duration = time.monotonic() - self.start_time self.output.put((False, mp_result)) if must_stop(mp_result.result, self.ns): @@ -444,11 +477,11 @@ def display_result(self, mp_result: MultiprocessResult) -> None: result = mp_result.result text = str(result) - if mp_result.error_msg is not None: - # CHILD_ERROR - text += ' (%s)' % mp_result.error_msg - elif (result.duration_sec >= PROGRESS_MIN_TIME and not self.ns.pgo): - text += ' (%s)' % format_duration(result.duration_sec) + if mp_result.err_msg: + # MULTIPROCESSING_ERROR + text += ' (%s)' % mp_result.err_msg + elif (result.duration >= PROGRESS_MIN_TIME and not self.ns.pgo): + text += ' (%s)' % format_duration(result.duration) running = get_running(self.workers) if running and not self.ns.pgo: text += ' -- running: %s' % ', '.join(running) @@ -460,7 +493,7 @@ def _process_result(self, item: QueueOutput) -> bool: # Thread got an exception format_exc = item[1] print_warning(f"regrtest worker thread failed: {format_exc}") - result = ChildError("") + result = TestResult("", state=State.MULTIPROCESSING_ERROR) self.regrtest.accumulate_result(result) return True @@ -469,8 +502,8 @@ def _process_result(self, item: QueueOutput) -> bool: self.regrtest.accumulate_result(mp_result.result) self.display_result(mp_result) - if mp_result.stdout: - print(mp_result.stdout, flush=True) + if mp_result.worker_stdout: + print(mp_result.worker_stdout, flush=True) if must_stop(mp_result.result, self.ns): return True @@ -512,32 +545,20 @@ class EncodeTestResult(json.JSONEncoder): def default(self, o: Any) -> dict[str, Any]: if isinstance(o, TestResult): - result = vars(o) + result = dataclasses.asdict(o) result["__test_result__"] = o.__class__.__name__ return result return super().default(o) -def decode_test_result(d: dict[str, Any]) -> TestResult | dict[str, Any]: +def decode_test_result(d: dict[str, Any]) -> TestResult | TestStats | dict[str, Any]: """Decode a TestResult (sub)class object from a JSON dict.""" if "__test_result__" not in d: return d - cls_name = d.pop("__test_result__") - for cls in get_all_test_result_classes(): - if cls.__name__ == cls_name: - return cls(**d) - - -def get_all_test_result_classes() -> set[type[TestResult]]: - prev_count = 0 - classes = {TestResult} - while len(classes) > prev_count: - prev_count = len(classes) - to_add = [] - for cls in classes: - to_add.extend(cls.__subclasses__()) - classes.update(to_add) - return classes + d.pop('__test_result__') + if d['stats'] is not None: + d['stats'] = TestStats(**d['stats']) + return TestResult(**d) diff --git a/Lib/test/libregrtest/save_env.py b/Lib/test/libregrtest/save_env.py index 60c9be24617a65..7e801a591325e1 100644 --- a/Lib/test/libregrtest/save_env.py +++ b/Lib/test/libregrtest/save_env.py @@ -23,7 +23,7 @@ class SkipTestEnvironment(Exception): class saved_test_environment: """Save bits of the test environment and restore them at block exit. - with saved_test_environment(testname, verbose, quiet): + with saved_test_environment(test_name, verbose, quiet): #stuff Unless quiet is True, a warning is printed to stderr if any of @@ -34,8 +34,8 @@ class saved_test_environment: items is also printed. """ - def __init__(self, testname, verbose=0, quiet=False, *, pgo=False): - self.testname = testname + def __init__(self, test_name, verbose=0, quiet=False, *, pgo=False): + self.test_name = test_name self.verbose = verbose self.quiet = quiet self.pgo = pgo @@ -321,7 +321,7 @@ def __exit__(self, exc_type, exc_val, exc_tb): restore(original) if not self.quiet and not self.pgo: print_warning( - f"{name} was modified by {self.testname}\n" + f"{name} was modified by {self.test_name}\n" f" Before: {original}\n" f" After: {current} ") return False diff --git a/Lib/test/libregrtest/utils.py b/Lib/test/libregrtest/utils.py index 8578a028c78bc2..bb64f997d3500a 100644 --- a/Lib/test/libregrtest/utils.py +++ b/Lib/test/libregrtest/utils.py @@ -1,6 +1,7 @@ import math import os.path import sys +import sysconfig import textwrap from test import support @@ -210,3 +211,99 @@ def clear_caches(): else: for f in typing._cleanups: f() + + try: + fractions = sys.modules['fractions'] + except KeyError: + pass + else: + fractions._hash_algorithm.cache_clear() + + +def get_build_info(): + # Get most important configure and build options as a list of strings. + # Example: ['debug', 'ASAN+MSAN'] or ['release', 'LTO+PGO']. + + config_args = sysconfig.get_config_var('CONFIG_ARGS') or '' + cflags = sysconfig.get_config_var('PY_CFLAGS') or '' + cflags_nodist = sysconfig.get_config_var('PY_CFLAGS_NODIST') or '' + ldflags_nodist = sysconfig.get_config_var('PY_LDFLAGS_NODIST') or '' + + build = [] + + # --disable-gil + if sysconfig.get_config_var('Py_NOGIL'): + build.append("nogil") + + if hasattr(sys, 'gettotalrefcount'): + # --with-pydebug + build.append('debug') + + if '-DNDEBUG' in (cflags + cflags_nodist): + build.append('without_assert') + else: + build.append('release') + + if '--with-assertions' in config_args: + build.append('with_assert') + elif '-DNDEBUG' not in (cflags + cflags_nodist): + build.append('with_assert') + + # --enable-framework=name + framework = sysconfig.get_config_var('PYTHONFRAMEWORK') + if framework: + build.append(f'framework={framework}') + + # --enable-shared + shared = int(sysconfig.get_config_var('PY_ENABLE_SHARED') or '0') + if shared: + build.append('shared') + + # --with-lto + optimizations = [] + if '-flto=thin' in ldflags_nodist: + optimizations.append('ThinLTO') + elif '-flto' in ldflags_nodist: + optimizations.append('LTO') + + # --enable-optimizations + pgo_options = ( + # GCC + '-fprofile-use', + # clang: -fprofile-instr-use=code.profclangd + '-fprofile-instr-use', + # ICC + "-prof-use", + ) + if any(option in cflags_nodist for option in pgo_options): + optimizations.append('PGO') + if optimizations: + build.append('+'.join(optimizations)) + + # --with-address-sanitizer + sanitizers = [] + if support.check_sanitizer(address=True): + sanitizers.append("ASAN") + # --with-memory-sanitizer + if support.check_sanitizer(memory=True): + sanitizers.append("MSAN") + # --with-undefined-behavior-sanitizer + if support.check_sanitizer(ub=True): + sanitizers.append("UBSAN") + if sanitizers: + build.append('+'.join(sanitizers)) + + # --with-trace-refs + if hasattr(sys, 'getobjects'): + build.append("TraceRefs") + # --enable-pystats + if hasattr(sys, '_stats_on'): + build.append("pystats") + # --with-valgrind + if sysconfig.get_config_var('WITH_VALGRIND'): + build.append("valgrind") + # --with-dtrace + if sysconfig.get_config_var('WITH_DTRACE'): + build.append("dtrace") + + return build diff --git a/Lib/test/pythoninfo.py b/Lib/test/pythoninfo.py index 61fd734d4c605d..3240e2e3bca80d 100644 --- a/Lib/test/pythoninfo.py +++ b/Lib/test/pythoninfo.py @@ -309,6 +309,13 @@ def format_groups(groups): "_PYTHON_PROJECT_BASE", "_PYTHON_SYSCONFIGDATA_NAME", "__PYVENV_LAUNCHER__", + + # Sanitizer options + "ASAN_OPTIONS", + "LSAN_OPTIONS", + "MSAN_OPTIONS", + "TSAN_OPTIONS", + "UBSAN_OPTIONS", )) for name, value in os.environ.items(): uname = name.upper() @@ -493,6 +500,7 @@ def collect_sysconfig(info_add): 'PY_STDMODULE_CFLAGS', 'Py_DEBUG', 'Py_ENABLE_SHARED', + 'Py_NOGIL', 'SHELL', 'SOABI', 'prefix', diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 98be9cdd0e1652..94967f2c302a8c 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -4,6 +4,7 @@ raise ImportError('support must be imported from the test package') import contextlib +import dataclasses import functools import getpass import os @@ -116,17 +117,20 @@ class Error(Exception): class TestFailed(Error): """Test failed.""" + def __init__(self, msg, *args, stats=None): + self.msg = msg + self.stats = stats + super().__init__(msg, *args) + + def __str__(self): + return self.msg class TestFailedWithDetails(TestFailed): """Test failed.""" - def __init__(self, msg, errors, failures): - self.msg = msg + def __init__(self, msg, errors, failures, stats): self.errors = errors self.failures = failures - super().__init__(msg, errors, failures) - - def __str__(self): - return self.msg + super().__init__(msg, errors, failures, stats=stats) class TestDidNotRun(Error): """Test did not run any subtests.""" @@ -400,19 +404,19 @@ def check_sanitizer(*, address=False, memory=False, ub=False): raise ValueError('At least one of address, memory, or ub must be True') - _cflags = sysconfig.get_config_var('CFLAGS') or '' - _config_args = sysconfig.get_config_var('CONFIG_ARGS') or '' + cflags = sysconfig.get_config_var('CFLAGS') or '' + config_args = sysconfig.get_config_var('CONFIG_ARGS') or '' memory_sanitizer = ( - '-fsanitize=memory' in _cflags or - '--with-memory-sanitizer' in _config_args + '-fsanitize=memory' in cflags or + '--with-memory-sanitizer' in config_args ) address_sanitizer = ( - '-fsanitize=address' in _cflags or - '--with-memory-sanitizer' in _config_args + '-fsanitize=address' in cflags or + '--with-address-sanitizer' in config_args ) ub_sanitizer = ( - '-fsanitize=undefined' in _cflags or - '--with-undefined-behavior-sanitizer' in _config_args + '-fsanitize=undefined' in cflags or + '--with-undefined-behavior-sanitizer' in config_args ) return ( (memory and memory_sanitizer) or @@ -1086,6 +1090,29 @@ def _filter_suite(suite, pred): newtests.append(test) suite._tests = newtests +@dataclasses.dataclass(slots=True) +class TestStats: + tests_run: int = 0 + failures: int = 0 + skipped: int = 0 + + @staticmethod + def from_unittest(result): + return TestStats(result.testsRun, + len(result.failures), + len(result.skipped)) + + @staticmethod + def from_doctest(results): + return TestStats(results.attempted, + results.failed) + + def accumulate(self, stats): + self.tests_run += stats.tests_run + self.failures += stats.failures + self.skipped += stats.skipped + + def _run_suite(suite): """Run tests from a unittest.TestSuite-derived class.""" runner = get_test_runner(sys.stdout, @@ -1100,6 +1127,7 @@ def _run_suite(suite): if not result.testsRun and not result.skipped and not result.errors: raise TestDidNotRun if not result.wasSuccessful(): + stats = TestStats.from_unittest(result) if len(result.errors) == 1 and not result.failures: err = result.errors[0][1] elif len(result.failures) == 1 and not result.errors: @@ -1109,7 +1137,8 @@ def _run_suite(suite): if not verbose: err += "; run in verbose mode for details" errors = [(str(tc), exc_str) for tc, exc_str in result.errors] failures = [(str(tc), exc_str) for tc, exc_str in result.failures] - raise TestFailedWithDetails(err, errors, failures) + raise TestFailedWithDetails(err, errors, failures, stats=stats) + return result # By default, don't filter tests @@ -1218,7 +1247,7 @@ def run_unittest(*classes): else: suite.addTest(loader.loadTestsFromTestCase(cls)) _filter_suite(suite, match_test) - _run_suite(suite) + return _run_suite(suite) #======================================================================= # Check for the presence of docstrings. @@ -1258,13 +1287,18 @@ def run_doctest(module, verbosity=None, optionflags=0): else: verbosity = None - f, t = doctest.testmod(module, verbose=verbosity, optionflags=optionflags) - if f: - raise TestFailed("%d of %d doctests failed" % (f, t)) + results = doctest.testmod(module, + verbose=verbosity, + optionflags=optionflags) + if results.failed: + stats = TestStats.from_doctest(results) + raise TestFailed(f"{results.failed} of {results.attempted} " + f"doctests failed", + stats=stats) if verbose: print('doctest (%s) ... %d tests with zero failures' % - (module.__name__, t)) - return f, t + (module.__name__, results.attempted)) + return results #======================================================================= diff --git a/Lib/test/test_netrc.py b/Lib/test/test_netrc.py index 573d636de956d1..b38cb327f68ecc 100644 --- a/Lib/test/test_netrc.py +++ b/Lib/test/test_netrc.py @@ -309,7 +309,7 @@ def test_security(self): ('anonymous', '', 'pass')) def test_main(): - run_unittest(NetrcTestCase) + return run_unittest(NetrcTestCase) if __name__ == "__main__": test_main() diff --git a/Lib/test/test_pep646_syntax.py b/Lib/test/test_pep646_syntax.py index 3ffa82dc55fa23..12a4227e4dc959 100644 --- a/Lib/test/test_pep646_syntax.py +++ b/Lib/test/test_pep646_syntax.py @@ -320,7 +320,7 @@ def test_main(verbose=False): from test import support from test import test_pep646_syntax - support.run_doctest(test_pep646_syntax, verbose) + return support.run_doctest(test_pep646_syntax, verbose) if __name__ == "__main__": test_main(verbose=True) diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py index 78552590a7289c..8b7599bcb931fe 100644 --- a/Lib/test/test_regrtest.py +++ b/Lib/test/test_regrtest.py @@ -20,7 +20,7 @@ import unittest from test import libregrtest from test import support -from test.support import os_helper +from test.support import os_helper, TestStats from test.libregrtest import utils, setup if not support.has_subprocess_support: @@ -411,7 +411,9 @@ def regex_search(self, regex, output): self.fail("%r not found in %r" % (regex, output)) return match - def check_line(self, output, regex): + def check_line(self, output, regex, full=False): + if full: + regex += '\n' regex = re.compile(r'^' + regex, re.MULTILINE) self.assertRegex(output, regex) @@ -423,21 +425,27 @@ def parse_executed_tests(self, output): def check_executed_tests(self, output, tests, skipped=(), failed=(), env_changed=(), omitted=(), - rerun={}, no_test_ran=(), + rerun={}, run_no_tests=(), + resource_denied=(), randomize=False, interrupted=False, - fail_env_changed=False): + fail_env_changed=False, + *, stats): if isinstance(tests, str): tests = [tests] if isinstance(skipped, str): skipped = [skipped] + if isinstance(resource_denied, str): + resource_denied = [resource_denied] if isinstance(failed, str): failed = [failed] if isinstance(env_changed, str): env_changed = [env_changed] if isinstance(omitted, str): omitted = [omitted] - if isinstance(no_test_ran, str): - no_test_ran = [no_test_ran] + if isinstance(run_no_tests, str): + run_no_tests = [run_no_tests] + if isinstance(stats, int): + stats = TestStats(stats) executed = self.parse_executed_tests(output) if randomize: @@ -481,12 +489,12 @@ def list_regex(line_format, tests): regex = LOG_PREFIX + f"Re-running {name} in verbose mode \\(matching: {match}\\)" self.check_line(output, regex) - if no_test_ran: - regex = list_regex('%s test%s run no tests', no_test_ran) + if run_no_tests: + regex = list_regex('%s test%s run no tests', run_no_tests) self.check_line(output, regex) good = (len(tests) - len(skipped) - len(failed) - - len(omitted) - len(env_changed) - len(no_test_ran)) + - len(omitted) - len(env_changed) - len(run_no_tests)) if good: regex = r'%s test%s OK\.$' % (good, plural(good)) if not skipped and not failed and good > 1: @@ -496,6 +504,33 @@ def list_regex(line_format, tests): if interrupted: self.check_line(output, 'Test suite interrupted by signal SIGINT.') + # Total tests + parts = [f'run={stats.tests_run:,}'] + if stats.failures: + parts.append(f'failures={stats.failures:,}') + if stats.skipped: + parts.append(f'skipped={stats.skipped:,}') + line = fr'Total tests: {" ".join(parts)}' + self.check_line(output, line, full=True) + + # Total test files + report = [f'success={good}'] + if failed: + report.append(f'failed={len(failed)}') + if env_changed: + report.append(f'env_changed={len(env_changed)}') + if skipped: + report.append(f'skipped={len(skipped)}') + if resource_denied: + report.append(f'resource_denied={len(resource_denied)}') + if rerun: + report.append(f'rerun={len(rerun)}') + if run_no_tests: + report.append(f'run_no_tests={len(run_no_tests)}') + line = fr'Total test files: {" ".join(report)}' + self.check_line(output, line, full=True) + + # Result result = [] if failed: result.append('FAILURE') @@ -505,15 +540,13 @@ def list_regex(line_format, tests): result.append('INTERRUPTED') if not any((good, result, failed, interrupted, skipped, env_changed, fail_env_changed)): - result.append("NO TEST RUN") + result.append("NO TESTS RAN") elif not result: result.append('SUCCESS') result = ', '.join(result) if rerun: - self.check_line(output, 'Tests result: FAILURE') result = 'FAILURE then %s' % result - - self.check_line(output, 'Tests result: %s' % result) + self.check_line(output, f'Result: {result}', full=True) def parse_random_seed(self, output): match = self.regex_search(r'Using random seed ([0-9]+)', output) @@ -602,7 +635,8 @@ def setUp(self): def check_output(self, output): self.parse_random_seed(output) - self.check_executed_tests(output, self.tests, randomize=True) + self.check_executed_tests(output, self.tests, + randomize=True, stats=len(self.tests)) def run_tests(self, args): output = self.run_python(args) @@ -715,8 +749,9 @@ def test_failing(self): test_failing = self.create_test('failing', code=code) tests = [test_ok, test_failing] - output = self.run_tests(*tests, exitcode=2) - self.check_executed_tests(output, tests, failed=test_failing) + output = self.run_tests(*tests, exitcode=EXITCODE_BAD_TEST) + self.check_executed_tests(output, tests, failed=test_failing, + stats=TestStats(2, 1)) def test_resources(self): # test -u command line option @@ -735,17 +770,21 @@ def test_pass(self): # -u all: 2 resources enabled output = self.run_tests('-u', 'all', *test_names) - self.check_executed_tests(output, test_names) + self.check_executed_tests(output, test_names, stats=2) # -u audio: 1 resource enabled output = self.run_tests('-uaudio', *test_names) self.check_executed_tests(output, test_names, - skipped=tests['network']) + skipped=tests['network'], + resource_denied=tests['network'], + stats=1) # no option: 0 resources enabled output = self.run_tests(*test_names) self.check_executed_tests(output, test_names, - skipped=test_names) + skipped=test_names, + resource_denied=test_names, + stats=0) def test_random(self): # test -r and --randseed command line option @@ -756,13 +795,14 @@ def test_random(self): test = self.create_test('random', code) # first run to get the output with the random seed - output = self.run_tests('-r', test) + output = self.run_tests('-r', test, exitcode=EXITCODE_NO_TESTS_RAN) randseed = self.parse_random_seed(output) match = self.regex_search(r'TESTRANDOM: ([0-9]+)', output) test_random = int(match.group(1)) # try to reproduce with the random seed - output = self.run_tests('-r', '--randseed=%s' % randseed, test) + output = self.run_tests('-r', '--randseed=%s' % randseed, test, + exitcode=EXITCODE_NO_TESTS_RAN) randseed2 = self.parse_random_seed(output) self.assertEqual(randseed2, randseed) @@ -792,7 +832,8 @@ def test_fromfile(self): previous = name output = self.run_tests('--fromfile', filename) - self.check_executed_tests(output, tests) + stats = len(tests) + self.check_executed_tests(output, tests, stats=stats) # test format '[2/7] test_opcodes' with open(filename, "w") as fp: @@ -800,7 +841,7 @@ def test_fromfile(self): print("[%s/%s] %s" % (index, len(tests), name), file=fp) output = self.run_tests('--fromfile', filename) - self.check_executed_tests(output, tests) + self.check_executed_tests(output, tests, stats=stats) # test format 'test_opcodes' with open(filename, "w") as fp: @@ -808,7 +849,7 @@ def test_fromfile(self): print(name, file=fp) output = self.run_tests('--fromfile', filename) - self.check_executed_tests(output, tests) + self.check_executed_tests(output, tests, stats=stats) # test format 'Lib/test/test_opcodes.py' with open(filename, "w") as fp: @@ -816,20 +857,20 @@ def test_fromfile(self): print('Lib/test/%s.py' % name, file=fp) output = self.run_tests('--fromfile', filename) - self.check_executed_tests(output, tests) + self.check_executed_tests(output, tests, stats=stats) def test_interrupted(self): code = TEST_INTERRUPTED test = self.create_test('sigint', code=code) - output = self.run_tests(test, exitcode=130) + output = self.run_tests(test, exitcode=EXITCODE_INTERRUPTED) self.check_executed_tests(output, test, omitted=test, - interrupted=True) + interrupted=True, stats=0) def test_slowest(self): # test --slowest tests = [self.create_test() for index in range(3)] output = self.run_tests("--slowest", *tests) - self.check_executed_tests(output, tests) + self.check_executed_tests(output, tests, stats=len(tests)) regex = ('10 slowest tests:\n' '(?:- %s: .*\n){%s}' % (self.TESTNAME_REGEX, len(tests))) @@ -846,9 +887,10 @@ def test_slowest_interrupted(self): args = ("--slowest", "-j2", test) else: args = ("--slowest", test) - output = self.run_tests(*args, exitcode=130) + output = self.run_tests(*args, exitcode=EXITCODE_INTERRUPTED) self.check_executed_tests(output, test, - omitted=test, interrupted=True) + omitted=test, interrupted=True, + stats=0) regex = ('10 slowest tests:\n') self.check_line(output, regex) @@ -857,7 +899,7 @@ def test_coverage(self): # test --coverage test = self.create_test('coverage') output = self.run_tests("--coverage", test) - self.check_executed_tests(output, [test]) + self.check_executed_tests(output, [test], stats=1) regex = (r'lines +cov% +module +\(path\)\n' r'(?: *[0-9]+ *[0-9]{1,2}% *[^ ]+ +\([^)]+\)+)+') self.check_line(output, regex) @@ -886,8 +928,9 @@ def test_run(self): builtins.__dict__['RUN'] = 1 """) test = self.create_test('forever', code=code) - output = self.run_tests('--forever', test, exitcode=2) - self.check_executed_tests(output, [test]*3, failed=test) + output = self.run_tests('--forever', test, exitcode=EXITCODE_BAD_TEST) + self.check_executed_tests(output, [test]*3, failed=test, + stats=TestStats(1, 1)) def check_leak(self, code, what): test = self.create_test('huntrleaks', code=code) @@ -895,9 +938,9 @@ def check_leak(self, code, what): filename = 'reflog.txt' self.addCleanup(os_helper.unlink, filename) output = self.run_tests('--huntrleaks', '3:3:', test, - exitcode=2, + exitcode=EXITCODE_BAD_TEST, stderr=subprocess.STDOUT) - self.check_executed_tests(output, [test], failed=test) + self.check_executed_tests(output, [test], failed=test, stats=1) line = 'beginning 6 repetitions\n123456\n......\n' self.check_line(output, re.escape(line)) @@ -977,9 +1020,9 @@ def test_crashed(self): crash_test = self.create_test(name="crash", code=code) tests = [crash_test] - output = self.run_tests("-j2", *tests, exitcode=2) + output = self.run_tests("-j2", *tests, exitcode=EXITCODE_BAD_TEST) self.check_executed_tests(output, tests, failed=crash_test, - randomize=True) + randomize=True, stats=0) def parse_methods(self, output): regex = re.compile("^(test[^ ]+).*ok$", flags=re.MULTILINE) @@ -1074,12 +1117,14 @@ def test_env_changed(self): # don't fail by default output = self.run_tests(testname) - self.check_executed_tests(output, [testname], env_changed=testname) + self.check_executed_tests(output, [testname], + env_changed=testname, stats=1) # fail with --fail-env-changed - output = self.run_tests("--fail-env-changed", testname, exitcode=3) + output = self.run_tests("--fail-env-changed", testname, + exitcode=EXITCODE_ENV_CHANGED) self.check_executed_tests(output, [testname], env_changed=testname, - fail_env_changed=True) + fail_env_changed=True, stats=1) def test_rerun_fail(self): # FAILURE then FAILURE @@ -1096,9 +1141,11 @@ def test_fail_always(self): """) testname = self.create_test(code=code) - output = self.run_tests("-w", testname, exitcode=2) + output = self.run_tests("-w", testname, exitcode=EXITCODE_BAD_TEST) self.check_executed_tests(output, [testname], - failed=testname, rerun={testname: "test_fail_always"}) + failed=testname, + rerun={testname: "test_fail_always"}, + stats=TestStats(1, 1)) def test_rerun_success(self): # FAILURE then SUCCESS @@ -1119,7 +1166,8 @@ def test_fail_once(self): output = self.run_tests("-w", testname, exitcode=0) self.check_executed_tests(output, [testname], - rerun={testname: "test_fail_once"}) + rerun={testname: "test_fail_once"}, + stats=1) def test_rerun_setup_class_hook_failure(self): # FAILURE then FAILURE @@ -1139,7 +1187,8 @@ def test_success(self): output = self.run_tests("-w", testname, exitcode=EXITCODE_BAD_TEST) self.check_executed_tests(output, testname, failed=[testname], - rerun={testname: "ExampleTests"}) + rerun={testname: "ExampleTests"}, + stats=0) def test_rerun_teardown_class_hook_failure(self): # FAILURE then FAILURE @@ -1159,7 +1208,8 @@ def test_success(self): output = self.run_tests("-w", testname, exitcode=EXITCODE_BAD_TEST) self.check_executed_tests(output, testname, failed=[testname], - rerun={testname: "ExampleTests"}) + rerun={testname: "ExampleTests"}, + stats=1) def test_rerun_setup_module_hook_failure(self): # FAILURE then FAILURE @@ -1178,7 +1228,8 @@ def test_success(self): output = self.run_tests("-w", testname, exitcode=EXITCODE_BAD_TEST) self.check_executed_tests(output, testname, failed=[testname], - rerun={testname: testname}) + rerun={testname: testname}, + stats=0) def test_rerun_teardown_module_hook_failure(self): # FAILURE then FAILURE @@ -1197,7 +1248,8 @@ def test_success(self): output = self.run_tests("-w", testname, exitcode=EXITCODE_BAD_TEST) self.check_executed_tests(output, testname, failed=[testname], - rerun={testname: testname}) + rerun={testname: testname}, + stats=1) def test_rerun_setup_hook_failure(self): # FAILURE then FAILURE @@ -1216,7 +1268,8 @@ def test_success(self): output = self.run_tests("-w", testname, exitcode=EXITCODE_BAD_TEST) self.check_executed_tests(output, testname, failed=[testname], - rerun={testname: "test_success"}) + rerun={testname: "test_success"}, + stats=1) def test_rerun_teardown_hook_failure(self): # FAILURE then FAILURE @@ -1235,7 +1288,8 @@ def test_success(self): output = self.run_tests("-w", testname, exitcode=EXITCODE_BAD_TEST) self.check_executed_tests(output, testname, failed=[testname], - rerun={testname: "test_success"}) + rerun={testname: "test_success"}, + stats=1) def test_rerun_async_setup_hook_failure(self): # FAILURE then FAILURE @@ -1254,7 +1308,8 @@ async def test_success(self): output = self.run_tests("-w", testname, exitcode=EXITCODE_BAD_TEST) self.check_executed_tests(output, testname, failed=[testname], - rerun={testname: "test_success"}) + rerun={testname: "test_success"}, + stats=1) def test_rerun_async_teardown_hook_failure(self): # FAILURE then FAILURE @@ -1273,7 +1328,8 @@ async def test_success(self): output = self.run_tests("-w", testname, exitcode=EXITCODE_BAD_TEST) self.check_executed_tests(output, testname, failed=[testname], - rerun={testname: "test_success"}) + rerun={testname: "test_success"}, + stats=1) def test_no_tests_ran(self): code = textwrap.dedent(""" @@ -1285,8 +1341,11 @@ def test_bug(self): """) testname = self.create_test(code=code) - output = self.run_tests(testname, "-m", "nosuchtest", exitcode=0) - self.check_executed_tests(output, [testname], no_test_ran=testname) + output = self.run_tests(testname, "-m", "nosuchtest", + exitcode=EXITCODE_NO_TESTS_RAN) + self.check_executed_tests(output, [testname], + run_no_tests=testname, + stats=0) def test_no_tests_ran_skip(self): code = textwrap.dedent(""" @@ -1298,8 +1357,9 @@ def test_skipped(self): """) testname = self.create_test(code=code) - output = self.run_tests(testname, exitcode=0) - self.check_executed_tests(output, [testname]) + output = self.run_tests(testname) + self.check_executed_tests(output, [testname], + stats=TestStats(1, skipped=1)) def test_no_tests_ran_multiple_tests_nonexistent(self): code = textwrap.dedent(""" @@ -1312,9 +1372,11 @@ def test_bug(self): testname = self.create_test(code=code) testname2 = self.create_test(code=code) - output = self.run_tests(testname, testname2, "-m", "nosuchtest", exitcode=0) + output = self.run_tests(testname, testname2, "-m", "nosuchtest", + exitcode=EXITCODE_NO_TESTS_RAN) self.check_executed_tests(output, [testname, testname2], - no_test_ran=[testname, testname2]) + run_no_tests=[testname, testname2], + stats=0) def test_no_test_ran_some_test_exist_some_not(self): code = textwrap.dedent(""" @@ -1337,7 +1399,8 @@ def test_other_bug(self): output = self.run_tests(testname, testname2, "-m", "nosuchtest", "-m", "test_other_bug", exitcode=0) self.check_executed_tests(output, [testname, testname2], - no_test_ran=[testname]) + run_no_tests=[testname], + stats=1) @support.cpython_only def test_uncollectable(self): @@ -1360,10 +1423,12 @@ def test_garbage(self): """) testname = self.create_test(code=code) - output = self.run_tests("--fail-env-changed", testname, exitcode=3) + output = self.run_tests("--fail-env-changed", testname, + exitcode=EXITCODE_ENV_CHANGED) self.check_executed_tests(output, [testname], env_changed=[testname], - fail_env_changed=True) + fail_env_changed=True, + stats=1) def test_multiprocessing_timeout(self): code = textwrap.dedent(r""" @@ -1386,9 +1451,10 @@ def test_sleep(self): """) testname = self.create_test(code=code) - output = self.run_tests("-j2", "--timeout=1.0", testname, exitcode=2) + output = self.run_tests("-j2", "--timeout=1.0", testname, + exitcode=EXITCODE_BAD_TEST) self.check_executed_tests(output, [testname], - failed=testname) + failed=testname, stats=0) self.assertRegex(output, re.compile('%s timed out' % testname, re.MULTILINE)) @@ -1418,10 +1484,12 @@ def test_unraisable_exc(self): """) testname = self.create_test(code=code) - output = self.run_tests("--fail-env-changed", "-v", testname, exitcode=3) + output = self.run_tests("--fail-env-changed", "-v", testname, + exitcode=EXITCODE_ENV_CHANGED) self.check_executed_tests(output, [testname], env_changed=[testname], - fail_env_changed=True) + fail_env_changed=True, + stats=1) self.assertIn("Warning -- Unraisable exception", output) self.assertIn("Exception: weakref callback bug", output) @@ -1449,10 +1517,12 @@ def test_threading_excepthook(self): """) testname = self.create_test(code=code) - output = self.run_tests("--fail-env-changed", "-v", testname, exitcode=3) + output = self.run_tests("--fail-env-changed", "-v", testname, + exitcode=EXITCODE_ENV_CHANGED) self.check_executed_tests(output, [testname], env_changed=[testname], - fail_env_changed=True) + fail_env_changed=True, + stats=1) self.assertIn("Warning -- Uncaught thread exception", output) self.assertIn("Exception: bug in thread", output) @@ -1490,10 +1560,11 @@ def test_print_warning(self): for option in ("-v", "-W"): with self.subTest(option=option): cmd = ["--fail-env-changed", option, testname] - output = self.run_tests(*cmd, exitcode=3) + output = self.run_tests(*cmd, exitcode=EXITCODE_ENV_CHANGED) self.check_executed_tests(output, [testname], env_changed=[testname], - fail_env_changed=True) + fail_env_changed=True, + stats=1) self.assertRegex(output, regex) def test_unicode_guard_env(self): @@ -1519,6 +1590,34 @@ def test_cleanup(self): for name in names: self.assertFalse(os.path.exists(name), name) + @unittest.skipIf(support.is_wasi, + 'checking temp files is not implemented on WASI') + def test_leak_tmp_file(self): + code = textwrap.dedent(r""" + import os.path + import tempfile + import unittest + + class FileTests(unittest.TestCase): + def test_leak_tmp_file(self): + filename = os.path.join(tempfile.gettempdir(), 'mytmpfile') + with open(filename, "wb") as fp: + fp.write(b'content') + """) + testnames = [self.create_test(code=code) for _ in range(3)] + + output = self.run_tests("--fail-env-changed", "-v", "-j2", *testnames, + exitcode=EXITCODE_ENV_CHANGED) + self.check_executed_tests(output, testnames, + env_changed=testnames, + fail_env_changed=True, + randomize=True, + stats=len(testnames)) + for testname in testnames: + self.assertIn(f"Warning -- {testname} leaked temporary " + f"files (1): mytmpfile", + output) + def test_mp_decode_error(self): # gh-101634: If a worker stdout cannot be decoded, report a failed test # and a non-zero exit code. @@ -1552,7 +1651,47 @@ def test_mp_decode_error(self): exitcode=EXITCODE_BAD_TEST) self.check_executed_tests(output, [testname], failed=[testname], - randomize=True) + randomize=True, + stats=0) + + def test_doctest(self): + code = textwrap.dedent(fr''' + import doctest + import sys + from test import support + + def my_function(): + """ + Pass: + + >>> 1 + 1 + 2 + + Failure: + + >>> 2 + 3 + 23 + >>> 1 + 1 + 11 + + Skipped test (ignored): + + >>> id(1.0) # doctest: +SKIP + 7948648 + """ + + def test_main(): + testmod = sys.modules[__name__] + return support.run_doctest(testmod) + ''') + testname = self.create_test(code=code) + + output = self.run_tests("--fail-env-changed", "-v", "-j1", testname, + exitcode=EXITCODE_BAD_TEST) + self.check_executed_tests(output, [testname], + failed=[testname], + randomize=True, + stats=TestStats(3, 2, 0)) class TestUtils(unittest.TestCase): diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py index abf3c62156a94e..88775dd9f06774 100644 --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -4259,7 +4259,7 @@ def test_main(module=None): old_factories = None try: - support.run_unittest(*test_classes) + return support.run_unittest(*test_classes) finally: from xml.etree import ElementPath # Restore mapping and path cache diff --git a/Misc/NEWS.d/next/Tests/2022-06-16-17-50-58.gh-issue-93353.JdpATx.rst b/Misc/NEWS.d/next/Tests/2022-06-16-17-50-58.gh-issue-93353.JdpATx.rst new file mode 100644 index 00000000000000..4e232948f49ee7 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2022-06-16-17-50-58.gh-issue-93353.JdpATx.rst @@ -0,0 +1,2 @@ +regrtest now checks if a test leaks temporary files or directories if run +with -jN option. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Tests/2022-10-20-17-49-50.gh-issue-95027.viRpJB.rst b/Misc/NEWS.d/next/Tests/2022-10-20-17-49-50.gh-issue-95027.viRpJB.rst new file mode 100644 index 00000000000000..8bf1a9d33573ab --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2022-10-20-17-49-50.gh-issue-95027.viRpJB.rst @@ -0,0 +1,4 @@ +On Windows, when the Python test suite is run with the ``-jN`` option, the +ANSI code page is now used as the encoding for the stdout temporary file, +rather than using UTF-8 which can lead to decoding errors. Patch by Victor +Stinner. diff --git a/Misc/NEWS.d/next/Tests/2022-10-31-14-47-49.gh-issue-98903.7KinCV.rst b/Misc/NEWS.d/next/Tests/2022-10-31-14-47-49.gh-issue-98903.7KinCV.rst new file mode 100644 index 00000000000000..65636ab0683f39 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2022-10-31-14-47-49.gh-issue-98903.7KinCV.rst @@ -0,0 +1,2 @@ +The Python test suite now fails wit exit code 4 if no tests ran. It should +help detecting typos in test names and test methods. diff --git a/Misc/NEWS.d/next/Tests/2022-12-08-00-03-37.gh-issue-100086.1zYpto.rst b/Misc/NEWS.d/next/Tests/2022-12-08-00-03-37.gh-issue-100086.1zYpto.rst new file mode 100644 index 00000000000000..a5f1bb9f5a5e05 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2022-12-08-00-03-37.gh-issue-100086.1zYpto.rst @@ -0,0 +1,3 @@ +The Python test runner (libregrtest) now logs Python build information like +"debug" vs "release" build, or LTO and PGO optimizations. Patch by Victor +Stinner. diff --git a/Misc/NEWS.d/next/Tests/2023-06-28-02-51-08.gh-issue-101634.Rayczr.rst b/Misc/NEWS.d/next/Tests/2023-06-28-02-51-08.gh-issue-101634.Rayczr.rst new file mode 100644 index 00000000000000..6fbfc84c19e1b8 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-06-28-02-51-08.gh-issue-101634.Rayczr.rst @@ -0,0 +1,3 @@ +When running the Python test suite with ``-jN`` option, if a worker stdout +cannot be decoded from the locale encoding report a failed testn so the +exitcode is non-zero. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Tests/2023-09-02-19-06-52.gh-issue-108822.arTbBI.rst b/Misc/NEWS.d/next/Tests/2023-09-02-19-06-52.gh-issue-108822.arTbBI.rst new file mode 100644 index 00000000000000..e1c6df2adcb0ae --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-09-02-19-06-52.gh-issue-108822.arTbBI.rst @@ -0,0 +1,4 @@ +``regrtest`` now computes statistics on all tests: successes, failures and +skipped. ``test_netrc``, ``test_pep646_syntax`` and ``test_xml_etree`` now +return results in their ``test_main()`` function. Patch by Victor Stinner +and Alex Waygood. From 75df36e0d5d9a805743580803812ea8197a3d305 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 4 Sep 2023 03:11:33 -0700 Subject: [PATCH 249/632] [3.11] gh-89392: Make test_pep646_syntax discoverable (GH-108861) (GH-108869) (cherry picked from commit d0b22f6bd84239e50b43709f98f2bb950222cfe5) Co-authored-by: Serhiy Storchaka --- Lib/test/test_pep646_syntax.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_pep646_syntax.py b/Lib/test/test_pep646_syntax.py index 12a4227e4dc959..def313da89acbd 100644 --- a/Lib/test/test_pep646_syntax.py +++ b/Lib/test/test_pep646_syntax.py @@ -1,3 +1,5 @@ +import doctest + doctests = """ Setup @@ -317,10 +319,10 @@ __test__ = {'doctests' : doctests} -def test_main(verbose=False): - from test import support - from test import test_pep646_syntax - return support.run_doctest(test_pep646_syntax, verbose) +def load_tests(loader, tests, pattern): + tests.addTest(doctest.DocTestSuite()) + return tests + if __name__ == "__main__": - test_main(verbose=True) + unittest.main() From 723ca8c387e2624a842047f662ce0d7733ced4d9 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 4 Sep 2023 03:20:24 -0700 Subject: [PATCH 250/632] [3.11] gh-89392: Remove test_main() in test_netrc (GH-108860) (GH-108868) (cherry picked from commit 76f3c043b6c5971d5a13fc6decf87a80ddf7ef95) Co-authored-by: Serhiy Storchaka --- Lib/test/test_netrc.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_netrc.py b/Lib/test/test_netrc.py index b38cb327f68ecc..81e11a293cc4c8 100644 --- a/Lib/test/test_netrc.py +++ b/Lib/test/test_netrc.py @@ -1,5 +1,5 @@ import netrc, os, unittest, sys, textwrap -from test.support import os_helper, run_unittest +from test.support import os_helper try: import pwd @@ -308,8 +308,6 @@ def test_security(self): self.assertEqual(nrc.hosts['foo.domain.com'], ('anonymous', '', 'pass')) -def test_main(): - return run_unittest(NetrcTestCase) if __name__ == "__main__": - test_main() + unittest.main() From 3fd6ada2e661764a3be4acb339c67e6d96eef806 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 4 Sep 2023 13:43:12 +0300 Subject: [PATCH 251/632] [3.11] bpo-45229: Make ElementTree tests discoverable (GH-108859) (GH-108874) (cherry picked from commit 074ac1f72e392a576516639f650bac0519d1cb52) --- Lib/test/test_xml_etree.py | 65 ++++++++++-------------------------- Lib/test/test_xml_etree_c.py | 31 +++++++++-------- 2 files changed, 35 insertions(+), 61 deletions(-) diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py index 88775dd9f06774..c095dd0b59300c 100644 --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -365,6 +365,7 @@ def test_path_cache(self): from xml.etree import ElementPath elem = ET.XML(SAMPLE_XML) + ElementPath._cache.clear() for i in range(10): ET.ElementTree(elem).find('./'+str(i)) cache_len_10 = len(ElementPath._cache) for i in range(10): ET.ElementTree(elem).find('./'+str(i)) @@ -3955,8 +3956,9 @@ def test_issue14818(self): # -------------------------------------------------------------------- class NoAcceleratorTest(unittest.TestCase): - def setUp(self): - if not pyET: + @classmethod + def setUpClass(cls): + if ET is not pyET: raise unittest.SkipTest('only for the Python version') # Test that the C accelerator was not imported for pyET @@ -4202,8 +4204,7 @@ def get_option(config, option_name, default=None): # -------------------------------------------------------------------- - -def test_main(module=None): +def setUpModule(module=None): # When invoked without a module, runs the Python ET tests by loading pyET. # Otherwise, uses the given module as the ET. global pyET @@ -4215,62 +4216,30 @@ def test_main(module=None): global ET ET = module - test_classes = [ - ModuleTest, - ElementSlicingTest, - BasicElementTest, - BadElementTest, - BadElementPathTest, - ElementTreeTest, - IOTest, - ParseErrorTest, - XIncludeTest, - ElementTreeTypeTest, - ElementFindTest, - ElementIterTest, - TreeBuilderTest, - XMLParserTest, - XMLPullParserTest, - BugsTest, - KeywordArgsTest, - C14NTest, - ] - - # These tests will only run for the pure-Python version that doesn't import - # _elementtree. We can't use skipUnless here, because pyET is filled in only - # after the module is loaded. - if pyET is not ET: - test_classes.extend([ - NoAcceleratorTest, - ]) + # don't interfere with subsequent tests + def cleanup(): + global ET, pyET + ET = pyET = None + unittest.addModuleCleanup(cleanup) # Provide default namespace mapping and path cache. from xml.etree import ElementPath nsmap = ET.register_namespace._namespace_map # Copy the default namespace mapping nsmap_copy = nsmap.copy() + unittest.addModuleCleanup(nsmap.update, nsmap_copy) + unittest.addModuleCleanup(nsmap.clear) + # Copy the path cache (should be empty) path_cache = ElementPath._cache + unittest.addModuleCleanup(setattr, ElementPath, "_cache", path_cache) ElementPath._cache = path_cache.copy() + # Align the Comment/PI factories. if hasattr(ET, '_set_factories'): old_factories = ET._set_factories(ET.Comment, ET.PI) - else: - old_factories = None - - try: - return support.run_unittest(*test_classes) - finally: - from xml.etree import ElementPath - # Restore mapping and path cache - nsmap.clear() - nsmap.update(nsmap_copy) - ElementPath._cache = path_cache - if old_factories is not None: - ET._set_factories(*old_factories) - # don't interfere with subsequent tests - ET = pyET = None + unittest.addModuleCleanup(ET._set_factories, *old_factories) if __name__ == '__main__': - test_main() + unittest.main() diff --git a/Lib/test/test_xml_etree_c.py b/Lib/test/test_xml_etree_c.py index bec8208571902e..e7dee3efe3cc75 100644 --- a/Lib/test/test_xml_etree_c.py +++ b/Lib/test/test_xml_etree_c.py @@ -234,20 +234,25 @@ def test_element_with_children(self): self.check_sizeof(e, self.elementsize + self.extra + struct.calcsize('8P')) -def test_main(): - from test import test_xml_etree - - # Run the tests specific to the C implementation - support.run_unittest( - MiscTests, - TestAliasWorking, - TestAcceleratorImported, - SizeofTest, - ) - # Run the same test suite as the Python module - test_xml_etree.test_main(module=cET) +def install_tests(): + # Test classes should have __module__ referring to this module. + from test import test_xml_etree + for name, base in vars(test_xml_etree).items(): + if isinstance(base, type) and issubclass(base, unittest.TestCase): + class Temp(base): + pass + Temp.__name__ = Temp.__qualname__ = name + Temp.__module__ = __name__ + assert name not in globals() + globals()[name] = Temp + +install_tests() + +def setUpModule(): + from test import test_xml_etree + test_xml_etree.setUpModule(module=cET) if __name__ == '__main__': - test_main() + unittest.main() From b26717b319b2385bf004ded65d04d0d4a80504f1 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 4 Sep 2023 18:21:07 -0600 Subject: [PATCH 252/632] [3.11] CI: Bump GitHub Actions (GH-108879) (#108891) Bump GitHub Actions --- .github/workflows/build.yml | 22 +++++++++---------- .github/workflows/build_msi.yml | 6 ++--- .github/workflows/lint.yml | 2 +- .github/workflows/reusable-docs.yml | 6 ++--- .github/workflows/verify-ensurepip-wheels.yml | 2 +- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8e7d831f4232d8..30d2a973a31d3d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -40,7 +40,7 @@ jobs: run_ssl_tests: ${{ steps.check.outputs.run_ssl_tests }} config_hash: ${{ steps.config_hash.outputs.hash }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Check for source changes id: check run: | @@ -99,8 +99,8 @@ jobs: needs: check_source if: needs.check_source.outputs.run_tests == 'true' steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 - name: Install Dependencies run: | sudo ./.github/workflows/posix-deps-apt.sh @@ -143,13 +143,13 @@ jobs: needs: check_source if: needs.check_source.outputs.run_tests == 'true' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Restore config.cache uses: actions/cache@v3 with: path: config.cache key: ${{ github.job }}-${{ runner.os }}-${{ needs.check_source.outputs.config_hash }} - - uses: actions/setup-python@v3 + - uses: actions/setup-python@v4 - name: Install Dependencies run: sudo ./.github/workflows/posix-deps-apt.sh - name: Add ccache to PATH @@ -203,7 +203,7 @@ jobs: env: IncludeUwp: 'true' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Build CPython run: .\PCbuild\build.bat -e -d -p Win32 - name: Display build info @@ -220,7 +220,7 @@ jobs: env: IncludeUwp: 'true' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Register MSVC problem matcher run: echo "::add-matcher::.github/problem-matchers/msvc.json" - name: Build CPython @@ -242,7 +242,7 @@ jobs: HOMEBREW_NO_INSTALL_CLEANUP: 1 PYTHONSTRICTEXTENSIONBUILD: 1 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Restore config.cache uses: actions/cache@v3 with: @@ -276,7 +276,7 @@ jobs: OPENSSL_VER: 1.1.1v PYTHONSTRICTEXTENSIONBUILD: 1 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Register gcc problem matcher run: echo "::add-matcher::.github/problem-matchers/gcc.json" - name: Install Dependencies @@ -349,7 +349,7 @@ jobs: OPENSSL_DIR: ${{ github.workspace }}/multissl/openssl/${{ matrix.openssl_ver }} LD_LIBRARY_PATH: ${{ github.workspace }}/multissl/openssl/${{ matrix.openssl_ver }}/lib steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Restore config.cache uses: actions/cache@v3 with: @@ -398,7 +398,7 @@ jobs: PYTHONSTRICTEXTENSIONBUILD: 1 ASAN_OPTIONS: detect_leaks=0:allocator_may_return_null=1:handle_segv=0 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Restore config.cache uses: actions/cache@v3 with: diff --git a/.github/workflows/build_msi.yml b/.github/workflows/build_msi.yml index 1b9c1d141d13cc..4c6e2678f716b5 100644 --- a/.github/workflows/build_msi.yml +++ b/.github/workflows/build_msi.yml @@ -36,7 +36,7 @@ jobs: runs-on: windows-latest timeout-minutes: 60 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Build CPython installer run: .\Tools\msi\build.bat -x86 @@ -45,7 +45,7 @@ jobs: runs-on: windows-latest timeout-minutes: 60 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Build CPython installer run: .\Tools\msi\build.bat -x64 @@ -54,6 +54,6 @@ jobs: runs-on: windows-latest timeout-minutes: 60 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Build CPython installer run: .\Tools\msi\build.bat -arm64 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 4481ea80bfd936..27b04ba1d412e3 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -15,7 +15,7 @@ jobs: timeout-minutes: 10 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: python-version: "3.x" diff --git a/.github/workflows/reusable-docs.yml b/.github/workflows/reusable-docs.yml index f8e166b780b3ed..b434d3dfeb6da6 100644 --- a/.github/workflows/reusable-docs.yml +++ b/.github/workflows/reusable-docs.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 60 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: 'Set up Python' uses: actions/setup-python@v4 with: @@ -40,7 +40,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 60 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: 'Set up Python' uses: actions/setup-python@v4 with: @@ -58,7 +58,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 60 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/cache@v3 with: path: ~/.cache/pip diff --git a/.github/workflows/verify-ensurepip-wheels.yml b/.github/workflows/verify-ensurepip-wheels.yml index 030fbc6ce29555..e1f120db3f5ca4 100644 --- a/.github/workflows/verify-ensurepip-wheels.yml +++ b/.github/workflows/verify-ensurepip-wheels.yml @@ -25,7 +25,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: python-version: '3' From 562c1688565643bad13477610d6f8825fec0698c Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 4 Sep 2023 23:41:15 -0700 Subject: [PATCH 253/632] [3.11] gh-89392: Fix running test_pep646_syntax as script (GH-108875) (GH-108877) (cherry picked from commit f3b6608ba2b1db6ac449f656bf439bda8d66eb9f) Co-authored-by: Serhiy Storchaka --- Lib/test/test_pep646_syntax.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_pep646_syntax.py b/Lib/test/test_pep646_syntax.py index def313da89acbd..aac089b190bc11 100644 --- a/Lib/test/test_pep646_syntax.py +++ b/Lib/test/test_pep646_syntax.py @@ -1,4 +1,5 @@ import doctest +import unittest doctests = """ From 492e1ffdce3edd741aaf9f7a89f645a2256b70e4 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 5 Sep 2023 09:54:12 +0300 Subject: [PATCH 254/632] [3.11] gh-89392: Remove support of test_main() in libregrtest (GH-108876) (GH-108898) (cherry picked from commit 04a0830b00879efe057e3dfe75e9aa9c0caf1a26) --- Lib/test/libregrtest/runtest.py | 9 ++++----- Lib/test/test_regrtest.py | 8 ++++---- .../Tests/2023-09-04-15-18-14.gh-issue-89392.8A4T5p.rst | 2 ++ 3 files changed, 10 insertions(+), 9 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2023-09-04-15-18-14.gh-issue-89392.8A4T5p.rst diff --git a/Lib/test/libregrtest/runtest.py b/Lib/test/libregrtest/runtest.py index 8ca7b8bb6526ca..e73a8f1a855dc1 100644 --- a/Lib/test/libregrtest/runtest.py +++ b/Lib/test/libregrtest/runtest.py @@ -341,11 +341,10 @@ def _load_run_test(result: TestResult, ns: Namespace) -> None: the_module = importlib.import_module(abstest) - # If the test has a test_main, that will run the appropriate - # tests. If not, use normal unittest test loading. - test_func = getattr(the_module, "test_main", None) - if test_func is None: - test_func = functools.partial(_test_module, the_module) + if hasattr(the_module, "test_main"): + # https://github.com/python/cpython/issues/89392 + raise Exception(f"Module {result.test_name} defines test_main() which is no longer supported by regrtest") + test_func = functools.partial(_test_module, the_module) try: with save_env(ns, result.test_name): diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py index 8b7599bcb931fe..88c59b79dd5a91 100644 --- a/Lib/test/test_regrtest.py +++ b/Lib/test/test_regrtest.py @@ -1680,9 +1680,9 @@ def my_function(): 7948648 """ - def test_main(): - testmod = sys.modules[__name__] - return support.run_doctest(testmod) + def load_tests(loader, tests, pattern): + tests.addTest(doctest.DocTestSuite()) + return tests ''') testname = self.create_test(code=code) @@ -1691,7 +1691,7 @@ def test_main(): self.check_executed_tests(output, [testname], failed=[testname], randomize=True, - stats=TestStats(3, 2, 0)) + stats=TestStats(1, 1, 0)) class TestUtils(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Tests/2023-09-04-15-18-14.gh-issue-89392.8A4T5p.rst b/Misc/NEWS.d/next/Tests/2023-09-04-15-18-14.gh-issue-89392.8A4T5p.rst new file mode 100644 index 00000000000000..e1dea8e78cdd4e --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-09-04-15-18-14.gh-issue-89392.8A4T5p.rst @@ -0,0 +1,2 @@ +Removed support of ``test_main()`` function in tests. They now always use +normal unittest test runner. From e2404f5ed9d1e3e4c5b4616ce94a6bd8ddcfa9e4 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 5 Sep 2023 07:11:49 -0700 Subject: [PATCH 255/632] [3.11] gh-89392: Use normal unittest runner in test_type_cache (GH-108911) (GH-108914) (cherry picked from commit eaabaac7c099884f92428a7bb04ffa1f1d6080dd) Co-authored-by: Serhiy Storchaka --- Lib/test/test_type_cache.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_type_cache.py b/Lib/test/test_type_cache.py index 8502f6b0584b00..9dc91dc93448ad 100644 --- a/Lib/test/test_type_cache.py +++ b/Lib/test/test_type_cache.py @@ -44,4 +44,4 @@ def test_tp_version_tag_unique(self): if __name__ == "__main__": - support.run_unittest(TypeCacheTests) + unittest.main() From 4e5fd6dc1423c2028440f43bf2dec3185d0c4978 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 5 Sep 2023 08:08:14 -0700 Subject: [PATCH 256/632] [3.11] gh-89392: Use unittest test runner for doctests in test_getopt (GH-108916) (GH-108920) (cherry picked from commit f980cc19b9cafc09ef21e906871f810a1c89e62f) Co-authored-by: Serhiy Storchaka --- Lib/test/test_getopt.py | 71 +++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/Lib/test/test_getopt.py b/Lib/test/test_getopt.py index 64b9ce01e05ea2..e1a66898136d98 100644 --- a/Lib/test/test_getopt.py +++ b/Lib/test/test_getopt.py @@ -1,8 +1,8 @@ # test_getopt.py # David Goodger 2000-08-19 -from test.support import verbose, run_doctest from test.support.os_helper import EnvironmentVarGuard +import doctest import unittest import getopt @@ -134,48 +134,49 @@ def test_gnu_getopt(self): self.assertEqual(opts, [('-a', '')]) self.assertEqual(args, ['arg1', '-b', '1', '--alpha', '--beta=2']) - def test_libref_examples(self): - s = """ - Examples from the Library Reference: Doc/lib/libgetopt.tex + def test_issue4629(self): + longopts, shortopts = getopt.getopt(['--help='], '', ['help=']) + self.assertEqual(longopts, [('--help', '')]) + longopts, shortopts = getopt.getopt(['--help=x'], '', ['help=']) + self.assertEqual(longopts, [('--help', 'x')]) + self.assertRaises(getopt.GetoptError, getopt.getopt, ['--help='], '', ['help']) - An example using only Unix style options: +def test_libref_examples(): + """ + Examples from the Library Reference: Doc/lib/libgetopt.tex + An example using only Unix style options: - >>> import getopt - >>> args = '-a -b -cfoo -d bar a1 a2'.split() - >>> args - ['-a', '-b', '-cfoo', '-d', 'bar', 'a1', 'a2'] - >>> optlist, args = getopt.getopt(args, 'abc:d:') - >>> optlist - [('-a', ''), ('-b', ''), ('-c', 'foo'), ('-d', 'bar')] - >>> args - ['a1', 'a2'] - Using long option names is equally easy: + >>> import getopt + >>> args = '-a -b -cfoo -d bar a1 a2'.split() + >>> args + ['-a', '-b', '-cfoo', '-d', 'bar', 'a1', 'a2'] + >>> optlist, args = getopt.getopt(args, 'abc:d:') + >>> optlist + [('-a', ''), ('-b', ''), ('-c', 'foo'), ('-d', 'bar')] + >>> args + ['a1', 'a2'] + Using long option names is equally easy: - >>> s = '--condition=foo --testing --output-file abc.def -x a1 a2' - >>> args = s.split() - >>> args - ['--condition=foo', '--testing', '--output-file', 'abc.def', '-x', 'a1', 'a2'] - >>> optlist, args = getopt.getopt(args, 'x', [ - ... 'condition=', 'output-file=', 'testing']) - >>> optlist - [('--condition', 'foo'), ('--testing', ''), ('--output-file', 'abc.def'), ('-x', '')] - >>> args - ['a1', 'a2'] - """ - import types - m = types.ModuleType("libreftest", s) - run_doctest(m, verbose) + >>> s = '--condition=foo --testing --output-file abc.def -x a1 a2' + >>> args = s.split() + >>> args + ['--condition=foo', '--testing', '--output-file', 'abc.def', '-x', 'a1', 'a2'] + >>> optlist, args = getopt.getopt(args, 'x', [ + ... 'condition=', 'output-file=', 'testing']) + >>> optlist + [('--condition', 'foo'), ('--testing', ''), ('--output-file', 'abc.def'), ('-x', '')] + >>> args + ['a1', 'a2'] + """ + +def load_tests(loader, tests, pattern): + tests.addTest(doctest.DocTestSuite()) + return tests - def test_issue4629(self): - longopts, shortopts = getopt.getopt(['--help='], '', ['help=']) - self.assertEqual(longopts, [('--help', '')]) - longopts, shortopts = getopt.getopt(['--help=x'], '', ['help=']) - self.assertEqual(longopts, [('--help', 'x')]) - self.assertRaises(getopt.GetoptError, getopt.getopt, ['--help='], '', ['help']) if __name__ == "__main__": unittest.main() From cf19e8ea3a232086ff1e0d7d5e2a092d3d96fc7c Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 5 Sep 2023 18:27:55 +0300 Subject: [PATCH 257/632] [3.11] gh-108416: Mark slow but not CPU bound test methods with requires_resource('walltime') (GH-108480) (GH-108924) (cherry picked from commit 1e0d62793a84001e92f1c80b511d3a212b435acc) --- Lib/test/_test_multiprocessing.py | 3 + Lib/test/libregrtest/cmdline.py | 4 +- Lib/test/test_concurrent_futures/executor.py | 108 ++++++++++++ Lib/test/test_concurrent_futures/test_wait.py | 164 ++++++++++++++++++ Lib/test/test_eintr.py | 1 + Lib/test/test_faulthandler.py | 1 + Lib/test/test_httplib.py | 1 + Lib/test/test_imaplib.py | 5 +- Lib/test/test_io.py | 6 + Lib/test/test_logging.py | 1 + Lib/test/test_poll.py | 3 +- Lib/test/test_signal.py | 1 + Lib/test/test_smtpnet.py | 1 + Lib/test/test_ssl.py | 2 + Lib/test/test_subprocess.py | 2 + Lib/test/test_urllib2net.py | 5 + Lib/test/test_urllibnet.py | 2 + Lib/test/test_xmlrpc.py | 9 + 18 files changed, 316 insertions(+), 3 deletions(-) create mode 100644 Lib/test/test_concurrent_futures/executor.py create mode 100644 Lib/test/test_concurrent_futures/test_wait.py diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 45ca80b08bf3ca..54719f2586bb40 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -674,6 +674,7 @@ def test_close(self): close_queue(q) + @support.requires_resource('walltime') def test_many_processes(self): if self.TYPE == 'threads': self.skipTest('test not appropriate for {}'.format(self.TYPE)) @@ -4919,6 +4920,7 @@ def test_wait_slow(self): def test_wait_socket_slow(self): self.test_wait_socket(True) + @support.requires_resource('walltime') def test_wait_timeout(self): from multiprocessing.connection import wait @@ -4947,6 +4949,7 @@ def signal_and_sleep(cls, sem, period): sem.release() time.sleep(period) + @support.requires_resource('walltime') def test_wait_integer(self): from multiprocessing.connection import wait diff --git a/Lib/test/libregrtest/cmdline.py b/Lib/test/libregrtest/cmdline.py index ebe57920d9185c..40ae8fc46e9b01 100644 --- a/Lib/test/libregrtest/cmdline.py +++ b/Lib/test/libregrtest/cmdline.py @@ -107,6 +107,8 @@ cpu - Used for certain CPU-heavy tests. + walltime - Long running but not CPU-bound tests. + subprocess Run all tests for the subprocess module. urlfetch - It is okay to download files required on testing. @@ -129,7 +131,7 @@ ALL_RESOURCES = ('audio', 'curses', 'largefile', 'network', - 'decimal', 'cpu', 'subprocess', 'urlfetch', 'gui') + 'decimal', 'cpu', 'subprocess', 'urlfetch', 'gui', 'walltime') # Other resources excluded from --use=all: # diff --git a/Lib/test/test_concurrent_futures/executor.py b/Lib/test/test_concurrent_futures/executor.py new file mode 100644 index 00000000000000..1e7d4344740943 --- /dev/null +++ b/Lib/test/test_concurrent_futures/executor.py @@ -0,0 +1,108 @@ +import threading +import time +import weakref +from concurrent import futures +from test import support + + +def mul(x, y): + return x * y + +def capture(*args, **kwargs): + return args, kwargs + + +class MyObject(object): + def my_method(self): + pass + + +def make_dummy_object(_): + return MyObject() + + +class ExecutorTest: + # Executor.shutdown() and context manager usage is tested by + # ExecutorShutdownTest. + def test_submit(self): + future = self.executor.submit(pow, 2, 8) + self.assertEqual(256, future.result()) + + def test_submit_keyword(self): + future = self.executor.submit(mul, 2, y=8) + self.assertEqual(16, future.result()) + future = self.executor.submit(capture, 1, self=2, fn=3) + self.assertEqual(future.result(), ((1,), {'self': 2, 'fn': 3})) + with self.assertRaises(TypeError): + self.executor.submit(fn=capture, arg=1) + with self.assertRaises(TypeError): + self.executor.submit(arg=1) + + def test_map(self): + self.assertEqual( + list(self.executor.map(pow, range(10), range(10))), + list(map(pow, range(10), range(10)))) + + self.assertEqual( + list(self.executor.map(pow, range(10), range(10), chunksize=3)), + list(map(pow, range(10), range(10)))) + + def test_map_exception(self): + i = self.executor.map(divmod, [1, 1, 1, 1], [2, 3, 0, 5]) + self.assertEqual(i.__next__(), (0, 1)) + self.assertEqual(i.__next__(), (0, 1)) + self.assertRaises(ZeroDivisionError, i.__next__) + + @support.requires_resource('walltime') + def test_map_timeout(self): + results = [] + try: + for i in self.executor.map(time.sleep, + [0, 0, 6], + timeout=5): + results.append(i) + except futures.TimeoutError: + pass + else: + self.fail('expected TimeoutError') + + self.assertEqual([None, None], results) + + def test_shutdown_race_issue12456(self): + # Issue #12456: race condition at shutdown where trying to post a + # sentinel in the call queue blocks (the queue is full while processes + # have exited). + self.executor.map(str, [2] * (self.worker_count + 1)) + self.executor.shutdown() + + @support.cpython_only + def test_no_stale_references(self): + # Issue #16284: check that the executors don't unnecessarily hang onto + # references. + my_object = MyObject() + my_object_collected = threading.Event() + my_object_callback = weakref.ref( + my_object, lambda obj: my_object_collected.set()) + # Deliberately discarding the future. + self.executor.submit(my_object.my_method) + del my_object + + collected = my_object_collected.wait(timeout=support.SHORT_TIMEOUT) + self.assertTrue(collected, + "Stale reference not collected within timeout.") + + def test_max_workers_negative(self): + for number in (0, -1): + with self.assertRaisesRegex(ValueError, + "max_workers must be greater " + "than 0"): + self.executor_type(max_workers=number) + + def test_free_reference(self): + # Issue #14406: Result iterator should not keep an internal + # reference to result objects. + for obj in self.executor.map(make_dummy_object, range(10)): + wr = weakref.ref(obj) + del obj + support.gc_collect() # For PyPy or other GCs. + self.assertIsNone(wr()) diff --git a/Lib/test/test_concurrent_futures/test_wait.py b/Lib/test/test_concurrent_futures/test_wait.py new file mode 100644 index 00000000000000..3f64ca173c02f6 --- /dev/null +++ b/Lib/test/test_concurrent_futures/test_wait.py @@ -0,0 +1,164 @@ +import sys +import threading +import time +import unittest +from concurrent import futures +from test import support + +from .util import ( + CANCELLED_FUTURE, CANCELLED_AND_NOTIFIED_FUTURE, EXCEPTION_FUTURE, + SUCCESSFUL_FUTURE, + create_executor_tests, setup_module, + BaseTestCase, ThreadPoolMixin, + ProcessPoolForkMixin, ProcessPoolForkserverMixin, ProcessPoolSpawnMixin) + + +def mul(x, y): + return x * y + +def sleep_and_raise(t): + time.sleep(t) + raise Exception('this is an exception') + + +class WaitTests: + def test_20369(self): + # See https://bugs.python.org/issue20369 + future = self.executor.submit(time.sleep, 1.5) + done, not_done = futures.wait([future, future], + return_when=futures.ALL_COMPLETED) + self.assertEqual({future}, done) + self.assertEqual(set(), not_done) + + + def test_first_completed(self): + future1 = self.executor.submit(mul, 21, 2) + future2 = self.executor.submit(time.sleep, 1.5) + + done, not_done = futures.wait( + [CANCELLED_FUTURE, future1, future2], + return_when=futures.FIRST_COMPLETED) + + self.assertEqual(set([future1]), done) + self.assertEqual(set([CANCELLED_FUTURE, future2]), not_done) + + def test_first_completed_some_already_completed(self): + future1 = self.executor.submit(time.sleep, 1.5) + + finished, pending = futures.wait( + [CANCELLED_AND_NOTIFIED_FUTURE, SUCCESSFUL_FUTURE, future1], + return_when=futures.FIRST_COMPLETED) + + self.assertEqual( + set([CANCELLED_AND_NOTIFIED_FUTURE, SUCCESSFUL_FUTURE]), + finished) + self.assertEqual(set([future1]), pending) + + @support.requires_resource('walltime') + def test_first_exception(self): + future1 = self.executor.submit(mul, 2, 21) + future2 = self.executor.submit(sleep_and_raise, 1.5) + future3 = self.executor.submit(time.sleep, 3) + + finished, pending = futures.wait( + [future1, future2, future3], + return_when=futures.FIRST_EXCEPTION) + + self.assertEqual(set([future1, future2]), finished) + self.assertEqual(set([future3]), pending) + + def test_first_exception_some_already_complete(self): + future1 = self.executor.submit(divmod, 21, 0) + future2 = self.executor.submit(time.sleep, 1.5) + + finished, pending = futures.wait( + [SUCCESSFUL_FUTURE, + CANCELLED_FUTURE, + CANCELLED_AND_NOTIFIED_FUTURE, + future1, future2], + return_when=futures.FIRST_EXCEPTION) + + self.assertEqual(set([SUCCESSFUL_FUTURE, + CANCELLED_AND_NOTIFIED_FUTURE, + future1]), finished) + self.assertEqual(set([CANCELLED_FUTURE, future2]), pending) + + def test_first_exception_one_already_failed(self): + future1 = self.executor.submit(time.sleep, 2) + + finished, pending = futures.wait( + [EXCEPTION_FUTURE, future1], + return_when=futures.FIRST_EXCEPTION) + + self.assertEqual(set([EXCEPTION_FUTURE]), finished) + self.assertEqual(set([future1]), pending) + + def test_all_completed(self): + future1 = self.executor.submit(divmod, 2, 0) + future2 = self.executor.submit(mul, 2, 21) + + finished, pending = futures.wait( + [SUCCESSFUL_FUTURE, + CANCELLED_AND_NOTIFIED_FUTURE, + EXCEPTION_FUTURE, + future1, + future2], + return_when=futures.ALL_COMPLETED) + + self.assertEqual(set([SUCCESSFUL_FUTURE, + CANCELLED_AND_NOTIFIED_FUTURE, + EXCEPTION_FUTURE, + future1, + future2]), finished) + self.assertEqual(set(), pending) + + @support.requires_resource('walltime') + def test_timeout(self): + future1 = self.executor.submit(mul, 6, 7) + future2 = self.executor.submit(time.sleep, 6) + + finished, pending = futures.wait( + [CANCELLED_AND_NOTIFIED_FUTURE, + EXCEPTION_FUTURE, + SUCCESSFUL_FUTURE, + future1, future2], + timeout=5, + return_when=futures.ALL_COMPLETED) + + self.assertEqual(set([CANCELLED_AND_NOTIFIED_FUTURE, + EXCEPTION_FUTURE, + SUCCESSFUL_FUTURE, + future1]), finished) + self.assertEqual(set([future2]), pending) + + +class ThreadPoolWaitTests(ThreadPoolMixin, WaitTests, BaseTestCase): + + def test_pending_calls_race(self): + # Issue #14406: multi-threaded race condition when waiting on all + # futures. + event = threading.Event() + def future_func(): + event.wait() + oldswitchinterval = sys.getswitchinterval() + sys.setswitchinterval(1e-6) + try: + fs = {self.executor.submit(future_func) for i in range(100)} + event.set() + futures.wait(fs, return_when=futures.ALL_COMPLETED) + finally: + sys.setswitchinterval(oldswitchinterval) + + +create_executor_tests(globals(), WaitTests, + executor_mixins=(ProcessPoolForkMixin, + ProcessPoolForkserverMixin, + ProcessPoolSpawnMixin)) + + +def setUpModule(): + setup_module() + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_eintr.py b/Lib/test/test_eintr.py index 528147802ba47e..49b15f1a2dba92 100644 --- a/Lib/test/test_eintr.py +++ b/Lib/test/test_eintr.py @@ -9,6 +9,7 @@ class EINTRTests(unittest.TestCase): @unittest.skipUnless(hasattr(signal, "setitimer"), "requires setitimer()") + @support.requires_resource('walltime') def test_all(self): # Run the tester in a sub-process, to make sure there is only one # thread (for reliable signal delivery). diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py index 8d106daaf6520a..0e03ee2128891c 100644 --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py @@ -676,6 +676,7 @@ def test_dump_traceback_later_fd(self): with tempfile.TemporaryFile('wb+') as fp: self.check_dump_traceback_later(fd=fp.fileno()) + @support.requires_resource('walltime') def test_dump_traceback_later_twice(self): self.check_dump_traceback_later(loops=2) diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index acae0127a13f4b..47dbf08f3700d9 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -1911,6 +1911,7 @@ def test_networked_good_cert(self): h.close() self.assertIn('nginx', server_string) + @support.requires_resource('walltime') def test_networked_bad_cert(self): # We feed a "CA" cert that is unrelated to the server's cert import ssl diff --git a/Lib/test/test_imaplib.py b/Lib/test/test_imaplib.py index ed26fa16d6ea8b..cf2477a830ec30 100644 --- a/Lib/test/test_imaplib.py +++ b/Lib/test/test_imaplib.py @@ -11,7 +11,7 @@ import socket from test.support import (verbose, - run_with_tz, run_with_locale, cpython_only, + run_with_tz, run_with_locale, cpython_only, requires_resource, requires_working_socket) from test.support import hashlib_helper from test.support import threading_helper @@ -459,6 +459,7 @@ def test_simple_with_statement(self): with self.imap_class(*server.server_address): pass + @requires_resource('walltime') def test_imaplib_timeout_test(self): _, server = self._setup(SimpleIMAPHandler) addr = server.server_address[1] @@ -552,6 +553,7 @@ class NewIMAPSSLTests(NewIMAPTestsMixin, unittest.TestCase): imap_class = IMAP4_SSL server_class = SecureTCPServer + @requires_resource('walltime') def test_ssl_raises(self): ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) self.assertEqual(ssl_context.verify_mode, ssl.CERT_REQUIRED) @@ -566,6 +568,7 @@ def test_ssl_raises(self): ssl_context=ssl_context) client.shutdown() + @requires_resource('walltime') def test_ssl_verified(self): ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) ssl_context.load_verify_locations(CAFILE) diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 0a5f82a8bed114..e54a13c2d84bde 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -4380,10 +4380,12 @@ def run(): self.assertFalse(err.strip('.!')) @threading_helper.requires_working_threading() + @support.requires_resource('walltime') def test_daemon_threads_shutdown_stdout_deadlock(self): self.check_daemon_threads_shutdown_deadlock('stdout') @threading_helper.requires_working_threading() + @support.requires_resource('walltime') def test_daemon_threads_shutdown_stderr_deadlock(self): self.check_daemon_threads_shutdown_deadlock('stderr') @@ -4557,11 +4559,13 @@ def alarm_handler(sig, frame): os.close(r) @requires_alarm + @support.requires_resource('walltime') def test_interrupted_read_retry_buffered(self): self.check_interrupted_read_retry(lambda x: x.decode('latin1'), mode="rb") @requires_alarm + @support.requires_resource('walltime') def test_interrupted_read_retry_text(self): self.check_interrupted_read_retry(lambda x: x, mode="r", encoding="latin1") @@ -4635,10 +4639,12 @@ def alarm2(sig, frame): raise @requires_alarm + @support.requires_resource('walltime') def test_interrupted_write_retry_buffered(self): self.check_interrupted_write_retry(b"x", mode="wb") @requires_alarm + @support.requires_resource('walltime') def test_interrupted_write_retry_text(self): self.check_interrupted_write_retry("x", mode="w", encoding="latin1") diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index f1f2b75fef869b..55c5cd565814e8 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -631,6 +631,7 @@ def test_path_objects(self): support.is_emscripten, "Emscripten cannot fstat unlinked files." ) @threading_helper.requires_working_threading() + @support.requires_resource('walltime') def test_race(self): # Issue #14632 refers. def remove_loop(fname, tries): diff --git a/Lib/test/test_poll.py b/Lib/test/test_poll.py index 02165a0244ddf4..1847ae95db9292 100644 --- a/Lib/test/test_poll.py +++ b/Lib/test/test_poll.py @@ -8,7 +8,7 @@ import time import unittest from test.support import ( - cpython_only, requires_subprocess, requires_working_socket + cpython_only, requires_subprocess, requires_working_socket, requires_resource ) from test.support import threading_helper from test.support.os_helper import TESTFN @@ -124,6 +124,7 @@ def fileno(self): # select(), modified to use poll() instead. @requires_subprocess() + @requires_resource('walltime') def test_poll2(self): cmd = 'for i in 0 1 2 3 4 5 6 7 8 9; do echo testing...; sleep 1; done' proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py index ddd635197a7adc..f6632896100a66 100644 --- a/Lib/test/test_signal.py +++ b/Lib/test/test_signal.py @@ -745,6 +745,7 @@ def test_siginterrupt_on(self): interrupted = self.readpipe_interrupted(True) self.assertTrue(interrupted) + @support.requires_resource('walltime') def test_siginterrupt_off(self): # If a signal handler is installed and siginterrupt is called with # a false value for the second argument, when that signal arrives, it diff --git a/Lib/test/test_smtpnet.py b/Lib/test/test_smtpnet.py index 72f51cd8d81f59..2e0dc1aa276f35 100644 --- a/Lib/test/test_smtpnet.py +++ b/Lib/test/test_smtpnet.py @@ -61,6 +61,7 @@ def test_connect_default_port(self): server.ehlo() server.quit() + @support.requires_resource('walltime') def test_connect_using_sslcontext(self): context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) context.check_hostname = False diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index d6acbf36e6e605..965c2728914b50 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -2398,6 +2398,7 @@ def test_timeout_connect_ex(self): self.assertIn(rc, (errno.EAGAIN, errno.EWOULDBLOCK)) @unittest.skipUnless(socket_helper.IPV6_ENABLED, 'Needs IPv6') + @support.requires_resource('walltime') def test_get_server_certificate_ipv6(self): with socket_helper.transient_internet('ipv6.google.com'): _test_get_server_certificate(self, 'ipv6.google.com', 443) @@ -2956,6 +2957,7 @@ def try_protocol_combo(server_protocol, client_protocol, expect_success, class ThreadedTests(unittest.TestCase): + @support.requires_resource('walltime') def test_echo(self): """Basic test of an SSL client connecting to a server""" if support.verbose: diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 7217508be8c029..3142026ac535b3 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -268,6 +268,7 @@ def test_check_output_stdin_with_input_arg(self): self.assertIn('stdin', c.exception.args[0]) self.assertIn('input', c.exception.args[0]) + @support.requires_resource('walltime') def test_check_output_timeout(self): # check_output() function with timeout arg with self.assertRaises(subprocess.TimeoutExpired) as c: @@ -1641,6 +1642,7 @@ def test_check_output_stdin_with_input_arg(self): self.assertIn('stdin', c.exception.args[0]) self.assertIn('input', c.exception.args[0]) + @support.requires_resource('walltime') def test_check_output_timeout(self): with self.assertRaises(subprocess.TimeoutExpired) as c: cp = self.run_python(( diff --git a/Lib/test/test_urllib2net.py b/Lib/test/test_urllib2net.py index d8d882b2d33589..f0874d8d3ce463 100644 --- a/Lib/test/test_urllib2net.py +++ b/Lib/test/test_urllib2net.py @@ -133,6 +133,7 @@ def setUp(self): # XXX The rest of these tests aren't very good -- they don't check much. # They do sometimes catch some major disasters, though. + @support.requires_resource('walltime') def test_ftp(self): # Testing the same URL twice exercises the caching in CacheFTPHandler urls = [ @@ -196,6 +197,7 @@ def test_urlwithfrag(self): self.assertEqual(res.geturl(), "http://www.pythontest.net/index.html#frag") + @support.requires_resource('walltime') def test_redirect_url_withfrag(self): redirect_url_with_frag = "http://www.pythontest.net/redir/with_frag/" with socket_helper.transient_internet(redirect_url_with_frag): @@ -334,6 +336,7 @@ def test_http_timeout(self): FTP_HOST = 'ftp://www.pythontest.net/' + @support.requires_resource('walltime') def test_ftp_basic(self): self.assertIsNone(socket.getdefaulttimeout()) with socket_helper.transient_internet(self.FTP_HOST, timeout=None): @@ -352,6 +355,7 @@ def test_ftp_default_timeout(self): socket.setdefaulttimeout(None) self.assertEqual(u.fp.fp.raw._sock.gettimeout(), 60) + @support.requires_resource('walltime') def test_ftp_no_timeout(self): self.assertIsNone(socket.getdefaulttimeout()) with socket_helper.transient_internet(self.FTP_HOST): @@ -363,6 +367,7 @@ def test_ftp_no_timeout(self): socket.setdefaulttimeout(None) self.assertIsNone(u.fp.fp.raw._sock.gettimeout()) + @support.requires_resource('walltime') def test_ftp_timeout(self): with socket_helper.transient_internet(self.FTP_HOST): u = _urlopen_with_retry(self.FTP_HOST, timeout=60) diff --git a/Lib/test/test_urllibnet.py b/Lib/test/test_urllibnet.py index 773101ce41f602..49a3b5afdebb2f 100644 --- a/Lib/test/test_urllibnet.py +++ b/Lib/test/test_urllibnet.py @@ -109,6 +109,7 @@ def test_getcode(self): open_url.close() self.assertEqual(code, 404) + @support.requires_resource('walltime') def test_bad_address(self): # Make sure proper exception is raised when connecting to a bogus # address. @@ -191,6 +192,7 @@ def test_header(self): logo = "http://www.pythontest.net/" + @support.requires_resource('walltime') def test_data_header(self): with self.urlretrieve(self.logo) as (file_location, fileheaders): datevalue = fileheaders.get('Date') diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py index 9ff5545f786a32..7f517dc7c13564 100644 --- a/Lib/test/test_xmlrpc.py +++ b/Lib/test/test_xmlrpc.py @@ -1031,38 +1031,47 @@ def test_path2(self): self.assertEqual(p.add(6,8), 6+8) self.assertRaises(xmlrpclib.Fault, p.pow, 6, 8) + @support.requires_resource('walltime') def test_path3(self): p = xmlrpclib.ServerProxy(URL+"/is/broken") self.assertRaises(xmlrpclib.Fault, p.add, 6, 8) + @support.requires_resource('walltime') def test_invalid_path(self): p = xmlrpclib.ServerProxy(URL+"/invalid") self.assertRaises(xmlrpclib.Fault, p.add, 6, 8) + @support.requires_resource('walltime') def test_path_query_fragment(self): p = xmlrpclib.ServerProxy(URL+"/foo?k=v#frag") self.assertEqual(p.test(), "/foo?k=v#frag") + @support.requires_resource('walltime') def test_path_fragment(self): p = xmlrpclib.ServerProxy(URL+"/foo#frag") self.assertEqual(p.test(), "/foo#frag") + @support.requires_resource('walltime') def test_path_query(self): p = xmlrpclib.ServerProxy(URL+"/foo?k=v") self.assertEqual(p.test(), "/foo?k=v") + @support.requires_resource('walltime') def test_empty_path(self): p = xmlrpclib.ServerProxy(URL) self.assertEqual(p.test(), "/RPC2") + @support.requires_resource('walltime') def test_root_path(self): p = xmlrpclib.ServerProxy(URL + "/") self.assertEqual(p.test(), "/") + @support.requires_resource('walltime') def test_empty_path_query(self): p = xmlrpclib.ServerProxy(URL + "?k=v") self.assertEqual(p.test(), "?k=v") + @support.requires_resource('walltime') def test_empty_path_fragment(self): p = xmlrpclib.ServerProxy(URL + "#frag") self.assertEqual(p.test(), "#frag") From 53148af9d7497759fcb405bc5dbe07282b18bf5c Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Tue, 5 Sep 2023 11:54:23 -0500 Subject: [PATCH 258/632] [3.11] gh-107565: Update Windows build to use OpenSSL 3.0.10 (GH-108932) --- .../Windows/2023-09-05-10-08-47.gh-issue-107565.CIMftz.rst | 1 + PCbuild/get_externals.bat | 4 ++-- PCbuild/python.props | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2023-09-05-10-08-47.gh-issue-107565.CIMftz.rst diff --git a/Misc/NEWS.d/next/Windows/2023-09-05-10-08-47.gh-issue-107565.CIMftz.rst b/Misc/NEWS.d/next/Windows/2023-09-05-10-08-47.gh-issue-107565.CIMftz.rst new file mode 100644 index 00000000000000..024a58299caed9 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-09-05-10-08-47.gh-issue-107565.CIMftz.rst @@ -0,0 +1 @@ +Update Windows build to use OpenSSL 3.0.10. diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index d41955ac482c73..83afe7851ec292 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -53,7 +53,7 @@ echo.Fetching external libraries... set libraries= set libraries=%libraries% bzip2-1.0.8 if NOT "%IncludeLibffiSrc%"=="false" set libraries=%libraries% libffi-3.4.4 -if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-3.0.9 +if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-3.0.10 set libraries=%libraries% sqlite-3.42.0.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.12.1 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.12.1 @@ -77,7 +77,7 @@ echo.Fetching external binaries... set binaries= if NOT "%IncludeLibffi%"=="false" set binaries=%binaries% libffi-3.4.4 -if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-3.0.9 +if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-3.0.10 if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.12.1 if NOT "%IncludeSSLSrc%"=="false" set binaries=%binaries% nasm-2.11.06 diff --git a/PCbuild/python.props b/PCbuild/python.props index d3586235c82652..94faa8221eac5a 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -74,8 +74,8 @@ $(ExternalsDir)libffi-3.4.4\ $(libffiDir)$(ArchName)\ $(libffiOutDir)include - $(ExternalsDir)openssl-3.0.9\ - $(ExternalsDir)openssl-bin-3.0.9\$(ArchName)\ + $(ExternalsDir)openssl-3.0.10\ + $(ExternalsDir)openssl-bin-3.0.10\$(ArchName)\ $(opensslOutDir)include $(ExternalsDir)\nasm-2.11.06\ $(ExternalsDir)\zlib-1.2.13\ From 2ef888d25046963a1c7572f7204c26047c82f6a2 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 5 Sep 2023 17:39:09 -0700 Subject: [PATCH 259/632] [3.11] gh-108857: improve markup in inspect.Signature.replace() docs (GH-108862) (#108970) gh-108857: improve markup in inspect.Signature.replace() docs (GH-108862) (cherry picked from commit 6f8411cfd68134ccae01b0b4cb332578008a69e3) Co-authored-by: nabin2004 <107109731+nabin2004@users.noreply.github.com> --- Doc/library/inspect.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index 932e52816413ae..11f2d701199771 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -709,7 +709,7 @@ function. .. method:: Signature.replace(*[, parameters][, return_annotation]) - Create a new Signature instance based on the instance replace was invoked + 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 From b8340905fcf86fd8813dd9971e499411476a74c1 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 6 Sep 2023 07:09:54 -0700 Subject: [PATCH 260/632] [3.11] gh-108983: Add more PEP 526 tests to `test_grammar` (GH-108984) (#109001) gh-108983: Add more PEP 526 tests to `test_grammar` (GH-108984) (cherry picked from commit 1fb20d42c58924e2e941622b3539645c7b843e0e) Co-authored-by: Nikita Sobolev --- Lib/test/test_grammar.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py index bcd8d584b0f51b..38e98beeb94c5e 100644 --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -355,6 +355,11 @@ def test_var_annot_syntax_errors(self): check_syntax_error(self, "x: int: str") check_syntax_error(self, "def f():\n" " nonlocal x: int\n") + check_syntax_error(self, "def f():\n" + " global x: int\n") + check_syntax_error(self, "x: int = y = 1") + check_syntax_error(self, "z = w: int = 1") + check_syntax_error(self, "x: int = y: int = 1") # AST pass check_syntax_error(self, "[x, 0]: int\n") check_syntax_error(self, "f(): int\n") @@ -368,6 +373,12 @@ def test_var_annot_syntax_errors(self): check_syntax_error(self, "def f():\n" " global x\n" " x: int\n") + check_syntax_error(self, "def f():\n" + " x: int\n" + " nonlocal x\n") + check_syntax_error(self, "def f():\n" + " nonlocal x\n" + " x: int\n") def test_var_annot_basic_semantics(self): # execution order From d61b8f9b8b3e8c2fdb3f3f7a24149ca08270b573 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 6 Sep 2023 08:32:36 -0700 Subject: [PATCH 261/632] [3.11] gh-91960: Skip test_gdb if gdb cannot retrive Python frames (GH-108999) (#109011) gh-91960: Skip test_gdb if gdb cannot retrive Python frames (GH-108999) Skip test_gdb if gdb is unable to retrieve Python frame objects: if a frame is "". When Python is built with "clang -Og", gdb can fail to retrive the 'frame' parameter of _PyEval_EvalFrameDefault(). In this case, tests like py_bt() are likely to fail. Without getting access to Python frames, python-gdb.py is mostly clueless on retrieving the Python traceback. Moreover, test_gdb is no longer skipped on macOS if Python is built with Clang. (cherry picked from commit fbce43a251488f666be9794c908a6613bf8ae260) Co-authored-by: Victor Stinner --- Lib/test/test_gdb.py | 7 +++---- .../Tests/2023-09-06-15-36-51.gh-issue-91960.P3nD5v.rst | 7 +++++++ 2 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2023-09-06-15-36-51.gh-issue-91960.P3nD5v.rst diff --git a/Lib/test/test_gdb.py b/Lib/test/test_gdb.py index bba9b07c6708b5..d1f9a03a41fd78 100644 --- a/Lib/test/test_gdb.py +++ b/Lib/test/test_gdb.py @@ -55,10 +55,6 @@ def get_gdb_version(): if not sysconfig.is_python_build(): raise unittest.SkipTest("test_gdb only works on source builds at the moment.") -if 'Clang' in platform.python_compiler() and sys.platform == 'darwin': - raise unittest.SkipTest("test_gdb doesn't work correctly when python is" - " built with LLVM clang") - if ((sysconfig.get_config_var('PGO_PROF_USE_FLAG') or 'xxx') in (sysconfig.get_config_var('PY_CORE_CFLAGS') or '')): raise unittest.SkipTest("test_gdb is not reliable on PGO builds") @@ -247,6 +243,9 @@ def get_stack_trace(self, source=None, script=None, for pattern in ( '(frame information optimized out)', 'Unable to read information on python frame', + # gh-91960: On Python built with "clang -Og", gdb gets + # "frame=" for _PyEval_EvalFrameDefault() parameter + '(unable to read python frame information)', ): if pattern in out: raise unittest.SkipTest(f"{pattern!r} found in gdb output") diff --git a/Misc/NEWS.d/next/Tests/2023-09-06-15-36-51.gh-issue-91960.P3nD5v.rst b/Misc/NEWS.d/next/Tests/2023-09-06-15-36-51.gh-issue-91960.P3nD5v.rst new file mode 100644 index 00000000000000..46472abf9802bc --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-09-06-15-36-51.gh-issue-91960.P3nD5v.rst @@ -0,0 +1,7 @@ +Skip ``test_gdb`` if gdb is unable to retrieve Python frame objects: if a +frame is ````. When Python is built with "clang -Og", gdb can +fail to retrive the *frame* parameter of ``_PyEval_EvalFrameDefault()``. In +this case, tests like ``py_bt()`` are likely to fail. Without getting access +to Python frames, ``python-gdb.py`` is mostly clueless on retrieving the +Python traceback. Moreover, ``test_gdb`` is no longer skipped on macOS if +Python is built with Clang. Patch by Victor Stinner. From 95eb9849dd8004b3019f79a3973e2f431c5635cc Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 6 Sep 2023 18:40:39 +0200 Subject: [PATCH 262/632] [3.11] gh-108851: Fix tomllib recursion tests (#108853) (#109013) gh-108851: Fix tomllib recursion tests (#108853) * Add get_recursion_available() and get_recursion_depth() functions to the test.support module. * Change infinite_recursion() default max_depth from 75 to 100. * Fix test_tomllib recursion tests for WASI buildbots: reduce the recursion limit and compute the maximum nested array/dict depending on the current available recursion limit. * test.pythoninfo logs sys.getrecursionlimit(). * Enhance test_sys tests on sys.getrecursionlimit() and sys.setrecursionlimit(). Backport notes: * Set support.infinite_recursion() minimum to 4 frames. * test_support.test_get_recursion_depth() uses limit-2, apparently f-string counts for 2 frames in Python 3.11. * test_sys.test_setrecursionlimit_to_depth() tests depth+2 instead of depth+1. (cherry picked from commit 8ff11425783806f8cb78e99f667546b1f7f3428e) --- Lib/test/pythoninfo.py | 1 + Lib/test/support/__init__.py | 57 +++++++++++-- Lib/test/test_support.py | 79 +++++++++++++++++++ Lib/test/test_sys.py | 57 +++++++------ Lib/test/test_tomllib/test_misc.py | 27 +++++-- ...-09-03-21-18-35.gh-issue-108851.CCuHyI.rst | 2 + ...-09-03-21-41-10.gh-issue-108851.xFTYOE.rst | 3 + 7 files changed, 184 insertions(+), 42 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2023-09-03-21-18-35.gh-issue-108851.CCuHyI.rst create mode 100644 Misc/NEWS.d/next/Tests/2023-09-03-21-41-10.gh-issue-108851.xFTYOE.rst diff --git a/Lib/test/pythoninfo.py b/Lib/test/pythoninfo.py index 3240e2e3bca80d..2575a9ae9e5538 100644 --- a/Lib/test/pythoninfo.py +++ b/Lib/test/pythoninfo.py @@ -112,6 +112,7 @@ def collect_sys(info_add): call_func(info_add, 'sys.androidapilevel', sys, 'getandroidapilevel') call_func(info_add, 'sys.windowsversion', sys, 'getwindowsversion') + call_func(info_add, 'sys.getrecursionlimit', sys, 'getrecursionlimit') encoding = sys.getfilesystemencoding() if hasattr(sys, 'getfilesystemencodeerrors'): diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 94967f2c302a8c..05b33753724dbc 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -2197,20 +2197,61 @@ def check_disallow_instantiation(testcase, tp, *args, **kwds): msg = f"cannot create '{re.escape(qualname)}' instances" testcase.assertRaisesRegex(TypeError, msg, tp, *args, **kwds) +def get_recursion_depth(): + """Get the recursion depth of the caller function. + + In the __main__ module, at the module level, it should be 1. + """ + try: + import _testinternalcapi + depth = _testinternalcapi.get_recursion_depth() + except (ImportError, RecursionError) as exc: + # sys._getframe() + frame.f_back implementation. + try: + depth = 0 + frame = sys._getframe() + while frame is not None: + depth += 1 + frame = frame.f_back + finally: + # Break any reference cycles. + frame = None + + # Ignore get_recursion_depth() frame. + return max(depth - 1, 1) + +def get_recursion_available(): + """Get the number of available frames before RecursionError. + + It depends on the current recursion depth of the caller function and + sys.getrecursionlimit(). + """ + limit = sys.getrecursionlimit() + depth = get_recursion_depth() + return limit - depth + @contextlib.contextmanager -def infinite_recursion(max_depth=75): +def set_recursion_limit(limit): + """Temporarily change the recursion limit.""" + original_limit = sys.getrecursionlimit() + try: + sys.setrecursionlimit(limit) + yield + finally: + sys.setrecursionlimit(original_limit) + +def infinite_recursion(max_depth=100): """Set a lower limit for tests that interact with infinite recursions (e.g test_ast.ASTHelpers_Test.test_recursion_direct) since on some debug windows builds, due to not enough functions being inlined the stack size might not handle the default recursion limit (1000). See bpo-11105 for details.""" - - original_depth = sys.getrecursionlimit() - try: - sys.setrecursionlimit(max_depth) - yield - finally: - sys.setrecursionlimit(original_depth) + if max_depth < 4: + raise ValueError("max_depth must be at least 4, got {max_depth}") + depth = get_recursion_depth() + depth = max(depth - 1, 1) # Ignore infinite_recursion() frame. + limit = depth + max_depth + return set_recursion_limit(limit) def ignore_deprecations_from(module: str, *, like: str) -> object: token = object() diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index 2a33889b7e97ac..54e532064740f3 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -698,6 +698,85 @@ def test_has_strftime_extensions(self): else: self.assertTrue(support.has_strftime_extensions) + def test_get_recursion_depth(self): + # test support.get_recursion_depth() + code = textwrap.dedent(""" + from test import support + import sys + + def check(cond): + if not cond: + raise AssertionError("test failed") + + # depth 1 + check(support.get_recursion_depth() == 1) + + # depth 2 + def test_func(): + check(support.get_recursion_depth() == 2) + test_func() + + def test_recursive(depth, limit): + if depth >= limit: + # cannot call get_recursion_depth() at this depth, + # it can raise RecursionError + return + get_depth = support.get_recursion_depth() + print(f"test_recursive: {depth}/{limit}: " + f"get_recursion_depth() says {get_depth}") + check(get_depth == depth) + test_recursive(depth + 1, limit) + + # depth up to 25 + with support.infinite_recursion(max_depth=25): + limit = sys.getrecursionlimit() + print(f"test with sys.getrecursionlimit()={limit}") + # Use limit-2 since f-string seems to consume 2 frames. + test_recursive(2, limit - 2) + + # depth up to 500 + with support.infinite_recursion(max_depth=500): + limit = sys.getrecursionlimit() + print(f"test with sys.getrecursionlimit()={limit}") + # limit-2 since f-string seems to consume 2 frames + test_recursive(2, limit - 2) + """) + script_helper.assert_python_ok("-c", code) + + def test_recursion(self): + # Test infinite_recursion() and get_recursion_available() functions. + def recursive_function(depth): + if depth: + recursive_function(depth - 1) + + for max_depth in (5, 25, 250): + with support.infinite_recursion(max_depth): + available = support.get_recursion_available() + + # Recursion up to 'available' additional frames should be OK. + recursive_function(available) + + # Recursion up to 'available+1' additional frames must raise + # RecursionError. Avoid self.assertRaises(RecursionError) which + # can consume more than 3 frames and so raises RecursionError. + try: + recursive_function(available + 1) + except RecursionError: + pass + else: + self.fail("RecursionError was not raised") + + # Test the bare minimumum: max_depth=4 + with support.infinite_recursion(4): + try: + recursive_function(4) + except RecursionError: + pass + else: + self.fail("RecursionError was not raised") + + #self.assertEqual(available, 2) + # XXX -follows a list of untested API # make_legacy_pyc # is_resource_enabled diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 6f56c9ef97e7c3..86cf1a794f973c 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -269,20 +269,29 @@ def test_switchinterval(self): finally: sys.setswitchinterval(orig) - def test_recursionlimit(self): + def test_getrecursionlimit(self): + limit = sys.getrecursionlimit() + self.assertIsInstance(limit, int) + self.assertGreater(limit, 1) + self.assertRaises(TypeError, sys.getrecursionlimit, 42) - oldlimit = sys.getrecursionlimit() - self.assertRaises(TypeError, sys.setrecursionlimit) - self.assertRaises(ValueError, sys.setrecursionlimit, -42) - sys.setrecursionlimit(10000) - self.assertEqual(sys.getrecursionlimit(), 10000) - sys.setrecursionlimit(oldlimit) + + def test_setrecursionlimit(self): + old_limit = sys.getrecursionlimit() + try: + sys.setrecursionlimit(10_005) + self.assertEqual(sys.getrecursionlimit(), 10_005) + + self.assertRaises(TypeError, sys.setrecursionlimit) + self.assertRaises(ValueError, sys.setrecursionlimit, -42) + finally: + sys.setrecursionlimit(old_limit) def test_recursionlimit_recovery(self): if hasattr(sys, 'gettrace') and sys.gettrace(): self.skipTest('fatal error if run with a trace function') - oldlimit = sys.getrecursionlimit() + old_limit = sys.getrecursionlimit() def f(): f() try: @@ -301,35 +310,31 @@ def f(): with self.assertRaises(RecursionError): f() finally: - sys.setrecursionlimit(oldlimit) + sys.setrecursionlimit(old_limit) @test.support.cpython_only - def test_setrecursionlimit_recursion_depth(self): + def test_setrecursionlimit_to_depth(self): # Issue #25274: Setting a low recursion limit must be blocked if the # current recursion depth is already higher than limit. - from _testinternalcapi import get_recursion_depth - - def set_recursion_limit_at_depth(depth, limit): - recursion_depth = get_recursion_depth() - if recursion_depth >= depth: + old_limit = sys.getrecursionlimit() + try: + depth = support.get_recursion_depth() + with self.subTest(limit=sys.getrecursionlimit(), depth=depth): + # depth + 2 is OK + sys.setrecursionlimit(depth + 2) + + # reset the limit to be able to call self.assertRaises() + # context manager + sys.setrecursionlimit(old_limit) with self.assertRaises(RecursionError) as cm: - sys.setrecursionlimit(limit) + sys.setrecursionlimit(depth + 1) self.assertRegex(str(cm.exception), "cannot set the recursion limit to [0-9]+ " "at the recursion depth [0-9]+: " "the limit is too low") - else: - set_recursion_limit_at_depth(depth, limit) - - oldlimit = sys.getrecursionlimit() - try: - sys.setrecursionlimit(1000) - - for limit in (10, 25, 50, 75, 100, 150, 200): - set_recursion_limit_at_depth(limit, limit) finally: - sys.setrecursionlimit(oldlimit) + sys.setrecursionlimit(old_limit) def test_getwindowsversion(self): # Raise SkipTest if sys doesn't have getwindowsversion attribute diff --git a/Lib/test/test_tomllib/test_misc.py b/Lib/test/test_tomllib/test_misc.py index a477a219fd9ebd..9e677a337a2835 100644 --- a/Lib/test/test_tomllib/test_misc.py +++ b/Lib/test/test_tomllib/test_misc.py @@ -9,6 +9,7 @@ import sys import tempfile import unittest +from test import support from . import tomllib @@ -92,13 +93,23 @@ def test_deepcopy(self): self.assertEqual(obj_copy, expected_obj) def test_inline_array_recursion_limit(self): - # 465 with default recursion limit - nest_count = int(sys.getrecursionlimit() * 0.465) - recursive_array_toml = "arr = " + nest_count * "[" + nest_count * "]" - tomllib.loads(recursive_array_toml) + with support.infinite_recursion(max_depth=100): + available = support.get_recursion_available() + nest_count = (available // 2) - 2 + # Add details if the test fails + with self.subTest(limit=sys.getrecursionlimit(), + available=available, + nest_count=nest_count): + recursive_array_toml = "arr = " + nest_count * "[" + nest_count * "]" + tomllib.loads(recursive_array_toml) def test_inline_table_recursion_limit(self): - # 310 with default recursion limit - nest_count = int(sys.getrecursionlimit() * 0.31) - recursive_table_toml = nest_count * "key = {" + nest_count * "}" - tomllib.loads(recursive_table_toml) + with support.infinite_recursion(max_depth=100): + available = support.get_recursion_available() + nest_count = (available // 3) - 1 + # Add details if the test fails + with self.subTest(limit=sys.getrecursionlimit(), + available=available, + nest_count=nest_count): + recursive_table_toml = nest_count * "key = {" + nest_count * "}" + tomllib.loads(recursive_table_toml) diff --git a/Misc/NEWS.d/next/Tests/2023-09-03-21-18-35.gh-issue-108851.CCuHyI.rst b/Misc/NEWS.d/next/Tests/2023-09-03-21-18-35.gh-issue-108851.CCuHyI.rst new file mode 100644 index 00000000000000..7a5b3052af22f2 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-09-03-21-18-35.gh-issue-108851.CCuHyI.rst @@ -0,0 +1,2 @@ +Add ``get_recursion_available()`` and ``get_recursion_depth()`` functions to +the :mod:`test.support` module. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Tests/2023-09-03-21-41-10.gh-issue-108851.xFTYOE.rst b/Misc/NEWS.d/next/Tests/2023-09-03-21-41-10.gh-issue-108851.xFTYOE.rst new file mode 100644 index 00000000000000..b35aaebb410afb --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-09-03-21-41-10.gh-issue-108851.xFTYOE.rst @@ -0,0 +1,3 @@ +Fix ``test_tomllib`` recursion tests for WASI buildbots: reduce the recursion +limit and compute the maximum nested array/dict depending on the current +available recursion limit. Patch by Victor Stinner. From d631b823116793050fd0416c0ec877ea00cb47de Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 6 Sep 2023 11:01:25 -0700 Subject: [PATCH 263/632] [3.11] gh-109002: Ensure only one wheel for each vendored package (GH-109003) (#109006) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Output with one wheel: ``` ❯ GITHUB_ACTIONS=true ./Tools/build/verify_ensurepip_wheels.py Verifying checksum for /Volumes/RAMDisk/cpython/Lib/ensurepip/_bundled/pip-23.2.1-py3-none-any.whl. Expected digest: 7ccf472345f20d35bdc9d1841ff5f313260c2c33fe417f48c30ac46cccabf5be Actual digest: 7ccf472345f20d35bdc9d1841ff5f313260c2c33fe417f48c30ac46cccabf5be ::notice file=/Volumes/RAMDisk/cpython/Lib/ensurepip/_bundled/pip-23.2.1-py3-none-any.whl::Successfully verified the checksum of the pip wheel. ``` Output with two wheels: ``` ❯ GITHUB_ACTIONS=true ./Tools/build/verify_ensurepip_wheels.py ::error file=/Volumes/RAMDisk/cpython/Lib/ensurepip/_bundled/pip-22.0.4-py3-none-any.whl::Found more than one wheel for package pip. ::error file=/Volumes/RAMDisk/cpython/Lib/ensurepip/_bundled/pip-23.2.1-py3-none-any.whl::Found more than one wheel for package pip. ``` Output without wheels: ``` ❯ GITHUB_ACTIONS=true ./Tools/build/verify_ensurepip_wheels.py ::error file=::Could not find a pip wheel on disk. ``` (cherry picked from commit f8a047941f2e4a1848700c21d58a08c9ec6a9c68) Co-authored-by: Łukasz Langa --- Tools/scripts/verify_ensurepip_wheels.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Tools/scripts/verify_ensurepip_wheels.py b/Tools/scripts/verify_ensurepip_wheels.py index 044d1fd6b3cf2d..434a0b4c5387d4 100755 --- a/Tools/scripts/verify_ensurepip_wheels.py +++ b/Tools/scripts/verify_ensurepip_wheels.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python3 +#!/usr/bin/env python3 """ Compare checksums for wheels in :mod:`ensurepip` against the Cheeseshop. @@ -35,11 +35,17 @@ def print_error(file_path: str, message: str) -> None: def verify_wheel(package_name: str) -> bool: # Find the package on disk - package_path = next(WHEEL_DIR.glob(f"{package_name}*.whl"), None) - if not package_path: - print_error("", f"Could not find a {package_name} wheel on disk.") + package_paths = list(WHEEL_DIR.glob(f"{package_name}*.whl")) + if len(package_paths) != 1: + if package_paths: + for p in package_paths: + print_error(p, f"Found more than one wheel for package {package_name}.") + else: + print_error("", f"Could not find a {package_name} wheel on disk.") return False + package_path = package_paths[0] + print(f"Verifying checksum for {package_path}.") # Find the version of the package used by ensurepip From ed233ddc00afca0fd9affb119b21dd13b8b935fb Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 6 Sep 2023 23:00:30 +0200 Subject: [PATCH 264/632] [3.11] gh-108740: Fix "make regen-all" race condition (#108741) (#109021) gh-108740: Fix "make regen-all" race condition (#108741) Fix a race condition in "make regen-all". The deepfreeze.c source and files generated by Argument Clinic are now generated or updated before generating "global objects". Previously, some identifiers may miss depending on the order in which these files were generated. * "make regen-global-objects": Make sure that deepfreeze.c is generated and up to date, and always run "make clinic". * "make regen-deepfreeze" now only updates deepfreeze.c (C file). It doesn't build deepfreeze.o (object) anymore. * Remove misleading messages in "make regen-global-objects" and "make clinic". They are now outdated, these commands are now safe to use. Backport notes: * Omit Doc/using/configure.rst changes. * no need to change "make clinic", it didn't run generate_global_objects.py script before. Co-authored-by: Erlend E. Aasland (cherry picked from commit db1ee6a19ab62191c16ecb732cb4dcaede98a902) --- Makefile.pre.in | 19 +++++++++++-------- ...-09-01-01-39-26.gh-issue-108740.JHExAQ.rst | 4 ++++ Tools/scripts/freeze_modules.py | 2 +- 3 files changed, 16 insertions(+), 9 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2023-09-01-01-39-26.gh-issue-108740.JHExAQ.rst diff --git a/Makefile.pre.in b/Makefile.pre.in index 884bea1841c232..99b1be2206c723 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -487,6 +487,7 @@ OBJECT_OBJS= \ Objects/unionobject.o \ Objects/weakrefobject.o +DEEPFREEZE_C = Python/deepfreeze/deepfreeze.c DEEPFREEZE_OBJS = Python/deepfreeze/deepfreeze.o ########################################################################## @@ -1152,12 +1153,12 @@ regen-frozen: Tools/scripts/freeze_modules.py $(FROZEN_FILES_IN) # Deepfreeze targets .PHONY: regen-deepfreeze -regen-deepfreeze: $(DEEPFREEZE_OBJS) +regen-deepfreeze: $(DEEPFREEZE_C) DEEPFREEZE_DEPS=$(srcdir)/Tools/scripts/deepfreeze.py $(FREEZE_MODULE_DEPS) $(FROZEN_FILES_OUT) # BEGIN: deepfreeze modules -Python/deepfreeze/deepfreeze.c: $(DEEPFREEZE_DEPS) +$(DEEPFREEZE_C): $(DEEPFREEZE_DEPS) $(PYTHON_FOR_FREEZE) $(srcdir)/Tools/scripts/deepfreeze.py \ Python/frozen_modules/importlib._bootstrap.h:importlib._bootstrap \ Python/frozen_modules/importlib._bootstrap_external.h:importlib._bootstrap_external \ @@ -1184,8 +1185,6 @@ Python/deepfreeze/deepfreeze.c: $(DEEPFREEZE_DEPS) Python/frozen_modules/frozen_only.h:frozen_only \ -o Python/deepfreeze/deepfreeze.c # END: deepfreeze modules - @echo "Note: Deepfreeze may have added some global objects," - @echo " so run 'make regen-global-objects' if necessary." # We keep this renamed target around for folks with muscle memory. .PHONY: regen-importlib @@ -1194,11 +1193,12 @@ regen-importlib: regen-frozen ############################################################################ # Global objects +# Dependencies which can add and/or remove _Py_ID() identifiers: +# - deepfreeze.c +# - "make clinic" .PHONY: regen-global-objects -regen-global-objects: $(srcdir)/Tools/scripts/generate_global_objects.py +regen-global-objects: $(srcdir)/Tools/scripts/generate_global_objects.py $(DEEPFREEZE_C) clinic $(PYTHON_FOR_REGEN) $(srcdir)/Tools/scripts/generate_global_objects.py - @echo "Note: Global objects can be added or removed by other tools (e.g. deepfreeze), " - @echo " so be sure to re-run regen-global-objects after those tools." ############################################################################ # ABI @@ -1217,8 +1217,10 @@ regen-limited-abi: all ############################################################################ # Regenerate all generated files +# "clinic" is regenerated implicitly via "regen-global-objects". +.PHONY: regen-all regen-all: regen-opcode regen-opcode-targets regen-typeslots \ - regen-token regen-ast regen-keyword regen-sre regen-frozen clinic \ + regen-token regen-ast regen-keyword regen-sre regen-frozen \ regen-pegen-metaparser regen-pegen regen-test-frozenmain \ regen-global-objects @echo @@ -2389,6 +2391,7 @@ recheck: autoconf: (cd $(srcdir); autoreconf -ivf -Werror) +# See https://github.com/tiran/cpython_autoconf container .PHONY: regen-configure regen-configure: @if command -v podman >/dev/null; then RUNTIME="podman"; else RUNTIME="docker"; fi; \ diff --git a/Misc/NEWS.d/next/Build/2023-09-01-01-39-26.gh-issue-108740.JHExAQ.rst b/Misc/NEWS.d/next/Build/2023-09-01-01-39-26.gh-issue-108740.JHExAQ.rst new file mode 100644 index 00000000000000..190d50387f339e --- /dev/null +++ b/Misc/NEWS.d/next/Build/2023-09-01-01-39-26.gh-issue-108740.JHExAQ.rst @@ -0,0 +1,4 @@ +Fix a race condition in ``make regen-all``. The ``deepfreeze.c`` source and +files generated by Argument Clinic are now generated or updated before +generating "global objects". Previously, some identifiers may miss depending +on the order in which these files were generated. Patch by Victor Stinner. diff --git a/Tools/scripts/freeze_modules.py b/Tools/scripts/freeze_modules.py index dd208c78471943..0e41d96d4623a8 100644 --- a/Tools/scripts/freeze_modules.py +++ b/Tools/scripts/freeze_modules.py @@ -581,7 +581,7 @@ def regen_makefile(modules): pyfiles = [] frozenfiles = [] rules = [''] - deepfreezerules = ["Python/deepfreeze/deepfreeze.c: $(DEEPFREEZE_DEPS)", + deepfreezerules = ["$(DEEPFREEZE_C): $(DEEPFREEZE_DEPS)", "\t$(PYTHON_FOR_FREEZE) $(srcdir)/Tools/scripts/deepfreeze.py \\"] for src in _iter_sources(modules): frozen_header = relpath_for_posix_display(src.frozenfile, ROOT_DIR) From 5fbd77165b9a3fdcac3c52bfbc3836e00cd91b79 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 6 Sep 2023 17:11:06 -0700 Subject: [PATCH 265/632] [3.11] GH-108202: Document ``calendar``'s command-line interface (GH-109020) (#109031) GH-108202: Document ``calendar``'s command-line interface (GH-109020) (cherry picked from commit f0f96a9f40762499811681d405b6f922b6ed7a55) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Co-authored-by: Hugo van Kemenade --- Doc/library/calendar.rst | 143 +++++++++++++++++++++++++++++++++++++++ Lib/calendar.py | 2 +- 2 files changed, 144 insertions(+), 1 deletion(-) diff --git a/Doc/library/calendar.rst b/Doc/library/calendar.rst index 6460e6f0f49745..dbef171c487dba 100644 --- a/Doc/library/calendar.rst +++ b/Doc/library/calendar.rst @@ -448,3 +448,146 @@ The :mod:`calendar` module defines the following exceptions: Module :mod:`time` Low-level time related functions. + + +.. _calendar-cli: + +Command-Line Usage +------------------ + +.. versionadded:: 2.5 + +The :mod:`calendar` module can be executed as a script from the command line +to interactively print a calendar. + +.. code-block:: shell + + python -m calendar [-h] [-L LOCALE] [-e ENCODING] [-t {text,html}] + [-w WIDTH] [-l LINES] [-s SPACING] [-m MONTHS] [-c CSS] + [year] [month] + + +For example, to print a calendar for the year 2000: + +.. code-block:: console + + $ python -m calendar 2000 + 2000 + + January February March + Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su + 1 2 1 2 3 4 5 6 1 2 3 4 5 + 3 4 5 6 7 8 9 7 8 9 10 11 12 13 6 7 8 9 10 11 12 + 10 11 12 13 14 15 16 14 15 16 17 18 19 20 13 14 15 16 17 18 19 + 17 18 19 20 21 22 23 21 22 23 24 25 26 27 20 21 22 23 24 25 26 + 24 25 26 27 28 29 30 28 29 27 28 29 30 31 + 31 + + April May June + Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su + 1 2 1 2 3 4 5 6 7 1 2 3 4 + 3 4 5 6 7 8 9 8 9 10 11 12 13 14 5 6 7 8 9 10 11 + 10 11 12 13 14 15 16 15 16 17 18 19 20 21 12 13 14 15 16 17 18 + 17 18 19 20 21 22 23 22 23 24 25 26 27 28 19 20 21 22 23 24 25 + 24 25 26 27 28 29 30 29 30 31 26 27 28 29 30 + + July August September + Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su + 1 2 1 2 3 4 5 6 1 2 3 + 3 4 5 6 7 8 9 7 8 9 10 11 12 13 4 5 6 7 8 9 10 + 10 11 12 13 14 15 16 14 15 16 17 18 19 20 11 12 13 14 15 16 17 + 17 18 19 20 21 22 23 21 22 23 24 25 26 27 18 19 20 21 22 23 24 + 24 25 26 27 28 29 30 28 29 30 31 25 26 27 28 29 30 + 31 + + October November December + Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su + 1 1 2 3 4 5 1 2 3 + 2 3 4 5 6 7 8 6 7 8 9 10 11 12 4 5 6 7 8 9 10 + 9 10 11 12 13 14 15 13 14 15 16 17 18 19 11 12 13 14 15 16 17 + 16 17 18 19 20 21 22 20 21 22 23 24 25 26 18 19 20 21 22 23 24 + 23 24 25 26 27 28 29 27 28 29 30 25 26 27 28 29 30 31 + 30 31 + + +The following options are accepted: + +.. program:: calendar + + +.. option:: --help, -h + + Show the help message and exit. + + +.. option:: --locale LOCALE, -L LOCALE + + The locale to use for month and weekday names. + Defaults to English. + + +.. option:: --encoding ENCODING, -e ENCODING + + The encoding to use for output. + :option:`--encoding` is required if :option:`--locale` is set. + + +.. option:: --type {text,html}, -t {text,html} + + Print the calendar to the terminal as text, + or as an HTML document. + + +.. option:: year + + The year to print the calendar for. + Must be a number between 1 and 9999. + Defaults to the current year. + + +.. option:: month + + The month of the specified :option:`year` to print the calendar for. + Must be a number between 1 and 12, + and may only be used in text mode. + Defaults to printing a calendar for the full year. + + +*Text-mode options:* + +.. option:: --width WIDTH, -w WIDTH + + The width of the date column in terminal columns. + The date is printed centred in the column. + Any value lower than 2 is ignored. + Defaults to 2. + + +.. option:: --lines LINES, -l LINES + + The number of lines for each week in terminal rows. + The date is printed top-aligned. + Any value lower than 1 is ignored. + Defaults to 1. + + +.. option:: --spacing SPACING, -s SPACING + + The space between months in columns. + Any value lower than 2 is ignored. + Defaults to 6. + + +.. option:: --months MONTHS, -m MONTHS + + The number of months printed per row. + Defaults to 3. + + +*HTML-mode options:* + +.. option:: --css CSS, -c CSS + + The path of a CSS stylesheet to use for the calendar. + This must either be relative to the generated HTML, + or an absolute HTTP or ``file:///`` URL. diff --git a/Lib/calendar.py b/Lib/calendar.py index 657396439c91fc..7cdf9311b57ce2 100644 --- a/Lib/calendar.py +++ b/Lib/calendar.py @@ -693,7 +693,7 @@ def main(args): parser.add_argument( "-L", "--locale", default=None, - help="locale to be used from month and weekday names" + help="locale to use for month and weekday names" ) parser.add_argument( "-e", "--encoding", From 1f115a8822e758f262b3dd2180a24ff1ddced19d Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 6 Sep 2023 17:11:53 -0700 Subject: [PATCH 266/632] [3.11] gh-107755: Document the correct default value of slice step (GH-107756) (#108956) gh-107755: Document the correct default value of slice step (GH-107756) Document the correct default value of slice step. (cherry picked from commit 9bf350b0662fcf1a8b43b9293e6c8ecf3c711561) Co-authored-by: wim glenn --- 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 2468e53cd596a1..6e048aa7eaeeab 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -1630,7 +1630,7 @@ are always available. They are listed here in alphabetical order. .. class:: slice(stop) - slice(start, stop, step=1) + slice(start, stop, step=None) Return a :term:`slice` object representing the set of indices specified by ``range(start, stop, step)``. The *start* and *step* arguments default to From 1ebe014d62a700514c7dce8fd0b4d6b6d7929835 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 7 Sep 2023 02:34:16 +0200 Subject: [PATCH 267/632] [3.11] gh-109015: Add test.support.socket_helper.tcp_blackhole() (#109016) (#109042) gh-109015: Add test.support.socket_helper.tcp_blackhole() (#109016) Skip test_asyncio, test_imaplib and test_socket tests if FreeBSD TCP blackhole is enabled (net.inet.tcp.blackhole=2). (cherry picked from commit a52a3509770f29f940cda9307704908949912276) --- Lib/test/support/socket_helper.py | 62 ++++++++++++++++++- Lib/test/test_asyncio/test_events.py | 3 + Lib/test/test_asyncio/test_sock_lowlevel.py | 4 ++ Lib/test/test_asyncio/test_sslproto.py | 3 + Lib/test/test_imaplib.py | 1 + Lib/test/test_socket.py | 2 + ...-09-06-18-27-53.gh-issue-109015.1dS1AQ.rst | 6 ++ 7 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Tests/2023-09-06-18-27-53.gh-issue-109015.1dS1AQ.rst diff --git a/Lib/test/support/socket_helper.py b/Lib/test/support/socket_helper.py index 50e1d4d56c9b20..ec6d1dee4e9d81 100644 --- a/Lib/test/support/socket_helper.py +++ b/Lib/test/support/socket_helper.py @@ -1,8 +1,9 @@ import contextlib import errno import socket -import unittest +import subprocess import sys +import unittest from .. import support from . import warnings_helper @@ -270,3 +271,62 @@ def filter_error(err): # __cause__ or __context__? finally: socket.setdefaulttimeout(old_timeout) + + +# consider that sysctl values should not change while tests are running +_sysctl_cache = {} + +def _get_sysctl(name): + """Get a sysctl value as an integer.""" + try: + return _sysctl_cache[name] + except KeyError: + pass + + # At least Linux and FreeBSD support the "-n" option + cmd = ['sysctl', '-n', name] + proc = subprocess.run(cmd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True) + if proc.returncode: + support.print_warning(f"{' '.join(cmd)!r} command failed with " + f"exit code {proc.returncode}") + # cache the error to only log the warning once + _sysctl_cache[name] = None + return None + output = proc.stdout + + # Parse '0\n' to get '0' + try: + value = int(output.strip()) + except Exception as exc: + support.print_warning(f"Failed to parse {' '.join(cmd)!r} " + f"command output {output!r}: {exc!r}") + # cache the error to only log the warning once + _sysctl_cache[name] = None + return None + + _sysctl_cache[name] = value + return value + + +def tcp_blackhole(): + if not sys.platform.startswith('freebsd'): + return False + + # gh-109015: test if FreeBSD TCP blackhole is enabled + value = _get_sysctl('net.inet.tcp.blackhole') + if value is None: + # don't skip if we fail to get the sysctl value + return False + return (value != 0) + + +def skip_if_tcp_blackhole(test): + """Decorator skipping test if TCP blackhole is enabled.""" + skip_if = unittest.skipIf( + tcp_blackhole(), + "TCP blackhole is enabled (sysctl net.inet.tcp.blackhole)" + ) + return skip_if(test) diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index 5728d25254f369..e811c8182a1bba 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -671,6 +671,7 @@ def test_create_connection_local_addr(self): self.assertEqual(port, expected) tr.close() + @socket_helper.skip_if_tcp_blackhole def test_create_connection_local_addr_skip_different_family(self): # See https://github.com/python/cpython/issues/86508 port1 = socket_helper.find_unused_port() @@ -692,6 +693,7 @@ async def getaddrinfo(host, port, *args, **kwargs): with self.assertRaises(OSError): self.loop.run_until_complete(f) + @socket_helper.skip_if_tcp_blackhole def test_create_connection_local_addr_nomatch_family(self): # See https://github.com/python/cpython/issues/86508 port1 = socket_helper.find_unused_port() @@ -1248,6 +1250,7 @@ def connection_made(self, transport): server.close() + @socket_helper.skip_if_tcp_blackhole def test_server_close(self): f = self.loop.create_server(MyProto, '0.0.0.0', 0) server = self.loop.run_until_complete(f) diff --git a/Lib/test/test_asyncio/test_sock_lowlevel.py b/Lib/test/test_asyncio/test_sock_lowlevel.py index db47616d18343e..1f8f4e2ddfc62f 100644 --- a/Lib/test/test_asyncio/test_sock_lowlevel.py +++ b/Lib/test/test_asyncio/test_sock_lowlevel.py @@ -10,6 +10,10 @@ from test import support from test.support import socket_helper +if socket_helper.tcp_blackhole(): + raise unittest.SkipTest('Not relevant to ProactorEventLoop') + + def tearDownModule(): asyncio.set_event_loop_policy(None) diff --git a/Lib/test/test_asyncio/test_sslproto.py b/Lib/test/test_asyncio/test_sslproto.py index 52a45f1c7c6e96..37d015339761c6 100644 --- a/Lib/test/test_asyncio/test_sslproto.py +++ b/Lib/test/test_asyncio/test_sslproto.py @@ -5,6 +5,7 @@ import unittest import weakref from test import support +from test.support import socket_helper from unittest import mock try: import ssl @@ -350,6 +351,7 @@ async def client(addr): support.gc_collect() self.assertIsNone(client_context()) + @socket_helper.skip_if_tcp_blackhole def test_start_tls_client_buf_proto_1(self): HELLO_MSG = b'1' * self.PAYLOAD_SIZE @@ -502,6 +504,7 @@ async def client(addr): asyncio.wait_for(client(srv.addr), timeout=support.SHORT_TIMEOUT)) + @socket_helper.skip_if_tcp_blackhole def test_start_tls_server_1(self): HELLO_MSG = b'1' * self.PAYLOAD_SIZE ANSWER = b'answer' diff --git a/Lib/test/test_imaplib.py b/Lib/test/test_imaplib.py index cf2477a830ec30..f097ba68154f25 100644 --- a/Lib/test/test_imaplib.py +++ b/Lib/test/test_imaplib.py @@ -77,6 +77,7 @@ def test_that_Time2Internaldate_returns_a_result(self): for t in self.timevalues(): imaplib.Time2Internaldate(t) + @socket_helper.skip_if_tcp_blackhole def test_imap4_host_default_value(self): # Check whether the IMAP4_PORT is truly unavailable. with socket.socket() as s: diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 13cb2a7945d1db..602603d51b1bfd 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -5171,6 +5171,7 @@ def mocked_socket_module(self): finally: socket.socket = old_socket + @socket_helper.skip_if_tcp_blackhole def test_connect(self): port = socket_helper.find_unused_port() cli = socket.socket(socket.AF_INET, socket.SOCK_STREAM) @@ -5179,6 +5180,7 @@ def test_connect(self): cli.connect((HOST, port)) self.assertEqual(cm.exception.errno, errno.ECONNREFUSED) + @socket_helper.skip_if_tcp_blackhole def test_create_connection(self): # Issue #9792: errors raised by create_connection() should have # a proper errno attribute. diff --git a/Misc/NEWS.d/next/Tests/2023-09-06-18-27-53.gh-issue-109015.1dS1AQ.rst b/Misc/NEWS.d/next/Tests/2023-09-06-18-27-53.gh-issue-109015.1dS1AQ.rst new file mode 100644 index 00000000000000..cb641be9312e1a --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-09-06-18-27-53.gh-issue-109015.1dS1AQ.rst @@ -0,0 +1,6 @@ +Fix test_asyncio, test_imaplib and test_socket tests on FreeBSD if the TCP +blackhole is enabled (``sysctl net.inet.tcp.blackhole``). Skip the few tests +which failed with ``ETIMEDOUT`` which such non standard configuration. +Currently, the `FreeBSD GCP image enables TCP and UDP blackhole +`_ (``sysctl net.inet.tcp.blackhole=2`` +and ``sysctl net.inet.udp.blackhole=1``). Patch by Victor Stinner. From c70a29160a6198d47e492ad41beb8fd5c4bcb29d Mon Sep 17 00:00:00 2001 From: Mikhail Samylov Date: Thu, 7 Sep 2023 08:32:55 +0300 Subject: [PATCH 268/632] [3.11] Docs: Fix typo in datetime.tzinfo docstring (GH-107257) (#109055) --- Lib/datetime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/datetime.py b/Lib/datetime.py index c3c2568f986594..60927de6adeb1a 100644 --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -1223,7 +1223,7 @@ def __reduce__(self): class tzinfo: """Abstract base class for time zone info classes. - Subclasses must override the name(), utcoffset() and dst() methods. + Subclasses must override the tzname(), utcoffset() and dst() methods. """ __slots__ = () From 784207ecf3217a104f315c390359af10ca6b1a47 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Wed, 6 Sep 2023 23:07:57 -0700 Subject: [PATCH 269/632] [3.11] gh-108915: Removes extra backslashes in str.split docstring (GH-109044) (#109062) [3.11] gh-108915: Removes extra backslashes in str.split docstring (GH-109044). (cherry picked from commit e7d5433f944a5725aa82595f9251abfc8a63d333) Co-authored-by: Daniel Weiss <134341009+justdan6@users.noreply.github.com> --- Objects/clinic/unicodeobject.c.h | 6 +++--- Objects/unicodeobject.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Objects/clinic/unicodeobject.c.h b/Objects/clinic/unicodeobject.c.h index 07877693c26e32..7a42cd8ffc939a 100644 --- a/Objects/clinic/unicodeobject.c.h +++ b/Objects/clinic/unicodeobject.c.h @@ -882,7 +882,7 @@ PyDoc_STRVAR(unicode_split__doc__, " The separator used to split the string.\n" "\n" " When set to None (the default value), will split on any whitespace\n" -" character (including \\\\n \\\\r \\\\t \\\\f and spaces) and will discard\n" +" character (including \\n \\r \\t \\f and spaces) and will discard\n" " empty strings from the result.\n" " maxsplit\n" " Maximum number of splits (starting from the left).\n" @@ -983,7 +983,7 @@ PyDoc_STRVAR(unicode_rsplit__doc__, " The separator used to split the string.\n" "\n" " When set to None (the default value), will split on any whitespace\n" -" character (including \\\\n \\\\r \\\\t \\\\f and spaces) and will discard\n" +" character (including \\n \\r \\t \\f and spaces) and will discard\n" " empty strings from the result.\n" " maxsplit\n" " Maximum number of splits (starting from the left).\n" @@ -1353,4 +1353,4 @@ unicode_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=b5dd7cefead9a8e7 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=11519887c1619a4e input=a9049054013a1b77]*/ diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 42e13f069060b8..c27fc009c5664e 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -13167,7 +13167,7 @@ str.split as unicode_split The separator used to split the string. When set to None (the default value), will split on any whitespace - character (including \\n \\r \\t \\f and spaces) and will discard + character (including \n \r \t \f and spaces) and will discard empty strings from the result. maxsplit: Py_ssize_t = -1 Maximum number of splits (starting from the left). @@ -13183,7 +13183,7 @@ the regular expression module. static PyObject * unicode_split_impl(PyObject *self, PyObject *sep, Py_ssize_t maxsplit) -/*[clinic end generated code: output=3a65b1db356948dc input=906d953b44efc43b]*/ +/*[clinic end generated code: output=3a65b1db356948dc input=07b9040d98c5fe8d]*/ { if (sep == Py_None) return split(self, NULL, maxsplit); From 883ff44f0d370d1f7c48bada494e0b34effa3019 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 6 Sep 2023 23:13:34 -0700 Subject: [PATCH 270/632] [3.11] test.pythoninfo logs freedesktop_os_release() (GH-109057) (#109063) test.pythoninfo logs freedesktop_os_release() (GH-109057) (cherry picked from commit babdced23fc299b7607ac76abfdd7a81050f8359) Co-authored-by: Victor Stinner --- Lib/test/pythoninfo.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/Lib/test/pythoninfo.py b/Lib/test/pythoninfo.py index 2575a9ae9e5538..7a9c01b8c74a62 100644 --- a/Lib/test/pythoninfo.py +++ b/Lib/test/pythoninfo.py @@ -164,6 +164,26 @@ def collect_platform(info_add): if libc_ver: info_add('platform.libc_ver', libc_ver) + try: + os_release = platform.freedesktop_os_release() + except OSError: + pass + else: + for key in ( + 'ID', + 'NAME', + 'PRETTY_NAME' + 'VARIANT', + 'VARIANT_ID', + 'VERSION', + 'VERSION_CODENAME', + 'VERSION_ID', + ): + if key not in os_release: + continue + info_add(f'platform.freedesktop_os_release[{key}]', + os_release[key]) + def collect_locale(info_add): import locale @@ -921,7 +941,6 @@ def dump_info(info, file=None): for key, value in infos: value = value.replace("\n", " ") print("%s: %s" % (key, value)) - print() def main(): @@ -930,6 +949,7 @@ def main(): dump_info(info) if error: + print() print("Collection failed: exit with error", file=sys.stderr) sys.exit(1) From f6d70e9a18873759ea1a68d749a79c3c11383a4d Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 7 Sep 2023 02:32:28 -0700 Subject: [PATCH 271/632] [3.11] bpo-38157: Add example about per file output for mock_open. (GH-16090) (#109072) Co-authored-by: Karthikeyan Singaravelan Co-authored-by: Stanley <46876382+slateny@users.noreply.github.com> Co-authored-by: Jelle Zijlstra Co-authored-by: Hugo van Kemenade --- Doc/library/unittest.mock-examples.rst | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Doc/library/unittest.mock-examples.rst b/Doc/library/unittest.mock-examples.rst index 744fc9de63cd16..34f343ebacdbb7 100644 --- a/Doc/library/unittest.mock-examples.rst +++ b/Doc/library/unittest.mock-examples.rst @@ -360,6 +360,30 @@ of arbitrary attributes as well as the getting of them then you can use *spec_set* instead of *spec*. +Using side_effect to return per file content +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:func:`mock_open` is used to patch :func:`open` method. :attr:`~Mock.side_effect` +can be used to return a new Mock object per call. This can be used to return different +contents per file stored in a dictionary:: + + DEFAULT = "default" + data_dict = {"file1": "data1", + "file2": "data2"} + + def open_side_effect(name): + return mock_open(read_data=data_dict.get(name, DEFAULT))() + + with patch("builtins.open", side_effect=open_side_effect): + with open("file1") as file1: + assert file1.read() == "data1" + + with open("file2") as file2: + assert file2.read() == "data2" + + with open("file3") as file2: + assert file2.read() == "default" + Patch Decorators ---------------- From cccb81d29427e4e33169d2ca480ce2fc11df043c Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 7 Sep 2023 03:44:35 -0700 Subject: [PATCH 272/632] [3.11] socket documentation fix - rename triple to 3-tuple (GH-24722) (#109074) Co-authored-by: Ori Hoch Co-authored-by: Hugo van Kemenade --- Doc/library/socket.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index 2e129175636f27..36151ca650e52e 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -880,7 +880,7 @@ The :mod:`socket` module also offers various network-related services: .. function:: gethostbyname_ex(hostname) Translate a host name to IPv4 address format, extended interface. Return a - triple ``(hostname, aliaslist, ipaddrlist)`` where *hostname* is the host's + 3-tuple ``(hostname, aliaslist, ipaddrlist)`` where *hostname* is the host's primary host name, *aliaslist* is a (possibly empty) list of alternative host names for the same address, and *ipaddrlist* is a list of IPv4 addresses for the same interface on the same host (often but not @@ -908,7 +908,7 @@ The :mod:`socket` module also offers various network-related services: .. function:: gethostbyaddr(ip_address) - Return a triple ``(hostname, aliaslist, ipaddrlist)`` where *hostname* is the + Return a 3-tuple ``(hostname, aliaslist, ipaddrlist)`` where *hostname* is the primary host name responding to the given *ip_address*, *aliaslist* is a (possibly empty) list of alternative host names for the same address, and *ipaddrlist* is a list of IPv4/v6 addresses for the same interface on the same From 417a7b5b10c8b994dd41fc314404803fe1811421 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 7 Sep 2023 06:23:08 -0700 Subject: [PATCH 273/632] [3.11] GH-90915: Document that SystemExit doesn't trigger sys.excepthook (GH-31357) (#109083) Co-authored-by: Colin Watson Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> Co-authored-by: Hugo van Kemenade --- Doc/library/sys.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index ecad0ed881da45..ab198732a427ea 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -374,7 +374,7 @@ always available. This function prints out a given traceback and exception to ``sys.stderr``. - When an exception is raised and uncaught, the interpreter calls + When an exception other than :exc:`SystemExit` is raised and uncaught, the interpreter calls ``sys.excepthook`` with three arguments, the exception class, exception instance, and a traceback object. In an interactive session this happens just before control is returned to the prompt; in a Python program this happens just From 297006b6f826b9801da60b8322410d32c33c38e8 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 7 Sep 2023 07:03:20 -0700 Subject: [PATCH 274/632] [3.11] gh-71770: Add more details on behavior of configparser's default_section (GH-31562) (#109089) Co-authored-by: Stanley <46876382+slateny@users.noreply.github.com> Co-authored-by: Hugo van Kemenade --- Doc/library/configparser.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Doc/library/configparser.rst b/Doc/library/configparser.rst index 846eb57f599412..819c3799b08da7 100644 --- a/Doc/library/configparser.rst +++ b/Doc/library/configparser.rst @@ -935,8 +935,10 @@ ConfigParser Objects When *default_section* is given, it specifies the name for the special section holding default values for other sections and interpolation purposes - (normally named ``"DEFAULT"``). This value can be retrieved and changed on - runtime using the ``default_section`` instance attribute. + (normally named ``"DEFAULT"``). This value can be retrieved and changed at + runtime using the ``default_section`` instance attribute. This won't + re-evaluate an already parsed config file, but will be used when writing + parsed settings to a new config file. Interpolation behaviour may be customized by providing a custom handler through the *interpolation* argument. ``None`` can be used to turn off From 12f40a43408ddd58f8f6c81d68a17461ea7b0c84 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 7 Sep 2023 07:13:42 -0700 Subject: [PATCH 275/632] [3.11] gh-103186: Remove debug print in test_sys_settrace (GH-109077) (GH-109085) (cherry picked from commit e4bb0026b9a21d066e7a5c4716ea4d755b95d2d5) Co-authored-by: Serhiy Storchaka --- Lib/test/test_sys_settrace.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/test/test_sys_settrace.py b/Lib/test/test_sys_settrace.py index 14781bbb8622fa..2f9e7b8bc43f18 100644 --- a/Lib/test/test_sys_settrace.py +++ b/Lib/test/test_sys_settrace.py @@ -1588,7 +1588,6 @@ def error_once(frame, event, arg): except Exception as ex: count = 0 tb = ex.__traceback__ - print(tb) while tb: if tb.tb_frame.f_code.co_name == "test_settrace_error": count += 1 From 49cc2e798419bb291bc3f2e141a0f5e98dfa6867 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 7 Sep 2023 17:16:13 +0300 Subject: [PATCH 276/632] [3.11] gh-103186: Suppress RuntimeWarning about unclosed async iterator in test_sys_settrace (GH-109075) (GH-109086) (cherry picked from commit d485551c9d1792ff3539eef1d6374bd4c01dcd5d) --- Lib/test/test_sys_settrace.py | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_sys_settrace.py b/Lib/test/test_sys_settrace.py index 2f9e7b8bc43f18..3540192b5bb5bb 100644 --- a/Lib/test/test_sys_settrace.py +++ b/Lib/test/test_sys_settrace.py @@ -41,6 +41,20 @@ async def asynciter(iterable): for x in iterable: yield x +def clean_asynciter(test): + @wraps(test) + async def wrapper(*args, **kwargs): + cleanups = [] + def wrapped_asynciter(iterable): + it = asynciter(iterable) + cleanups.append(it.aclose) + return it + try: + return await test(*args, **kwargs, asynciter=wrapped_asynciter) + finally: + while cleanups: + await cleanups.pop()() + return wrapper # A very basic example. If this fails, we're in deep trouble. def basic(): @@ -1868,7 +1882,11 @@ def compare_jump_output(self, expected, received): def run_test(self, func, jumpFrom, jumpTo, expected, error=None, event='line', decorated=False): - tracer = JumpTracer(func, jumpFrom, jumpTo, event, decorated) + wrapped = func + while hasattr(wrapped, '__wrapped__'): + wrapped = wrapped.__wrapped__ + + tracer = JumpTracer(wrapped, jumpFrom, jumpTo, event, decorated) sys.settrace(tracer.trace) output = [] if error is None: @@ -1881,7 +1899,11 @@ def run_test(self, func, jumpFrom, jumpTo, expected, error=None, def run_async_test(self, func, jumpFrom, jumpTo, expected, error=None, event='line', decorated=False): - tracer = JumpTracer(func, jumpFrom, jumpTo, event, decorated) + wrapped = func + while hasattr(wrapped, '__wrapped__'): + wrapped = wrapped.__wrapped__ + + tracer = JumpTracer(wrapped, jumpFrom, jumpTo, event, decorated) sys.settrace(tracer.trace) output = [] if error is None: @@ -1949,7 +1971,8 @@ def test_jump_out_of_block_backwards(output): output.append(7) @async_jump_test(4, 5, [3, 5]) - async def test_jump_out_of_async_for_block_forwards(output): + @clean_asynciter + async def test_jump_out_of_async_for_block_forwards(output, asynciter): for i in [1]: async for i in asynciter([1, 2]): output.append(3) @@ -1957,7 +1980,8 @@ async def test_jump_out_of_async_for_block_forwards(output): output.append(5) @async_jump_test(5, 2, [2, 4, 2, 4, 5, 6]) - async def test_jump_out_of_async_for_block_backwards(output): + @clean_asynciter + async def test_jump_out_of_async_for_block_backwards(output, asynciter): for i in [1]: output.append(2) async for i in asynciter([1]): From d9d64a4a1397c9e3256984c66a12d92da0dfcd30 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 7 Sep 2023 07:34:30 -0700 Subject: [PATCH 277/632] [3.11] gh-102823: Document return type of floor division on floats (GH-102824) (#109093) Co-authored-by: Mark Dickinson Co-authored-by: Hugo van Kemenade --- Doc/library/stdtypes.rst | 8 +++++--- .../2023-03-19-09-39-31.gh-issue-102823.OzsOz0.rst | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Documentation/2023-03-19-09-39-31.gh-issue-102823.OzsOz0.rst diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 6158911fb28a7a..6ba2dd0930ed7d 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -282,7 +282,7 @@ the operations, see :ref:`operator-summary`): +---------------------+---------------------------------+---------+--------------------+ | ``x / y`` | quotient of *x* and *y* | | | +---------------------+---------------------------------+---------+--------------------+ -| ``x // y`` | floored quotient of *x* and | \(1) | | +| ``x // y`` | floored quotient of *x* and | \(1)\(2)| | | | *y* | | | +---------------------+---------------------------------+---------+--------------------+ | ``x % y`` | remainder of ``x / y`` | \(2) | | @@ -319,8 +319,10 @@ the operations, see :ref:`operator-summary`): Notes: (1) - Also referred to as integer division. The resultant value is a whole - integer, though the result's type is not necessarily int. The result is + Also referred to as integer division. For operands of type :class:`int`, + the result has type :class:`int`. For operands of type :class:`float`, + the result has type :class:`float`. In general, the result is a whole + integer, though the result's type is not necessarily :class:`int`. The result is always rounded towards minus infinity: ``1//2`` is ``0``, ``(-1)//2`` is ``-1``, ``1//(-2)`` is ``-1``, and ``(-1)//(-2)`` is ``0``. diff --git a/Misc/NEWS.d/next/Documentation/2023-03-19-09-39-31.gh-issue-102823.OzsOz0.rst b/Misc/NEWS.d/next/Documentation/2023-03-19-09-39-31.gh-issue-102823.OzsOz0.rst new file mode 100644 index 00000000000000..1e32f3c89231c8 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2023-03-19-09-39-31.gh-issue-102823.OzsOz0.rst @@ -0,0 +1,2 @@ +Document the return type of ``x // y`` when ``x`` and ``y`` have type +:class:`float`. From dae62d456e4f2b956e7e4dd538698bec97aec903 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 7 Sep 2023 17:54:07 +0300 Subject: [PATCH 278/632] [3.11] gh-88943: Improve syntax error for non-ASCII character that follows a numerical literal (GH-109081) (GH-109091) It now points on the invalid non-ASCII character, not on the valid numerical literal. (cherry picked from commit b2729e93e9d73503b1fda4ea4fecd77c58909091) --- Lib/test/test_grammar.py | 4 ++++ .../2023-09-07-16-05-36.gh-issue-88943.rH_X3W.rst | 3 +++ Parser/tokenizer.c | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-09-07-16-05-36.gh-issue-88943.rH_X3W.rst diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py index 38e98beeb94c5e..f1f9b5c0075100 100644 --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -238,6 +238,10 @@ def check(test, error=False): check(f"[{num}for x in ()]") check(f"{num}spam", error=True) + # gh-88943: Invalid non-ASCII character following a numerical literal. + with self.assertRaisesRegex(SyntaxError, r"invalid character '⁄' \(U\+2044\)"): + compile(f"{num}⁄7", "", "eval") + with warnings.catch_warnings(): warnings.filterwarnings('ignore', '"is" with a literal', SyntaxWarning) diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-09-07-16-05-36.gh-issue-88943.rH_X3W.rst b/Misc/NEWS.d/next/Core and Builtins/2023-09-07-16-05-36.gh-issue-88943.rH_X3W.rst new file mode 100644 index 00000000000000..a99830fe4227c9 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-09-07-16-05-36.gh-issue-88943.rH_X3W.rst @@ -0,0 +1,3 @@ +Improve syntax error for non-ASCII character that follows a numerical +literal. It now points on the invalid non-ASCII character, not on the valid +numerical literal. diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c index b552b4171888f5..7fc8a585621d3a 100644 --- a/Parser/tokenizer.c +++ b/Parser/tokenizer.c @@ -1303,7 +1303,7 @@ verify_end_of_number(struct tok_state *tok, int c, const char *kind) tok_nextc(tok); } else /* In future releases, only error will remain. */ - if (is_potential_identifier_char(c)) { + if (c < 128 && is_potential_identifier_char(c)) { tok_backup(tok, c); syntaxerror(tok, "invalid %s literal", kind); return 0; From c2b8d4fe0d785358902487fb8abcb81351da1b90 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 7 Sep 2023 11:55:50 -0700 Subject: [PATCH 279/632] [3.11] gh-75743: Restore test_timeout.testConnectTimeout() (GH-109087) (#109102) gh-75743: Restore test_timeout.testConnectTimeout() (GH-109087) This un-skips this test now that pythontest.net implements appropriate firewall rules for it. (cherry picked from commit 1829a3c9a3712b6a68a3a449e4a08787c73da51d) Co-authored-by: Ee Durbin --- Lib/test/test_timeout.py | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/Lib/test/test_timeout.py b/Lib/test/test_timeout.py index fa85c7e6cd87d5..b4b0dd09a41b99 100644 --- a/Lib/test/test_timeout.py +++ b/Lib/test/test_timeout.py @@ -148,13 +148,12 @@ def setUp(self): def tearDown(self): self.sock.close() - @unittest.skipIf(True, 'need to replace these hosts; see bpo-35518') def testConnectTimeout(self): # Testing connect timeout is tricky: we need to have IP connectivity # to a host that silently drops our packets. We can't simulate this # from Python because it's a function of the underlying TCP/IP stack. - # So, the following Snakebite host has been defined: - blackhole = resolve_address('blackhole.snakebite.net', 56666) + # So, the following port on the pythontest.net host has been defined: + blackhole = resolve_address('pythontest.net', 56666) # Blackhole has been configured to silently drop any incoming packets. # No RSTs (for TCP) or ICMP UNREACH (for UDP/ICMP) will be sent back @@ -166,7 +165,7 @@ def testConnectTimeout(self): # to firewalling or general network configuration. In order to improve # our confidence in testing the blackhole, a corresponding 'whitehole' # has also been set up using one port higher: - whitehole = resolve_address('whitehole.snakebite.net', 56667) + whitehole = resolve_address('pythontest.net', 56667) # This address has been configured to immediately drop any incoming # packets as well, but it does it respectfully with regards to the @@ -180,20 +179,15 @@ def testConnectTimeout(self): # timeframe). # For the records, the whitehole/blackhole configuration has been set - # up using the 'pf' firewall (available on BSDs), using the following: + # up using the 'iptables' firewall, using the following rules: # - # ext_if="bge0" - # - # blackhole_ip="35.8.247.6" - # whitehole_ip="35.8.247.6" - # blackhole_port="56666" - # whitehole_port="56667" - # - # block return in log quick on $ext_if proto { tcp udp } \ - # from any to $whitehole_ip port $whitehole_port - # block drop in log quick on $ext_if proto { tcp udp } \ - # from any to $blackhole_ip port $blackhole_port + # -A INPUT -p tcp --destination-port 56666 -j DROP + # -A INPUT -p udp --destination-port 56666 -j DROP + # -A INPUT -p tcp --destination-port 56667 -j REJECT + # -A INPUT -p udp --destination-port 56667 -j REJECT # + # See https://github.com/python/psf-salt/blob/main/pillar/base/firewall/snakebite.sls + # for the current configuration. skip = True sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) From e46be0d2fa18d7e3435c3b19370604043b1f8cf0 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 7 Sep 2023 12:10:19 -0700 Subject: [PATCH 280/632] [3.11] gh-68403: Fix test_coverage in test_trace (GH-108910) (GH-109105) Its behavior no longer affected by test running options such as -m. (cherry picked from commit 7e1a7abb9831965cdec477e62dbe4f8415b8a582) Co-authored-by: Serhiy Storchaka --- Lib/test/test_trace.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_trace.py b/Lib/test/test_trace.py index 94b314381bfe21..8be89eb0245bb7 100644 --- a/Lib/test/test_trace.py +++ b/Lib/test/test_trace.py @@ -362,9 +362,14 @@ def tearDown(self): rmtree(TESTFN) unlink(TESTFN) - def _coverage(self, tracer, - cmd='import test.support, test.test_pprint;' - 'test.support.run_unittest(test.test_pprint.QueryTestCase)'): + DEFAULT_SCRIPT = '''if True: + import unittest + from test.test_pprint import QueryTestCase + loader = unittest.TestLoader() + tests = loader.loadTestsFromTestCase(QueryTestCase) + tests(unittest.TestResult()) + ''' + def _coverage(self, tracer, cmd=DEFAULT_SCRIPT): tracer.run(cmd) r = tracer.results() r.write_results(show_missing=True, summary=True, coverdir=TESTFN) From effa2ecdcf0d019d6ed04ca160f789af57e959de Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Thu, 7 Sep 2023 18:57:48 -0700 Subject: [PATCH 281/632] [3.11] gh-108682: [Enum] raise TypeError if super().__new__ called in custom __new__ (GH-108704) (GH-108739) When overriding the `__new__` method of an enum, the underlying data type should be created directly; i.e. . member = object.__new__(cls) member = int.__new__(cls, value) member = str.__new__(cls, value) Calling `super().__new__()` finds the lookup version of `Enum.__new__`, and will now raise an exception when detected. (cherry picked from commit d48760b2f1e28dd3c1a35721939f400a8ab619b8) --- Doc/howto/enum.rst | 23 ++++++++++++++++++- Lib/enum.py | 7 ++++++ Lib/test/test_enum.py | 11 +++++++++ ...-08-30-20-10-28.gh-issue-108682.c2gzLQ.rst | 2 ++ 4 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2023-08-30-20-10-28.gh-issue-108682.c2gzLQ.rst diff --git a/Doc/howto/enum.rst b/Doc/howto/enum.rst index e9049440d23686..465be653b51adb 100644 --- a/Doc/howto/enum.rst +++ b/Doc/howto/enum.rst @@ -422,10 +422,17 @@ enumeration, with the exception of special methods (:meth:`__str__`, :meth:`__add__`, etc.), descriptors (methods are also descriptors), and variable names listed in :attr:`_ignore_`. -Note: if your enumeration defines :meth:`__new__` and/or :meth:`__init__` then +Note: if your enumeration defines :meth:`__new__` and/or :meth:`__init__`, any value(s) given to the enum member will be passed into those methods. See `Planet`_ for an example. +.. note:: + + The :meth:`__new__` method, if defined, is used during creation of the Enum + members; it is then replaced by Enum's :meth:`__new__` which is used after + class creation for lookup of existing members. See :ref:`new-vs-init` for + more details. + Restricted Enum subclassing --------------------------- @@ -860,6 +867,8 @@ Some rules: :meth:`__str__` method has been reset to their data types' :meth:`__str__` method. +.. _new-vs-init: + When to use :meth:`__new__` vs. :meth:`__init__` ------------------------------------------------ @@ -892,6 +901,11 @@ want one of them to be the value:: >>> print(Coordinate(3)) Coordinate.VY +.. warning:: + + *Do not* call ``super().__new__()``, as the lookup-only ``__new__`` is the one + that is found; instead, use the data type directly. + Finer Points ^^^^^^^^^^^^ @@ -1316,6 +1330,13 @@ to handle any extra arguments:: members; it is then replaced by Enum's :meth:`__new__` which is used after class creation for lookup of existing members. +.. warning:: + + *Do not* call ``super().__new__()``, as the lookup-only ``__new__`` is the one + that is found; instead, use the data type directly -- e.g.:: + + obj = int.__new__(cls, value) + OrderedEnum ^^^^^^^^^^^ diff --git a/Lib/enum.py b/Lib/enum.py index 1f447c878c1f84..155cb13022fa16 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -863,6 +863,8 @@ def _create_(cls, class_name, names, *, module=None, qualname=None, type=None, s value = first_enum._generate_next_value_(name, start, count, last_values[:]) last_values.append(value) names.append((name, value)) + if names is None: + names = () # Here, names is either an iterable of (name, value) or a mapping. for item in names: @@ -1107,6 +1109,11 @@ def __new__(cls, value): for member in cls._member_map_.values(): if member._value_ == value: return member + # 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_: + raise TypeError("%r has no members defined" % cls) + # # still not found -- try _missing_ hook try: exc = None diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index cc66875bde02a8..ed1c3a59ce479c 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -322,6 +322,17 @@ def spam(cls): with self.assertRaises(AttributeError): del Season.SPRING.name + def test_bad_new_super(self): + with self.assertRaisesRegex( + TypeError, + 'has no members defined', + ): + class BadSuper(self.enum_type): + def __new__(cls, value): + obj = super().__new__(cls, value) + return obj + failed = 1 + def test_basics(self): TE = self.MainEnum if self.is_flag: diff --git a/Misc/NEWS.d/next/Library/2023-08-30-20-10-28.gh-issue-108682.c2gzLQ.rst b/Misc/NEWS.d/next/Library/2023-08-30-20-10-28.gh-issue-108682.c2gzLQ.rst new file mode 100644 index 00000000000000..148d4329142740 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-08-30-20-10-28.gh-issue-108682.c2gzLQ.rst @@ -0,0 +1,2 @@ +Enum: raise :exc:`TypeError` if ``super().__new__()`` is called from a +custom ``__new__``. From 9dd28d2da941160abf0b5f54b1e7e3a7ef4465fb Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 8 Sep 2023 00:07:54 -0700 Subject: [PATCH 282/632] [3.11] gh-108962: Skip test_tempfile.test_flags() if not supported (GH-108964) (#108968) gh-108962: Skip test_tempfile.test_flags() if not supported (GH-108964) Skip test_tempfile.test_flags() if chflags() fails with "OSError: [Errno 45] Operation not supported" (ex: on FreeBSD 13). (cherry picked from commit cd2ef21b076b494224985e266c5f5f8b37c66618) Co-authored-by: Victor Stinner --- Lib/test/test_tempfile.py | 18 +++++++++++++++++- ...3-09-05-23-00-09.gh-issue-108962.R4NwuU.rst | 3 +++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Tests/2023-09-05-23-00-09.gh-issue-108962.R4NwuU.rst diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py index ccf7ea072de276..2632e77fc82dec 100644 --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py @@ -1726,9 +1726,25 @@ def test_modes(self): d.cleanup() self.assertFalse(os.path.exists(d.name)) - @unittest.skipUnless(hasattr(os, 'chflags'), 'requires os.lchflags') + @unittest.skipUnless(hasattr(os, 'chflags'), 'requires os.chflags') def test_flags(self): flags = stat.UF_IMMUTABLE | stat.UF_NOUNLINK + + # skip the test if these flags are not supported (ex: FreeBSD 13) + filename = os_helper.TESTFN + try: + open(filename, "w").close() + try: + 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}") + else: + os.chflags(filename, 0) + finally: + os_helper.unlink(filename) + 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/Tests/2023-09-05-23-00-09.gh-issue-108962.R4NwuU.rst b/Misc/NEWS.d/next/Tests/2023-09-05-23-00-09.gh-issue-108962.R4NwuU.rst new file mode 100644 index 00000000000000..380fb20b8881b2 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-09-05-23-00-09.gh-issue-108962.R4NwuU.rst @@ -0,0 +1,3 @@ +Skip ``test_tempfile.test_flags()`` if ``chflags()`` fails with "OSError: +[Errno 45] Operation not supported" (ex: on FreeBSD 13). Patch by Victor +Stinner. From b55cf2c2d82d7567720df113117752841ce74606 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 8 Sep 2023 13:10:33 +0200 Subject: [PATCH 283/632] [3.11] gh-104690: thread_run() checks for tstate dangling pointer (#109056) (#109134) gh-104690: thread_run() checks for tstate dangling pointer (#109056) thread_run() of _threadmodule.c now calls _PyThreadState_CheckConsistency() to check if tstate is a dangling pointer when Python is built in debug mode. Rename ceval_gil.c is_tstate_valid() to _PyThreadState_CheckConsistency() to reuse it in _threadmodule.c. (cherry picked from commit f63d37877ad166041489a968233b57540f8456e8) --- Include/internal/pycore_pystate.h | 4 ++++ Modules/_threadmodule.c | 7 +++++-- Python/ceval.c | 26 ++++++-------------------- Python/ceval_gil.h | 8 ++++---- Python/pystate.c | 18 ++++++++++++++++++ 5 files changed, 37 insertions(+), 26 deletions(-) diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h index cb8f115768a352..8696aaf26724b8 100644 --- a/Include/internal/pycore_pystate.h +++ b/Include/internal/pycore_pystate.h @@ -61,6 +61,10 @@ _Py_ThreadCanHandlePendingCalls(void) } +#ifndef NDEBUG +extern int _PyThreadState_CheckConsistency(PyThreadState *tstate); +#endif + /* Variable and macro for in-line access to current thread and interpreter state */ diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index 199e3b89d99213..b70325a2b693c4 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -1076,9 +1076,12 @@ static void thread_run(void *boot_raw) { struct bootstate *boot = (struct bootstate *) boot_raw; - PyThreadState *tstate; + PyThreadState *tstate = boot->tstate; + + // gh-104690: If Python is being finalized and PyInterpreterState_Delete() + // was called, tstate becomes a dangling pointer. + assert(_PyThreadState_CheckConsistency(tstate)); - tstate = boot->tstate; tstate->thread_id = PyThread_get_thread_ident(); #ifdef PY_HAVE_THREAD_NATIVE_ID tstate->native_thread_id = PyThread_get_thread_native_id(); diff --git a/Python/ceval.c b/Python/ceval.c index 47df353197094b..df11de084d2d72 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -216,20 +216,6 @@ _PyEvalFrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame *frame); "cannot access free variable '%s' where it is not associated with a" \ " value in enclosing scope" -#ifndef NDEBUG -/* Ensure that tstate is valid: sanity check for PyEval_AcquireThread() and - PyEval_RestoreThread(). Detect if tstate memory was freed. It can happen - when a thread continues to run after Python finalization, especially - daemon threads. */ -static int -is_tstate_valid(PyThreadState *tstate) -{ - assert(!_PyMem_IsPtrFreed(tstate)); - assert(!_PyMem_IsPtrFreed(tstate->interp)); - return 1; -} -#endif - /* This can set eval_breaker to 0 even though gil_drop_request became 1. We believe this is all right because the eval loop will release @@ -464,7 +450,7 @@ PyEval_AcquireThread(PyThreadState *tstate) void PyEval_ReleaseThread(PyThreadState *tstate) { - assert(is_tstate_valid(tstate)); + assert(_PyThreadState_CheckConsistency(tstate)); _PyRuntimeState *runtime = tstate->interp->runtime; PyThreadState *new_tstate = _PyThreadState_Swap(&runtime->gilstate, NULL); @@ -671,7 +657,7 @@ Py_AddPendingCall(int (*func)(void *), void *arg) static int handle_signals(PyThreadState *tstate) { - assert(is_tstate_valid(tstate)); + assert(_PyThreadState_CheckConsistency(tstate)); if (!_Py_ThreadCanHandleSignals(tstate->interp)) { return 0; } @@ -739,7 +725,7 @@ void _Py_FinishPendingCalls(PyThreadState *tstate) { assert(PyGILState_Check()); - assert(is_tstate_valid(tstate)); + assert(_PyThreadState_CheckConsistency(tstate)); struct _pending_calls *pending = &tstate->interp->ceval.pending; @@ -764,7 +750,7 @@ Py_MakePendingCalls(void) assert(PyGILState_Check()); PyThreadState *tstate = _PyThreadState_GET(); - assert(is_tstate_valid(tstate)); + assert(_PyThreadState_CheckConsistency(tstate)); /* Python signal handler doesn't really queue a callback: it only signals that a signal was received, see _PyEval_SignalReceived(). */ @@ -6947,7 +6933,7 @@ maybe_call_line_trace(Py_tracefunc func, PyObject *obj, int _PyEval_SetProfile(PyThreadState *tstate, Py_tracefunc func, PyObject *arg) { - assert(is_tstate_valid(tstate)); + assert(_PyThreadState_CheckConsistency(tstate)); /* The caller must hold the GIL */ assert(PyGILState_Check()); @@ -6999,7 +6985,7 @@ PyEval_SetProfile(Py_tracefunc func, PyObject *arg) int _PyEval_SetTrace(PyThreadState *tstate, Py_tracefunc func, PyObject *arg) { - assert(is_tstate_valid(tstate)); + assert(_PyThreadState_CheckConsistency(tstate)); /* The caller must hold the GIL */ assert(PyGILState_Check()); diff --git a/Python/ceval_gil.h b/Python/ceval_gil.h index 476ed7f1a2dd4b..d20af2690d1845 100644 --- a/Python/ceval_gil.h +++ b/Python/ceval_gil.h @@ -171,7 +171,7 @@ drop_gil(struct _ceval_runtime_state *ceval, struct _ceval_state *ceval2, /* Not switched yet => wait */ if (((PyThreadState*)_Py_atomic_load_relaxed(&gil->last_holder)) == tstate) { - assert(is_tstate_valid(tstate)); + assert(_PyThreadState_CheckConsistency(tstate)); RESET_GIL_DROP_REQUEST(tstate->interp); /* NOTE: if COND_WAIT does not atomically start waiting when releasing the mutex, another thread can run through, take @@ -226,7 +226,7 @@ take_gil(PyThreadState *tstate) PyThread_exit_thread(); } - assert(is_tstate_valid(tstate)); + assert(_PyThreadState_CheckConsistency(tstate)); PyInterpreterState *interp = tstate->interp; struct _ceval_runtime_state *ceval = &interp->runtime->ceval; struct _ceval_state *ceval2 = &interp->ceval; @@ -268,7 +268,7 @@ take_gil(PyThreadState *tstate) } PyThread_exit_thread(); } - assert(is_tstate_valid(tstate)); + assert(_PyThreadState_CheckConsistency(tstate)); SET_GIL_DROP_REQUEST(interp); drop_requested = 1; @@ -307,7 +307,7 @@ take_gil(PyThreadState *tstate) drop_gil(ceval, ceval2, tstate); PyThread_exit_thread(); } - assert(is_tstate_valid(tstate)); + assert(_PyThreadState_CheckConsistency(tstate)); if (_Py_atomic_load_relaxed(&ceval2->gil_drop_request)) { RESET_GIL_DROP_REQUEST(interp); diff --git a/Python/pystate.c b/Python/pystate.c index dfca3f5fd71376..ec278eef836c30 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -2237,6 +2237,24 @@ _PyThreadState_PopFrame(PyThreadState *tstate, _PyInterpreterFrame * frame) } +#ifndef NDEBUG +// Check that a Python thread state valid. In practice, this function is used +// on a Python debug build to check if 'tstate' is a dangling pointer, if the +// PyThreadState memory has been freed. +// +// Usage: +// +// assert(_PyThreadState_CheckConsistency(tstate)); +int +_PyThreadState_CheckConsistency(PyThreadState *tstate) +{ + assert(!_PyMem_IsPtrFreed(tstate)); + assert(!_PyMem_IsPtrFreed(tstate->interp)); + return 1; +} +#endif + + #ifdef __cplusplus } #endif From c1a2ef5efceda2705919ee0233ff657575909ce9 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 8 Sep 2023 09:56:10 -0700 Subject: [PATCH 284/632] [3.11] gh-106922: Fix error location for constructs with spaces and parentheses (GH-108959) (#109148) Co-authored-by: Pablo Galindo Salgado --- Lib/test/test_traceback.py | 36 +++++++++++++++++++ Lib/traceback.py | 16 +++++++-- ...-09-05-20-52-17.gh-issue-108959.6z45Sy.rst | 2 ++ Python/traceback.c | 17 +++++++++ 4 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-09-05-20-52-17.gh-issue-108959.6z45Sy.rst diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index eadc9c440e32cc..544b24c01b37d8 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -566,6 +566,24 @@ 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_binary_operators_with_spaces_and_parenthesis(self): + def f_with_binary_operator(): + a = 1 + 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' + f' File "{__file__}", line {lineno_f+3}, in f_with_binary_operator\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_subscript(self): def f_with_subscript(): some_dict = {'x': {'y': None}} @@ -600,6 +618,24 @@ def f_with_subscript(): result_lines = self.get_exception(f_with_subscript) self.assertEqual(result_lines, expected_error.splitlines()) + def test_caret_for_subscript_with_spaces_and_parenthesis(self): + def f_with_binary_operator(): + a = [] + b = c = 1 + return b [ a ] + c + + 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' + f' File "{__file__}", line {lineno_f+3}, in f_with_binary_operator\n' + ' return b [ a ] + c\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") diff --git a/Lib/traceback.py b/Lib/traceback.py index 09950a8b55e33e..d3edd3a63efed9 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -603,11 +603,21 @@ def _extract_caret_anchors_from_line_segment(segment): and not operator_str[operator_offset + 1].isspace() ): right_anchor += 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)) case ast.Subscript(): - subscript_start = normalize(expr.value.end_col_offset) - subscript_end = normalize(expr.slice.end_col_offset + 1) - return _Anchors(subscript_start, subscript_end) + 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) return None diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-09-05-20-52-17.gh-issue-108959.6z45Sy.rst b/Misc/NEWS.d/next/Core and Builtins/2023-09-05-20-52-17.gh-issue-108959.6z45Sy.rst new file mode 100644 index 00000000000000..792bbc454f2b27 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-09-05-20-52-17.gh-issue-108959.6z45Sy.rst @@ -0,0 +1,2 @@ +Fix caret placement for error locations for subscript and binary operations +that involve non-semantic parentheses and spaces. Patch by Pablo Galindo diff --git a/Python/traceback.c b/Python/traceback.c index 7f47349a27a1ae..7010736ad0c539 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -621,6 +621,11 @@ extract_anchors_from_expr(const char *segment_str, expr_ty expr, Py_ssize_t *lef ++*right_anchor; } + // Keep going if the current char is not ')' + if (i+1 < right->col_offset && (segment_str[i] == ')')) { + continue; + } + // Set the error characters *primary_error_char = "~"; *secondary_error_char = "^"; @@ -631,6 +636,18 @@ extract_anchors_from_expr(const char *segment_str, expr_ty expr, Py_ssize_t *lef case Subscript_kind: { *left_anchor = expr->v.Subscript.value->end_col_offset; *right_anchor = expr->v.Subscript.slice->end_col_offset + 1; + Py_ssize_t str_len = strlen(segment_str); + + // Move right_anchor and left_anchor forward to the first non-whitespace character that is not ']' and '[' + while (*left_anchor < str_len && (IS_WHITESPACE(segment_str[*left_anchor]) || segment_str[*left_anchor] != '[')) { + ++*left_anchor; + } + while (*right_anchor < str_len && (IS_WHITESPACE(segment_str[*right_anchor]) || segment_str[*right_anchor] != ']')) { + ++*right_anchor; + } + if (*right_anchor < str_len){ + *right_anchor += 1; + } // Set the error characters *primary_error_char = "~"; From 6b2f44ea7860bc9957e1f48ecddedff7a973876d Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 8 Sep 2023 10:02:57 -0700 Subject: [PATCH 285/632] [3.11] GH-109067: fix randomly failing `test_async_gen_asyncio_gc_aclose_09` test (GH-109142) (#109150) GH-109067: fix randomly failing `test_async_gen_asyncio_gc_aclose_09` test (GH-109142) Use `asyncio.sleep(0)` instead of short sleeps. (cherry picked from commit ccd48623d4860e730a16f3f252d67bfea8c1e905) Co-authored-by: Kumar Aditya --- Lib/test/test_asyncgen.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_asyncgen.py b/Lib/test/test_asyncgen.py index fb22f411c2e296..2241b39f3b1d67 100644 --- a/Lib/test/test_asyncgen.py +++ b/Lib/test/test_asyncgen.py @@ -1037,8 +1037,7 @@ async def gen(): while True: yield 1 finally: - await asyncio.sleep(0.01) - await asyncio.sleep(0.01) + await asyncio.sleep(0) DONE = 1 async def run(): @@ -1048,7 +1047,10 @@ async def run(): del g gc_collect() # For PyPy or other GCs. - await asyncio.sleep(0.1) + # Starts running the aclose task + await asyncio.sleep(0) + # For asyncio.sleep(0) in finally block + await asyncio.sleep(0) self.loop.run_until_complete(run()) self.assertEqual(DONE, 1) From 50e4143f8dbbb6fb054a817df3331aea18773116 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 8 Sep 2023 22:28:18 +0300 Subject: [PATCH 286/632] [3.11] Check the result of PySet_Contains() for error in Python/symtable.c (GH-109146) (GH-109158) (cherry picked from commit 87a7faf6b68c8076e640a9a1347a255f132d8382) --- Python/symtable.c | 52 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/Python/symtable.c b/Python/symtable.c index 0b259b08b61f97..afe05c71b97d9d 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -502,6 +502,7 @@ analyze_name(PySTEntryObject *ste, PyObject *scopes, PyObject *name, long flags, PyObject *bound, PyObject *local, PyObject *free, PyObject *global) { + int contains; if (flags & DEF_GLOBAL) { if (flags & DEF_NONLOCAL) { PyErr_Format(PyExc_SyntaxError, @@ -522,7 +523,11 @@ analyze_name(PySTEntryObject *ste, PyObject *scopes, PyObject *name, long flags, "nonlocal declaration not allowed at module level"); return error_at_directive(ste, name); } - if (!PySet_Contains(bound, name)) { + contains = PySet_Contains(bound, name); + if (contains < 0) { + return 0; + } + if (!contains) { PyErr_Format(PyExc_SyntaxError, "no binding for nonlocal '%U' found", name); @@ -546,17 +551,29 @@ analyze_name(PySTEntryObject *ste, PyObject *scopes, PyObject *name, long flags, Note that having a non-NULL bound implies that the block is nested. */ - if (bound && PySet_Contains(bound, name)) { - SET_SCOPE(scopes, name, FREE); - ste->ste_free = 1; - return PySet_Add(free, name) >= 0; + if (bound) { + contains = PySet_Contains(bound, name); + if (contains < 0) { + return 0; + } + if (contains) { + SET_SCOPE(scopes, name, FREE); + ste->ste_free = 1; + return PySet_Add(free, name) >= 0; + } } /* If a parent has a global statement, then call it global explicit? It could also be global implicit. */ - if (global && PySet_Contains(global, name)) { - SET_SCOPE(scopes, name, GLOBAL_IMPLICIT); - return 1; + if (global) { + contains = PySet_Contains(global, name); + if (contains < 0) { + return 0; + } + if (contains) { + SET_SCOPE(scopes, name, GLOBAL_IMPLICIT); + return 1; + } } if (ste->ste_nested) ste->ste_free = 1; @@ -590,8 +607,13 @@ analyze_cells(PyObject *scopes, PyObject *free) scope = PyLong_AS_LONG(v); if (scope != LOCAL) continue; - if (!PySet_Contains(free, name)) + int contains = PySet_Contains(free, name); + if (contains < 0) { + goto error; + } + if (!contains) { continue; + } /* Replace LOCAL with CELL for this name, and remove from free. It is safe to replace the value of name in the dict, because it will not cause a resize. @@ -691,9 +713,15 @@ update_symbols(PyObject *symbols, PyObject *scopes, goto error; } /* Handle global symbol */ - if (bound && !PySet_Contains(bound, name)) { - Py_DECREF(name); - continue; /* it's a global */ + if (bound) { + int contains = PySet_Contains(bound, name); + if (contains < 0) { + goto error; + } + if (!contains) { + Py_DECREF(name); + continue; /* it's a global */ + } } /* Propagate new free symbol up the lexical stack */ if (PyDict_SetItem(symbols, name, v_free) < 0) { From 77356f6f9607b1e9700f988912ad70ecaef024cc Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sat, 9 Sep 2023 03:34:18 -0600 Subject: [PATCH 287/632] [3.11] gh-107924: re-order os.sendfile() flag documentation (#107926) (#109178) Co-authored-by: Hugo van Kemenade Co-authored-by: Christoph Anton Mitterer --- Doc/library/os.rst | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 38024f78f4f048..75ffee0b8e3cfc 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -1508,21 +1508,6 @@ or `the MSDN `_ on Windo Parameters *out* and *in* was renamed to *out_fd* and *in_fd*. -.. function:: set_blocking(fd, blocking, /) - - Set the blocking mode of the specified file descriptor. Set the - :data:`O_NONBLOCK` flag if blocking is ``False``, clear the flag otherwise. - - See also :func:`get_blocking` and :meth:`socket.socket.setblocking`. - - .. availability:: Unix. - - The function is limited on Emscripten and WASI, see - :ref:`wasm-availability` for more information. - - .. versionadded:: 3.5 - - .. data:: SF_NODISKIO SF_MNOWAIT SF_SYNC @@ -1544,6 +1529,21 @@ or `the MSDN `_ on Windo .. versionadded:: 3.11 +.. function:: set_blocking(fd, blocking, /) + + Set the blocking mode of the specified file descriptor. Set the + :data:`O_NONBLOCK` flag if blocking is ``False``, clear the flag otherwise. + + See also :func:`get_blocking` and :meth:`socket.socket.setblocking`. + + .. availability:: Unix. + + The function is limited on Emscripten and WASI, see + :ref:`wasm-availability` for more information. + + .. versionadded:: 3.5 + + .. function:: splice(src, dst, count, offset_src=None, offset_dst=None) Transfer *count* bytes from file descriptor *src*, starting from offset From c20658249da47408c2d2455de87d7e70445cf3f0 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 10 Sep 2023 06:50:22 -0700 Subject: [PATCH 288/632] [3.11] gh-109207: Fix SystemError when printing symtable entry object. (GH-109225) (GH-109228) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 429749969621b149c1a7c3c004bd44f52bec8f44) Co-authored-by: 云line <31395137+yunline@users.noreply.github.com> --- Lib/test/test_symtable.py | 4 ++++ .../2023-09-10-18-53-55.gh-issue-109207.Fei8bY.rst | 1 + Python/symtable.c | 5 ++--- 3 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-09-10-18-53-55.gh-issue-109207.Fei8bY.rst diff --git a/Lib/test/test_symtable.py b/Lib/test/test_symtable.py index 819354e4eee9b5..f38d791c83d41d 100644 --- a/Lib/test/test_symtable.py +++ b/Lib/test/test_symtable.py @@ -252,6 +252,10 @@ def test_symtable_repr(self): self.assertEqual(str(self.top), "") self.assertEqual(str(self.spam), "") + def test_symtable_entry_repr(self): + expected = f"" + self.assertEqual(repr(self.top._table), expected) + if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-09-10-18-53-55.gh-issue-109207.Fei8bY.rst b/Misc/NEWS.d/next/Core and Builtins/2023-09-10-18-53-55.gh-issue-109207.Fei8bY.rst new file mode 100644 index 00000000000000..f9da3ac4d1abbd --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-09-10-18-53-55.gh-issue-109207.Fei8bY.rst @@ -0,0 +1 @@ +Fix a SystemError in ``__repr__`` of symtable entry object. diff --git a/Python/symtable.c b/Python/symtable.c index afe05c71b97d9d..37e5c697405b1a 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -128,9 +128,8 @@ ste_new(struct symtable *st, identifier name, _Py_block_ty block, static PyObject * ste_repr(PySTEntryObject *ste) { - return PyUnicode_FromFormat("", - ste->ste_name, - PyLong_AS_LONG(ste->ste_id), ste->ste_lineno); + return PyUnicode_FromFormat("", + ste->ste_name, ste->ste_id, ste->ste_lineno); } static void From b070d73ff20df210eb41e3e5104ba6c355d9584e Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 10 Sep 2023 10:41:19 -0700 Subject: [PATCH 289/632] [3.11] gh-50644: Forbid pickling of codecs streams (GH-109180) (GH-109232) Attempts to pickle or create a shallow or deep copy of codecs streams now raise a TypeError. Previously, copying failed with a RecursionError, while pickling produced wrong results that eventually caused unpickling to fail with a RecursionError. (cherry picked from commit d6892c2b9263b39ea1c7905667942914b6a24b2c) Co-authored-by: Serhiy Storchaka --- Lib/codecs.py | 12 +++ Lib/test/test_codecs.py | 79 +++++++++++++++++++ ...3-09-09-15-08-37.gh-issue-50644.JUAZOh.rst | 4 + 3 files changed, 95 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2023-09-09-15-08-37.gh-issue-50644.JUAZOh.rst diff --git a/Lib/codecs.py b/Lib/codecs.py index 3b173b612101e7..c6165fcb142a7f 100644 --- a/Lib/codecs.py +++ b/Lib/codecs.py @@ -414,6 +414,9 @@ def __enter__(self): def __exit__(self, type, value, tb): self.stream.close() + def __reduce_ex__(self, proto): + raise TypeError("can't serialize %s" % self.__class__.__name__) + ### class StreamReader(Codec): @@ -663,6 +666,9 @@ def __enter__(self): def __exit__(self, type, value, tb): self.stream.close() + def __reduce_ex__(self, proto): + raise TypeError("can't serialize %s" % self.__class__.__name__) + ### class StreamReaderWriter: @@ -750,6 +756,9 @@ def __enter__(self): def __exit__(self, type, value, tb): self.stream.close() + def __reduce_ex__(self, proto): + raise TypeError("can't serialize %s" % self.__class__.__name__) + ### class StreamRecoder: @@ -866,6 +875,9 @@ def __enter__(self): def __exit__(self, type, value, tb): self.stream.close() + def __reduce_ex__(self, proto): + raise TypeError("can't serialize %s" % self.__class__.__name__) + ### Shortcuts def open(filename, mode='r', encoding=None, errors='strict', buffering=-1): diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index e170a30263f8ba..684e6cf7515481 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -1,7 +1,9 @@ import codecs import contextlib +import copy import io import locale +import pickle import sys import unittest import encodings @@ -1772,6 +1774,61 @@ def test_readlines(self): f = self.reader(self.stream) self.assertEqual(f.readlines(), ['\ud55c\n', '\uae00']) + def test_copy(self): + f = self.reader(Queue(b'\xed\x95\x9c\n\xea\xb8\x80')) + with self.assertRaisesRegex(TypeError, 'StreamReader'): + copy.copy(f) + with self.assertRaisesRegex(TypeError, 'StreamReader'): + copy.deepcopy(f) + + def test_pickle(self): + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + with self.subTest(protocol=proto): + f = self.reader(Queue(b'\xed\x95\x9c\n\xea\xb8\x80')) + with self.assertRaisesRegex(TypeError, 'StreamReader'): + pickle.dumps(f, proto) + + +class StreamWriterTest(unittest.TestCase): + + def setUp(self): + self.writer = codecs.getwriter('utf-8') + + def test_copy(self): + f = self.writer(Queue(b'')) + with self.assertRaisesRegex(TypeError, 'StreamWriter'): + copy.copy(f) + with self.assertRaisesRegex(TypeError, 'StreamWriter'): + copy.deepcopy(f) + + def test_pickle(self): + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + with self.subTest(protocol=proto): + f = self.writer(Queue(b'')) + with self.assertRaisesRegex(TypeError, 'StreamWriter'): + pickle.dumps(f, proto) + + +class StreamReaderWriterTest(unittest.TestCase): + + def setUp(self): + self.reader = codecs.getreader('latin1') + self.writer = codecs.getwriter('utf-8') + + def test_copy(self): + f = codecs.StreamReaderWriter(Queue(b''), self.reader, self.writer) + with self.assertRaisesRegex(TypeError, 'StreamReaderWriter'): + copy.copy(f) + with self.assertRaisesRegex(TypeError, 'StreamReaderWriter'): + copy.deepcopy(f) + + def test_pickle(self): + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + with self.subTest(protocol=proto): + f = codecs.StreamReaderWriter(Queue(b''), self.reader, self.writer) + with self.assertRaisesRegex(TypeError, 'StreamReaderWriter'): + pickle.dumps(f, proto) + class EncodedFileTest(unittest.TestCase): @@ -3369,6 +3426,28 @@ def test_seeking_write(self): self.assertEqual(sr.readline(), b'abc\n') self.assertEqual(sr.readline(), b'789\n') + def test_copy(self): + bio = io.BytesIO() + codec = codecs.lookup('ascii') + sr = codecs.StreamRecoder(bio, codec.encode, codec.decode, + encodings.ascii.StreamReader, encodings.ascii.StreamWriter) + + with self.assertRaisesRegex(TypeError, 'StreamRecoder'): + copy.copy(sr) + with self.assertRaisesRegex(TypeError, 'StreamRecoder'): + copy.deepcopy(sr) + + def test_pickle(self): + q = Queue(b'') + codec = codecs.lookup('ascii') + sr = codecs.StreamRecoder(q, codec.encode, codec.decode, + encodings.ascii.StreamReader, encodings.ascii.StreamWriter) + + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + with self.subTest(protocol=proto): + with self.assertRaisesRegex(TypeError, 'StreamRecoder'): + pickle.dumps(sr, proto) + @unittest.skipIf(_testinternalcapi is None, 'need _testinternalcapi module') class LocaleCodecTest(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2023-09-09-15-08-37.gh-issue-50644.JUAZOh.rst b/Misc/NEWS.d/next/Library/2023-09-09-15-08-37.gh-issue-50644.JUAZOh.rst new file mode 100644 index 00000000000000..a7a442e35289d3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-09-09-15-08-37.gh-issue-50644.JUAZOh.rst @@ -0,0 +1,4 @@ +Attempts to pickle or create a shallow or deep copy of :mod:`codecs` streams +now raise a TypeError. Previously, copying failed with a RecursionError, +while pickling produced wrong results that eventually caused unpickling +to fail with a RecursionError. From cef4951116744d140dae4e4c12738a1cb99a676e Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 10 Sep 2023 15:39:49 -0700 Subject: [PATCH 290/632] [3.11] gh-109237: Fix test_site for non-ASCII working directory (GH-109238) (#109240) gh-109237: Fix test_site for non-ASCII working directory (GH-109238) Fix test_site.test_underpth_basic() when the working directory contains at least one non-ASCII character: encode the "._pth" file to UTF-8 and enable the UTF-8 Mode to use UTF-8 for the child process stdout. (cherry picked from commit cbb3a6f8ada3d133c3ab9f9465b65067fce5bb42) Co-authored-by: Victor Stinner --- Lib/test/test_site.py | 4 ++-- .../next/Tests/2023-09-10-22-32-20.gh-issue-109237.SvgKwD.rst | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2023-09-10-22-32-20.gh-issue-109237.SvgKwD.rst diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index 12a2b73a3fe2fb..613e946ba71ba2 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -577,7 +577,7 @@ def _create_underpth_exe(self, lines, exe_pth=True): _pth_file = os.path.splitext(exe_file)[0] + '._pth' else: _pth_file = os.path.splitext(dll_file)[0] + '._pth' - with open(_pth_file, 'w') as f: + with open(_pth_file, 'w', encoding='utf8') as f: for line in lines: print(line, file=f) return exe_file @@ -614,7 +614,7 @@ def test_underpth_basic(self): os.path.dirname(exe_file), pth_lines) - output = subprocess.check_output([exe_file, '-c', + output = subprocess.check_output([exe_file, '-X', 'utf8', '-c', 'import sys; print("\\n".join(sys.path) if sys.flags.no_site else "")' ], encoding='utf-8', errors='surrogateescape') actual_sys_path = output.rstrip().split('\n') diff --git a/Misc/NEWS.d/next/Tests/2023-09-10-22-32-20.gh-issue-109237.SvgKwD.rst b/Misc/NEWS.d/next/Tests/2023-09-10-22-32-20.gh-issue-109237.SvgKwD.rst new file mode 100644 index 00000000000000..1d762bbe1d2592 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-09-10-22-32-20.gh-issue-109237.SvgKwD.rst @@ -0,0 +1,4 @@ +Fix ``test_site.test_underpth_basic()`` when the working directory contains +at least one non-ASCII character: encode the ``._pth`` file to UTF-8 and +enable the UTF-8 Mode to use UTF-8 for the child process stdout. Patch by +Victor Stinner. From a7e80fb62872127c23ada5cd80fb1251ba19fbd2 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 10 Sep 2023 15:46:47 -0700 Subject: [PATCH 291/632] [3.11] gh-109230: test_pyexpat no longer depends on the current directory (GH-109233) (#109242) gh-109230: test_pyexpat no longer depends on the current directory (GH-109233) Fix test_pyexpat.test_exception(): it can now be run from a directory different than Python source code directory. Before, the test failed in this case. Skip the test if Modules/pyexpat.c source is not available. Skip also the test on Python implementations other than CPython. (cherry picked from commit e55aab95786e0e9fb36a9a1122d2d0fb3d2403cd) Co-authored-by: Victor Stinner --- Lib/test/test_pyexpat.py | 72 ++++++++++++------- ...-09-10-19-59-57.gh-issue-109230.SRNLFQ.rst | 5 ++ 2 files changed, 53 insertions(+), 24 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2023-09-10-19-59-57.gh-issue-109230.SRNLFQ.rst diff --git a/Lib/test/test_pyexpat.py b/Lib/test/test_pyexpat.py index 6f0441b66d9b88..fa19c0ad9eebdb 100644 --- a/Lib/test/test_pyexpat.py +++ b/Lib/test/test_pyexpat.py @@ -1,13 +1,15 @@ # XXX TypeErrors on calling handlers, or on bad return values from a # handler, are obscure and unhelpful. -from io import BytesIO import os import platform import sys import sysconfig import unittest import traceback +from io import BytesIO +from test import support +from test.support import os_helper from xml.parsers import expat from xml.parsers.expat import errors @@ -441,37 +443,59 @@ def test7(self): # Test handling of exception from callback: class HandlerExceptionTest(unittest.TestCase): def StartElementHandler(self, name, attrs): - raise RuntimeError(name) + raise RuntimeError(f'StartElementHandler: <{name}>') def check_traceback_entry(self, entry, filename, funcname): - self.assertEqual(os.path.basename(entry[0]), filename) - self.assertEqual(entry[2], funcname) + self.assertEqual(os.path.basename(entry.filename), filename) + self.assertEqual(entry.name, funcname) + @support.cpython_only def test_exception(self): + # gh-66652: test _PyTraceback_Add() used by pyexpat.c to inject frames + + # Change the current directory to the Python source code directory + # if it is available. + src_dir = sysconfig.get_config_var('abs_builddir') + if src_dir: + have_source = os.path.isdir(src_dir) + else: + have_source = False + if have_source: + with os_helper.change_cwd(src_dir): + self._test_exception(have_source) + else: + self._test_exception(have_source) + + def _test_exception(self, have_source): + # Use path relative to the current directory which should be the Python + # source code directory (if it is available). + PYEXPAT_C = os.path.join('Modules', 'pyexpat.c') + parser = expat.ParserCreate() parser.StartElementHandler = self.StartElementHandler try: parser.Parse(b"", True) - self.fail() - except RuntimeError as e: - self.assertEqual(e.args[0], 'a', - "Expected RuntimeError for element 'a', but" + \ - " found %r" % e.args[0]) - # Check that the traceback contains the relevant line in pyexpat.c - entries = traceback.extract_tb(e.__traceback__) - self.assertEqual(len(entries), 3) - self.check_traceback_entry(entries[0], - "test_pyexpat.py", "test_exception") - self.check_traceback_entry(entries[1], - "pyexpat.c", "StartElement") - self.check_traceback_entry(entries[2], - "test_pyexpat.py", "StartElementHandler") - if (sysconfig.is_python_build() - and not (sys.platform == 'win32' and platform.machine() == 'ARM') - and not is_emscripten - and not is_wasi - ): - self.assertIn('call_with_frame("StartElement"', entries[1][3]) + + self.fail("the parser did not raise RuntimeError") + except RuntimeError as exc: + self.assertEqual(exc.args[0], 'StartElementHandler: ', exc) + entries = traceback.extract_tb(exc.__traceback__) + + self.assertEqual(len(entries), 3, entries) + self.check_traceback_entry(entries[0], + "test_pyexpat.py", "_test_exception") + self.check_traceback_entry(entries[1], + os.path.basename(PYEXPAT_C), + "StartElement") + self.check_traceback_entry(entries[2], + "test_pyexpat.py", "StartElementHandler") + + # Check that the traceback contains the relevant line in + # Modules/pyexpat.c. Skip the test if Modules/pyexpat.c is not + # available. + if have_source and os.path.exists(PYEXPAT_C): + self.assertIn('call_with_frame("StartElement"', + entries[1].line) # Test Current* members: diff --git a/Misc/NEWS.d/next/Tests/2023-09-10-19-59-57.gh-issue-109230.SRNLFQ.rst b/Misc/NEWS.d/next/Tests/2023-09-10-19-59-57.gh-issue-109230.SRNLFQ.rst new file mode 100644 index 00000000000000..18e1e85242005a --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-09-10-19-59-57.gh-issue-109230.SRNLFQ.rst @@ -0,0 +1,5 @@ +Fix ``test_pyexpat.test_exception()``: it can now be run from a directory +different than Python source code directory. Before, the test failed in this +case. Skip the test if Modules/pyexpat.c source is not available. Skip also +the test on Python implementations other than CPython. Patch by Victor +Stinner. From 2f8c80f531808999e0cf37095107e4e8c074ba06 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 11 Sep 2023 01:52:17 -0700 Subject: [PATCH 292/632] [3.11] gh-107219: Fix concurrent.futures terminate_broken() (GH-109244) (#109255) gh-107219: Fix concurrent.futures terminate_broken() (GH-109244) Fix a race condition in concurrent.futures. When a process in the process pool was terminated abruptly (while the future was running or pending), close the connection write end. If the call queue is blocked on sending bytes to a worker process, closing the connection write end interrupts the send, so the queue can be closed. Changes: * _ExecutorManagerThread.terminate_broken() now closes call_queue._writer. * multiprocessing PipeConnection.close() now interrupts WaitForMultipleObjects() in _send_bytes() by cancelling the overlapped operation. (cherry picked from commit a9b1f84790e977fb09f75b148c4c4f5924a6ef99) Co-authored-by: Victor Stinner --- Lib/concurrent/futures/process.py | 4 ++++ Lib/multiprocessing/connection.py | 18 ++++++++++++++++++ ...3-09-11-00-32-18.gh-issue-107219.3zqyFT.rst | 5 +++++ 3 files changed, 27 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2023-09-11-00-32-18.gh-issue-107219.3zqyFT.rst diff --git a/Lib/concurrent/futures/process.py b/Lib/concurrent/futures/process.py index 321608ab174cfb..7a069895d479ee 100644 --- a/Lib/concurrent/futures/process.py +++ b/Lib/concurrent/futures/process.py @@ -501,6 +501,10 @@ def terminate_broken(self, cause): # https://github.com/python/cpython/issues/94777 self.call_queue._reader.close() + # gh-107219: Close the connection writer which can unblock + # Queue._feed() if it was stuck in send_bytes(). + self.call_queue._writer.close() + # clean up resources self.join_executor_internals() diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py index b08144f7a1a169..63c6596b77cd14 100644 --- a/Lib/multiprocessing/connection.py +++ b/Lib/multiprocessing/connection.py @@ -9,6 +9,7 @@ __all__ = [ 'Client', 'Listener', 'Pipe', 'wait' ] +import errno import io import os import sys @@ -41,6 +42,7 @@ BUFSIZE = 8192 # A very generous timeout when it comes to local connections... CONNECTION_TIMEOUT = 20. +WSA_OPERATION_ABORTED = 995 _mmap_counter = itertools.count() @@ -271,12 +273,22 @@ class PipeConnection(_ConnectionBase): with FILE_FLAG_OVERLAPPED. """ _got_empty_message = False + _send_ov = None def _close(self, _CloseHandle=_winapi.CloseHandle): + ov = self._send_ov + if ov is not None: + # Interrupt WaitForMultipleObjects() in _send_bytes() + ov.cancel() _CloseHandle(self._handle) def _send_bytes(self, buf): + if self._send_ov is not None: + # A connection should only be used by a single thread + raise ValueError("concurrent send_bytes() calls " + "are not supported") ov, err = _winapi.WriteFile(self._handle, buf, overlapped=True) + self._send_ov = ov try: if err == _winapi.ERROR_IO_PENDING: waitres = _winapi.WaitForMultipleObjects( @@ -286,7 +298,13 @@ def _send_bytes(self, buf): ov.cancel() raise finally: + self._send_ov = None nwritten, err = ov.GetOverlappedResult(True) + if err == WSA_OPERATION_ABORTED: + # close() was called by another thread while + # WaitForMultipleObjects() was waiting for the overlapped + # operation. + raise OSError(errno.EPIPE, "handle is closed") assert err == 0 assert nwritten == len(buf) diff --git a/Misc/NEWS.d/next/Library/2023-09-11-00-32-18.gh-issue-107219.3zqyFT.rst b/Misc/NEWS.d/next/Library/2023-09-11-00-32-18.gh-issue-107219.3zqyFT.rst new file mode 100644 index 00000000000000..10afbcf823386a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-09-11-00-32-18.gh-issue-107219.3zqyFT.rst @@ -0,0 +1,5 @@ +Fix a race condition in ``concurrent.futures``. When a process in the +process pool was terminated abruptly (while the future was running or +pending), close the connection write end. If the call queue is blocked on +sending bytes to a worker process, closing the connection write end interrupts +the send, so the queue can be closed. Patch by Victor Stinner. From 74988a47e770095620f6199a69cebe06a55020b2 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 11 Sep 2023 04:30:24 -0700 Subject: [PATCH 293/632] [3.11] Test DocTestFinder directly instead of calling support.run_doctest() (GH-108917) (GH-109260) (cherry picked from commit 0abc935086931d4915ea3c45cffffecb31e7a45c) Co-authored-by: Serhiy Storchaka --- Lib/test/test_doctest.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py index 00aeacddc12cac..e48d91fafeda7d 100644 --- a/Lib/test/test_doctest.py +++ b/Lib/test/test_doctest.py @@ -742,15 +742,13 @@ class TestDocTestFinder(unittest.TestCase): def test_issue35753(self): # This import of `call` should trigger issue35753 when - # `support.run_doctest` is called due to unwrap failing, + # DocTestFinder.find() is called due to inspect.unwrap() failing, # however with a patched doctest this should succeed. from unittest.mock import call dummy_module = types.ModuleType("dummy") dummy_module.__dict__['inject_call'] = call - try: - support.run_doctest(dummy_module, verbosity=True) - except ValueError as e: - raise support.TestFailed("Doctest unwrap failed") from e + finder = doctest.DocTestFinder() + self.assertEqual(finder.find(dummy_module), []) def test_empty_namespace_package(self): pkg_name = 'doctest_empty_pkg' From 9297a72dbd39aca99fa8cf92960ce1869bc51c0b Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 11 Sep 2023 09:35:41 -0700 Subject: [PATCH 294/632] [3.11] gh-90805: Make sure test_functools works with and without _functoolsmodule (GH-108644) (GH-109274) (cherry picked from commit baa6dc8e388e71b2a00347143ecefb2ad3a8e53b) Co-authored-by: Nikita Sobolev --- Lib/test/test_functools.py | 58 ++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index 382e7dbffddf9d..fb6e1860ac11fe 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -27,10 +27,16 @@ py_functools = import_helper.import_fresh_module('functools', blocked=['_functools']) -c_functools = import_helper.import_fresh_module('functools') +c_functools = import_helper.import_fresh_module('functools', + fresh=['_functools']) decimal = import_helper.import_fresh_module('decimal', fresh=['_decimal']) +_partial_types = [py_functools.partial] +if c_functools: + _partial_types.append(c_functools.partial) + + @contextlib.contextmanager def replaced_module(name, replacement): original_module = sys.modules[name] @@ -202,7 +208,7 @@ def test_repr(self): kwargs = {'a': object(), 'b': object()} kwargs_reprs = ['a={a!r}, b={b!r}'.format_map(kwargs), 'b={b!r}, a={a!r}'.format_map(kwargs)] - if self.partial in (c_functools.partial, py_functools.partial): + if self.partial in _partial_types: name = 'functools.partial' else: name = self.partial.__name__ @@ -224,7 +230,7 @@ def test_repr(self): for kwargs_repr in kwargs_reprs]) def test_recursive_repr(self): - if self.partial in (c_functools.partial, py_functools.partial): + if self.partial in _partial_types: name = 'functools.partial' else: name = self.partial.__name__ @@ -251,7 +257,7 @@ def test_recursive_repr(self): f.__setstate__((capture, (), {}, {})) def test_pickle(self): - with self.AllowPickle(): + with replaced_module('functools', self.module): f = self.partial(signature, ['asdf'], bar=[True]) f.attr = [] for proto in range(pickle.HIGHEST_PROTOCOL + 1): @@ -334,7 +340,7 @@ def test_setstate_subclasses(self): self.assertIs(type(r[0]), tuple) def test_recursive_pickle(self): - with self.AllowPickle(): + with replaced_module('functools', self.module): f = self.partial(capture) f.__setstate__((f, (), {}, {})) try: @@ -388,14 +394,9 @@ def __getitem__(self, key): @unittest.skipUnless(c_functools, 'requires the C _functools module') class TestPartialC(TestPartial, unittest.TestCase): if c_functools: + module = c_functools partial = c_functools.partial - class AllowPickle: - def __enter__(self): - return self - def __exit__(self, type, value, tb): - return False - def test_attributes_unwritable(self): # attributes should not be writable p = self.partial(capture, 1, 2, a=10, b=20) @@ -438,15 +439,9 @@ def __str__(self): class TestPartialPy(TestPartial, unittest.TestCase): + module = py_functools partial = py_functools.partial - class AllowPickle: - def __init__(self): - self._cm = replaced_module("functools", py_functools) - def __enter__(self): - return self._cm.__enter__() - def __exit__(self, type, value, tb): - return self._cm.__exit__(type, value, tb) if c_functools: class CPartialSubclass(c_functools.partial): @@ -1860,9 +1855,10 @@ def test_staticmethod(x): def py_cached_func(x, y): return 3 * x + y -@c_functools.lru_cache() -def c_cached_func(x, y): - return 3 * x + y +if c_functools: + @c_functools.lru_cache() + def c_cached_func(x, y): + return 3 * x + y class TestLRUPy(TestLRU, unittest.TestCase): @@ -1879,18 +1875,20 @@ def cached_staticmeth(x, y): return 3 * x + y +@unittest.skipUnless(c_functools, 'requires the C _functools module') class TestLRUC(TestLRU, unittest.TestCase): - module = c_functools - cached_func = c_cached_func, + if c_functools: + module = c_functools + cached_func = c_cached_func, - @module.lru_cache() - def cached_meth(self, x, y): - return 3 * x + y + @module.lru_cache() + def cached_meth(self, x, y): + return 3 * x + y - @staticmethod - @module.lru_cache() - def cached_staticmeth(x, y): - return 3 * x + y + @staticmethod + @module.lru_cache() + def cached_staticmeth(x, y): + return 3 * x + y class TestSingleDispatch(unittest.TestCase): From 82a18069a1c288b9be3cdaf63a04e4c77fbd231f Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 11 Sep 2023 19:33:08 +0200 Subject: [PATCH 295/632] [3.11] gh-108987: Fix _thread.start_new_thread() race condition (#109135) (#109272) gh-108987: Fix _thread.start_new_thread() race condition (#109135) Fix _thread.start_new_thread() race condition. If a thread is created during Python finalization, the newly spawned thread now exits immediately instead of trying to access freed memory and lead to a crash. thread_run() calls PyEval_AcquireThread() which checks if the thread must exit. The problem was that tstate was dereferenced earlier in _PyThreadState_Bind() which leads to a crash most of the time. Move _PyThreadState_CheckConsistency() from thread_run() to _PyThreadState_Bind(). (cherry picked from commit 517cd82ea7d01b344804413ef05610934a43a241) --- Include/internal/pycore_pystate.h | 2 + ...-09-08-12-09-55.gh-issue-108987.x5AIG8.rst | 4 ++ Modules/_threadmodule.c | 47 ++++++++++++------- Python/ceval_gil.h | 25 ++-------- Python/pystate.c | 26 ++++++++++ 5 files changed, 66 insertions(+), 38 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-09-08-12-09-55.gh-issue-108987.x5AIG8.rst diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h index 8696aaf26724b8..7c5aba12d5295e 100644 --- a/Include/internal/pycore_pystate.h +++ b/Include/internal/pycore_pystate.h @@ -65,6 +65,8 @@ _Py_ThreadCanHandlePendingCalls(void) extern int _PyThreadState_CheckConsistency(PyThreadState *tstate); #endif +int _PyThreadState_MustExit(PyThreadState *tstate); + /* Variable and macro for in-line access to current thread and interpreter state */ diff --git a/Misc/NEWS.d/next/Library/2023-09-08-12-09-55.gh-issue-108987.x5AIG8.rst b/Misc/NEWS.d/next/Library/2023-09-08-12-09-55.gh-issue-108987.x5AIG8.rst new file mode 100644 index 00000000000000..16526ee748d869 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-09-08-12-09-55.gh-issue-108987.x5AIG8.rst @@ -0,0 +1,4 @@ +Fix :func:`_thread.start_new_thread` race condition. If a thread is created +during Python finalization, the newly spawned thread now exits immediately +instead of trying to access freed memory and lead to a crash. Patch by +Victor Stinner. diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index b70325a2b693c4..ac49ee5c35d4b3 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -1053,21 +1053,21 @@ _localdummy_destroyed(PyObject *localweakref, PyObject *dummyweakref) /* Module functions */ struct bootstate { - PyInterpreterState *interp; + PyThreadState *tstate; PyObject *func; PyObject *args; PyObject *kwargs; - PyThreadState *tstate; - _PyRuntimeState *runtime; }; static void -thread_bootstate_free(struct bootstate *boot) +thread_bootstate_free(struct bootstate *boot, int decref) { - Py_DECREF(boot->func); - Py_DECREF(boot->args); - Py_XDECREF(boot->kwargs); + if (decref) { + Py_DECREF(boot->func); + Py_DECREF(boot->args); + Py_XDECREF(boot->kwargs); + } PyMem_Free(boot); } @@ -1078,9 +1078,24 @@ thread_run(void *boot_raw) struct bootstate *boot = (struct bootstate *) boot_raw; PyThreadState *tstate = boot->tstate; - // gh-104690: If Python is being finalized and PyInterpreterState_Delete() - // was called, tstate becomes a dangling pointer. - assert(_PyThreadState_CheckConsistency(tstate)); + // gh-108987: If _thread.start_new_thread() is called before or while + // Python is being finalized, thread_run() can called *after*. + // _PyRuntimeState_SetFinalizing() is called. At this point, all Python + // threads must exit, except of the thread calling Py_Finalize() whch holds + // the GIL and must not exit. + // + // At this stage, tstate can be a dangling pointer (point to freed memory), + // it's ok to call _PyThreadState_MustExit() with a dangling pointer. + if (_PyThreadState_MustExit(tstate)) { + // Don't call PyThreadState_Clear() nor _PyThreadState_DeleteCurrent(). + // These functions are called on tstate indirectly by Py_Finalize() + // which calls _PyInterpreterState_Clear(). + // + // Py_DECREF() cannot be called because the GIL is not held: leak + // references on purpose. Python is being finalized anyway. + thread_bootstate_free(boot, 0); + goto exit; + } tstate->thread_id = PyThread_get_thread_ident(); #ifdef PY_HAVE_THREAD_NATIVE_ID @@ -1105,20 +1120,22 @@ thread_run(void *boot_raw) Py_DECREF(res); } - thread_bootstate_free(boot); + thread_bootstate_free(boot, 1); + tstate->interp->threads.count--; PyThreadState_Clear(tstate); _PyThreadState_DeleteCurrent(tstate); +exit: // bpo-44434: Don't call explicitly PyThread_exit_thread(). On Linux with // the glibc, pthread_exit() can abort the whole process if dlopen() fails // to open the libgcc_s.so library (ex: EMFILE error). + return; } static PyObject * thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs) { - _PyRuntimeState *runtime = &_PyRuntime; PyObject *func, *args, *kwargs = NULL; if (!PyArg_UnpackTuple(fargs, "start_new_thread", 2, 3, @@ -1151,13 +1168,11 @@ thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs) if (boot == NULL) { return PyErr_NoMemory(); } - boot->interp = _PyInterpreterState_GET(); - boot->tstate = _PyThreadState_Prealloc(boot->interp); + boot->tstate = _PyThreadState_Prealloc(interp); if (boot->tstate == NULL) { PyMem_Free(boot); return PyErr_NoMemory(); } - boot->runtime = runtime; boot->func = Py_NewRef(func); boot->args = Py_NewRef(args); boot->kwargs = Py_XNewRef(kwargs); @@ -1166,7 +1181,7 @@ thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs) if (ident == PYTHREAD_INVALID_THREAD_ID) { PyErr_SetString(ThreadError, "can't start new thread"); PyThreadState_Clear(boot->tstate); - thread_bootstate_free(boot); + thread_bootstate_free(boot, 1); return NULL; } return PyLong_FromUnsignedLong(ident); diff --git a/Python/ceval_gil.h b/Python/ceval_gil.h index d20af2690d1845..94e2df03e0e522 100644 --- a/Python/ceval_gil.h +++ b/Python/ceval_gil.h @@ -185,25 +185,6 @@ drop_gil(struct _ceval_runtime_state *ceval, struct _ceval_state *ceval2, } -/* Check if a Python thread must exit immediately, rather than taking the GIL - if Py_Finalize() has been called. - - When this function is called by a daemon thread after Py_Finalize() has been - called, the GIL does no longer exist. - - tstate must be non-NULL. */ -static inline int -tstate_must_exit(PyThreadState *tstate) -{ - /* bpo-39877: Access _PyRuntime directly rather than using - tstate->interp->runtime to support calls from Python daemon threads. - After Py_Finalize() has been called, tstate can be a dangling pointer: - point to PyThreadState freed memory. */ - PyThreadState *finalizing = _PyRuntimeState_GetFinalizing(&_PyRuntime); - return (finalizing != NULL && finalizing != tstate); -} - - /* Take the GIL. The function saves errno at entry and restores its value at exit. @@ -216,7 +197,7 @@ take_gil(PyThreadState *tstate) assert(tstate != NULL); - if (tstate_must_exit(tstate)) { + if (_PyThreadState_MustExit(tstate)) { /* bpo-39877: If Py_Finalize() has been called and tstate is not the thread which called Py_Finalize(), exit immediately the thread. @@ -255,7 +236,7 @@ take_gil(PyThreadState *tstate) _Py_atomic_load_relaxed(&gil->locked) && gil->switch_number == saved_switchnum) { - if (tstate_must_exit(tstate)) { + if (_PyThreadState_MustExit(tstate)) { MUTEX_UNLOCK(gil->mutex); // gh-96387: If the loop requested a drop request in a previous // iteration, reset the request. Otherwise, drop_gil() can @@ -295,7 +276,7 @@ take_gil(PyThreadState *tstate) MUTEX_UNLOCK(gil->switch_mutex); #endif - if (tstate_must_exit(tstate)) { + if (_PyThreadState_MustExit(tstate)) { /* bpo-36475: If Py_Finalize() has been called and tstate is not the thread which called Py_Finalize(), exit immediately the thread. diff --git a/Python/pystate.c b/Python/pystate.c index ec278eef836c30..db2ce878af64ec 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -882,6 +882,10 @@ _PyThreadState_Init(PyThreadState *tstate) void _PyThreadState_SetCurrent(PyThreadState *tstate) { + // gh-104690: If Python is being finalized and PyInterpreterState_Delete() + // was called, tstate becomes a dangling pointer. + assert(_PyThreadState_CheckConsistency(tstate)); + _PyGILState_NoteThreadState(&tstate->interp->runtime->gilstate, tstate); } @@ -2255,6 +2259,28 @@ _PyThreadState_CheckConsistency(PyThreadState *tstate) #endif +// Check if a Python thread must exit immediately, rather than taking the GIL +// if Py_Finalize() has been called. +// +// When this function is called by a daemon thread after Py_Finalize() has been +// called, the GIL does no longer exist. +// +// tstate can be a dangling pointer (point to freed memory): only tstate value +// is used, the pointer is not deferenced. +// +// tstate must be non-NULL. +int +_PyThreadState_MustExit(PyThreadState *tstate) +{ + /* bpo-39877: Access _PyRuntime directly rather than using + tstate->interp->runtime to support calls from Python daemon threads. + After Py_Finalize() has been called, tstate can be a dangling pointer: + point to PyThreadState freed memory. */ + PyThreadState *finalizing = _PyRuntimeState_GetFinalizing(&_PyRuntime); + return (finalizing != NULL && finalizing != tstate); +} + + #ifdef __cplusplus } #endif From abf3a68fe31b6e5700e75f1b41b7b5da5cf0596d Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 11 Sep 2023 18:53:59 -0700 Subject: [PATCH 296/632] [3.11] gh-109295: Clean up multiprocessing in test_asyncio and test_compileall (GH-109298) (#109302) gh-109295: Clean up multiprocessing in test_asyncio and test_compileall (GH-109298) test_asyncio and test_compileall now clean up multiprocessing by calling multiprocessing _cleanup_tests(): explicitly clean up resources and stop background processes like the resource tracker. (cherry picked from commit 09ea4b8706165fd9474165090a0ba86509abd6c8) Co-authored-by: Victor Stinner --- Lib/test/test_asyncio/test_events.py | 3 +++ Lib/test/test_compileall.py | 10 +++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index e811c8182a1bba..4e0321a87daeca 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -31,6 +31,7 @@ from asyncio import events from asyncio import proactor_events from asyncio import selector_events +from multiprocessing.util import _cleanup_tests as multiprocessing_cleanup_tests from test.test_asyncio import utils as test_utils from test import support from test.support import socket_helper @@ -2730,6 +2731,8 @@ def test_get_event_loop_new_process(self): # multiprocessing.synchronize module cannot be imported. support.skip_if_broken_multiprocessing_synchronize() + self.addCleanup(multiprocessing_cleanup_tests) + async def main(): pool = concurrent.futures.ProcessPoolExecutor() result = await self.loop.run_in_executor( diff --git a/Lib/test/test_compileall.py b/Lib/test/test_compileall.py index df7c5122b3b1f5..9cd92ad365c5a9 100644 --- a/Lib/test/test_compileall.py +++ b/Lib/test/test_compileall.py @@ -18,6 +18,7 @@ try: # compileall relies on ProcessPoolExecutor if ProcessPoolExecutor exists # and it can function. + from multiprocessing.util import _cleanup_tests as multiprocessing_cleanup_tests from concurrent.futures import ProcessPoolExecutor from concurrent.futures.process import _check_system_limits _check_system_limits() @@ -54,6 +55,8 @@ class CompileallTestsBase: def setUp(self): self.directory = tempfile.mkdtemp() + self.addCleanup(shutil.rmtree, self.directory) + self.source_path = os.path.join(self.directory, '_test.py') self.bc_path = importlib.util.cache_from_source(self.source_path) with open(self.source_path, 'w', encoding="utf-8") as file: @@ -66,9 +69,6 @@ def setUp(self): self.source_path3 = os.path.join(self.subdirectory, '_test3.py') shutil.copyfile(self.source_path, self.source_path3) - def tearDown(self): - shutil.rmtree(self.directory) - def add_bad_source_file(self): self.bad_source_path = os.path.join(self.directory, '_test_bad.py') with open(self.bad_source_path, 'w', encoding="utf-8") as file: @@ -307,9 +307,13 @@ def _test_ddir_only(self, *, ddir, parallel=True): script_helper.make_script(path, "__init__", "") mods.append(script_helper.make_script(path, "mod", "def fn(): 1/0\nfn()\n")) + + if parallel: + self.addCleanup(multiprocessing_cleanup_tests) compileall.compile_dir( self.directory, quiet=True, ddir=ddir, workers=2 if parallel else 1) + self.assertTrue(mods) for mod in mods: self.assertTrue(mod.startswith(self.directory), mod) From 5d47fb186e7f2689fec89c7bfd72448ad4d352ca Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 11 Sep 2023 19:00:36 -0700 Subject: [PATCH 297/632] [3.11] gh-109295: Fix test_os.test_access_denied() for TEMP=cwd (GH-109299) (#109303) gh-109295: Fix test_os.test_access_denied() for TEMP=cwd (GH-109299) Fix test_os.test_access_denied() when the TEMP environment variable is equal to the current working directory. Run the test using a different filename, since self.fname already exists in this case. (cherry picked from commit 7dedfd36dc16d9e1e15d7d0b0a636dd401a5a543) Co-authored-by: Victor Stinner --- Lib/test/test_os.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index aa414ca14889a4..1e1980e3ac47e5 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -726,7 +726,7 @@ def test_access_denied(self): # denied. See issue 28075. # os.environ['TEMP'] should be located on a volume that # supports file ACLs. - fname = os.path.join(os.environ['TEMP'], self.fname) + fname = os.path.join(os.environ['TEMP'], self.fname + "_access") self.addCleanup(os_helper.unlink, fname) create_file(fname, b'ABC') # Deny the right to [S]YNCHRONIZE on the file to From 0e2d67457ba245442457432d15115d1dc1b0ecb5 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 11 Sep 2023 22:53:15 -0700 Subject: [PATCH 298/632] [3.11] gh-107322: zipapp: Remove the suggestion to remove .dist-info directories (GH-107296) (#109282) Co-authored-by: wim glenn --- Doc/library/zipapp.rst | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Doc/library/zipapp.rst b/Doc/library/zipapp.rst index 981020b13cd988..81d61d8fe853d3 100644 --- a/Doc/library/zipapp.rst +++ b/Doc/library/zipapp.rst @@ -281,12 +281,7 @@ The steps to create a standalone archive are as follows: file - if not, you can just list the dependencies manually on the pip command line). -3. Optionally, delete the ``.dist-info`` directories created by pip in the - ``myapp`` directory. These hold metadata for pip to manage the packages, and - as you won't be making any further use of pip they aren't required - - although it won't do any harm if you leave them. - -4. Package the application using: +3. Package the application using: .. code-block:: shell-session From 1e8696133c6c83323e448238924fc94d462e36f0 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Tue, 12 Sep 2023 10:57:28 +0100 Subject: [PATCH 299/632] [3.11] gh-109179: Fix traceback display for SyntaxErrors with notes (#109197) (#109283) gh-109179: Fix traceback display for SyntaxErrors with notes (#109197) (cherry picked from commit ecd21a629a2a30bcae89902f7cad5670e9441e2c) --- Lib/test/test_traceback.py | 43 ++++++++++--------- ...-09-09-21-17-18.gh-issue-109179.ZR8qs2.rst | 1 + Python/pythonrun.c | 24 +++++------ 3 files changed, 34 insertions(+), 34 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-09-09-21-17-18.gh-issue-109179.ZR8qs2.rst diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 544b24c01b37d8..ccc59870c2a952 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -1546,27 +1546,28 @@ def __repr__(self): err_msg = '' self.assertEqual(self.get_report(e), vanilla + err_msg + '\nFinal Note\n') - def test_exception_with_note_with_multiple_notes(self): - e = ValueError(42) - vanilla = self.get_report(e) - - e.add_note('Note 1') - e.add_note('Note 2') - e.add_note('Note 3') - - self.assertEqual( - self.get_report(e), - vanilla + 'Note 1\n' + 'Note 2\n' + 'Note 3\n') - - del e.__notes__ - e.add_note('Note 4') - del e.__notes__ - e.add_note('Note 5') - e.add_note('Note 6') - - self.assertEqual( - self.get_report(e), - vanilla + 'Note 5\n' + 'Note 6\n') + def test_exception_with_multiple_notes(self): + for e in [ValueError(42), SyntaxError('bad syntax')]: + with self.subTest(e=e): + vanilla = self.get_report(e) + + e.add_note('Note 1') + e.add_note('Note 2') + e.add_note('Note 3') + + self.assertEqual( + self.get_report(e), + vanilla + 'Note 1\n' + 'Note 2\n' + 'Note 3\n') + + del e.__notes__ + e.add_note('Note 4') + del e.__notes__ + e.add_note('Note 5') + e.add_note('Note 6') + + self.assertEqual( + self.get_report(e), + vanilla + 'Note 5\n' + 'Note 6\n') def test_exception_qualname(self): class A: diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-09-09-21-17-18.gh-issue-109179.ZR8qs2.rst b/Misc/NEWS.d/next/Core and Builtins/2023-09-09-21-17-18.gh-issue-109179.ZR8qs2.rst new file mode 100644 index 00000000000000..dd95a8ec7920aa --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-09-09-21-17-18.gh-issue-109179.ZR8qs2.rst @@ -0,0 +1 @@ +Fix bug where the C traceback display drops notes from :exc:`SyntaxError`. diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 03e366a8d095dd..91c2ad3a13d432 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -1130,21 +1130,16 @@ print_exception_suggestions(struct exception_print_context *ctx, } static int -print_exception_notes(struct exception_print_context *ctx, PyObject *value) +print_exception_notes(struct exception_print_context *ctx, PyObject *notes) { PyObject *f = ctx->file; - if (!PyExceptionInstance_Check(value)) { + if (notes == NULL) { return 0; } - PyObject *notes; - int res = _PyObject_LookupAttr(value, &_Py_ID(__notes__), ¬es); - if (res <= 0) { - return res; - } if (!PySequence_Check(notes)) { - res = 0; + int res = 0; if (write_indented_margin(ctx, f) < 0) { res = -1; } @@ -1157,7 +1152,6 @@ print_exception_notes(struct exception_print_context *ctx, PyObject *value) res = PyFile_WriteObject(s, f, Py_PRINT_RAW); Py_DECREF(s); } - Py_DECREF(notes); return res; } Py_ssize_t num_notes = PySequence_Length(notes); @@ -1199,17 +1193,16 @@ print_exception_notes(struct exception_print_context *ctx, PyObject *value) } } - Py_DECREF(notes); return 0; error: Py_XDECREF(lines); - Py_DECREF(notes); return -1; } static int print_exception(struct exception_print_context *ctx, PyObject *value) { + PyObject *notes = NULL; PyObject *f = ctx->file; if (!PyExceptionInstance_Check(value)) { @@ -1223,8 +1216,11 @@ print_exception(struct exception_print_context *ctx, PyObject *value) goto error; } - /* grab the type now because value can change below */ + /* grab the type and notes now because value can change below */ PyObject *type = (PyObject *) Py_TYPE(value); + if (_PyObject_LookupAttr(value, &_Py_ID(__notes__), ¬es) < 0) { + goto error; + } if (print_exception_file_and_line(ctx, &value) < 0) { goto error; @@ -1238,14 +1234,16 @@ print_exception(struct exception_print_context *ctx, PyObject *value) if (PyFile_WriteString("\n", f) < 0) { goto error; } - if (print_exception_notes(ctx, value) < 0) { + if (print_exception_notes(ctx, notes) < 0) { goto error; } + Py_XDECREF(notes); Py_DECREF(value); assert(!PyErr_Occurred()); return 0; error: + Py_XDECREF(notes); Py_DECREF(value); return -1; } From b4c1cf93c782bb95980420754d2855a903f5aad1 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 12 Sep 2023 07:05:58 -0700 Subject: [PATCH 300/632] [3.11] gh-84867: Do not load tests from TestCase and FunctionTestCase (GH-100497) (GH-109328) (cherry picked from commit 66d1d7eb067d445f1ade151f4a6db3864dd9109f) Co-authored-by: Nikita Sobolev --- Lib/unittest/loader.py | 22 ++++++++++---- Lib/unittest/test/test_loader.py | 29 +++++++++++++++++++ ...2-12-24-12-50-54.gh-issue-84867.OhaLbU.rst | 2 ++ 3 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-12-24-12-50-54.gh-issue-84867.OhaLbU.rst diff --git a/Lib/unittest/loader.py b/Lib/unittest/loader.py index 7e6ce2f224bafc..f4e3d6e8f275dc 100644 --- a/Lib/unittest/loader.py +++ b/Lib/unittest/loader.py @@ -87,9 +87,13 @@ def loadTestsFromTestCase(self, testCaseClass): raise TypeError("Test cases should not be derived from " "TestSuite. Maybe you meant to derive from " "TestCase?") - testCaseNames = self.getTestCaseNames(testCaseClass) - if not testCaseNames and hasattr(testCaseClass, 'runTest'): - testCaseNames = ['runTest'] + if testCaseClass in (case.TestCase, case.FunctionTestCase): + # We don't load any tests from base types that should not be loaded. + testCaseNames = [] + else: + testCaseNames = self.getTestCaseNames(testCaseClass) + if not testCaseNames and hasattr(testCaseClass, 'runTest'): + testCaseNames = ['runTest'] loaded_suite = self.suiteClass(map(testCaseClass, testCaseNames)) return loaded_suite @@ -120,7 +124,11 @@ def loadTestsFromModule(self, module, *args, pattern=None, **kws): tests = [] for name in dir(module): obj = getattr(module, name) - if isinstance(obj, type) and issubclass(obj, case.TestCase): + if ( + isinstance(obj, type) + and issubclass(obj, case.TestCase) + and obj not in (case.TestCase, case.FunctionTestCase) + ): tests.append(self.loadTestsFromTestCase(obj)) load_tests = getattr(module, 'load_tests', None) @@ -189,7 +197,11 @@ def loadTestsFromName(self, name, module=None): if isinstance(obj, types.ModuleType): return self.loadTestsFromModule(obj) - elif isinstance(obj, type) and issubclass(obj, case.TestCase): + elif ( + isinstance(obj, type) + and issubclass(obj, case.TestCase) + and obj not in (case.TestCase, case.FunctionTestCase) + ): return self.loadTestsFromTestCase(obj) elif (isinstance(obj, types.FunctionType) and isinstance(parent, type) and diff --git a/Lib/unittest/test/test_loader.py b/Lib/unittest/test/test_loader.py index de2268cda90688..24001822b2f136 100644 --- a/Lib/unittest/test/test_loader.py +++ b/Lib/unittest/test/test_loader.py @@ -102,6 +102,22 @@ def runTest(self): self.assertIsInstance(suite, loader.suiteClass) self.assertEqual(list(suite), [Foo('runTest')]) + # "Do not load any tests from `TestCase` class itself." + def test_loadTestsFromTestCase__from_TestCase(self): + loader = unittest.TestLoader() + + suite = loader.loadTestsFromTestCase(unittest.TestCase) + self.assertIsInstance(suite, loader.suiteClass) + self.assertEqual(list(suite), []) + + # "Do not load any tests from `FunctionTestCase` class." + def test_loadTestsFromTestCase__from_FunctionTestCase(self): + loader = unittest.TestLoader() + + suite = loader.loadTestsFromTestCase(unittest.FunctionTestCase) + self.assertIsInstance(suite, loader.suiteClass) + self.assertEqual(list(suite), []) + ################################################################ ### /Tests for TestLoader.loadTestsFromTestCase @@ -123,6 +139,19 @@ def test(self): expected = [loader.suiteClass([MyTestCase('test')])] self.assertEqual(list(suite), expected) + # "This test ensures that internal `TestCase` subclasses are not loaded" + def test_loadTestsFromModule__TestCase_subclass_internals(self): + # See https://github.com/python/cpython/issues/84867 + m = types.ModuleType('m') + # Simulate imported names: + m.TestCase = unittest.TestCase + m.FunctionTestCase = unittest.FunctionTestCase + + loader = unittest.TestLoader() + suite = loader.loadTestsFromModule(m) + self.assertIsInstance(suite, loader.suiteClass) + self.assertEqual(list(suite), []) + # "This method searches `module` for classes derived from TestCase" # # What happens if no tests are found (no TestCase instances)? diff --git a/Misc/NEWS.d/next/Library/2022-12-24-12-50-54.gh-issue-84867.OhaLbU.rst b/Misc/NEWS.d/next/Library/2022-12-24-12-50-54.gh-issue-84867.OhaLbU.rst new file mode 100644 index 00000000000000..8b45dcee481916 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-12-24-12-50-54.gh-issue-84867.OhaLbU.rst @@ -0,0 +1,2 @@ +:class:`unittest.TestLoader` no longer loads test cases from exact +:class:`unittest.TestCase` and :class:`unittest.FunctionTestCase` classes. From 5681cd1a9dc417ae6472c56b5403ac841e43695b Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Tue, 12 Sep 2023 18:08:38 +0100 Subject: [PATCH 301/632] [3.11] gh-109184: update traceback module doc w.r.t notes (message is no longer always at the end) (#109201) (#109336) gh-109184: update traceback module doc w.r.t notes (message is no longer always at the end) (#109201) (cherry picked from commit 0e76cc359ba5d5e29d7c75355d7c1bc7e817eecf) --- Doc/library/traceback.rst | 41 ++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/Doc/library/traceback.rst b/Doc/library/traceback.rst index 100e3e6816b092..e15fe76dbb0186 100644 --- a/Doc/library/traceback.rst +++ b/Doc/library/traceback.rst @@ -140,11 +140,11 @@ The module defines the following functions: Format the exception part of a traceback using an exception value such as given by ``sys.last_value``. The return value is a list of strings, each - ending in a newline. Normally, the list contains a single string; however, - for :exc:`SyntaxError` exceptions, it contains several lines that (when - printed) display detailed information about where the syntax error occurred. - The message indicating which exception occurred is the always last string in - the list. + ending in a newline. The list contains the exception's message, which is + normally a single string; however, for :exc:`SyntaxError` exceptions, it + contains several lines that (when printed) display detailed information + about where the syntax error occurred. Following the message, the list + contains the exception's :attr:`notes `. Since Python 3.10, instead of passing *value*, an exception object can be passed as the first argument. If *value* is provided, the first @@ -154,6 +154,9 @@ The module defines the following functions: The *etype* parameter has been renamed to *exc* and is now positional-only. + .. versionchanged:: 3.11 + The returned list now includes any notes attached to the exception. + .. function:: format_exception(exc, /[, value, tb], limit=None, chain=True) @@ -236,6 +239,12 @@ capture data for later printing in a lightweight fashion. group's exceptions array. The formatted output is truncated when either limit is exceeded. + .. versionchanged:: 3.10 + Added the *compact* parameter. + + .. versionchanged:: 3.11 + Added the *max_group_width* and *max_group_depth* parameters. + .. attribute:: __cause__ A :class:`TracebackException` of the original ``__cause__``. @@ -331,28 +340,20 @@ capture data for later printing in a lightweight fashion. some containing internal newlines. :func:`~traceback.print_exception` is a wrapper around this method which just prints the lines to a file. - The message indicating which exception occurred is always the last - string in the output. - .. method:: format_exception_only() Format the exception part of the traceback. The return value is a generator of strings, each ending in a newline. - Normally, the generator emits a single string; however, for - :exc:`SyntaxError` exceptions, it emits several lines that (when - printed) display detailed information about where the syntax - error occurred. - - The message indicating which exception occurred is always the last - string in the output. + The generator emits the exception's message followed by its notes + (if it has any). The exception message is normally a single string; + however, for :exc:`SyntaxError` exceptions, it consists of several + lines that (when printed) display detailed information about where + the syntax error occurred. - .. versionchanged:: 3.10 - Added the *compact* parameter. - - .. versionchanged:: 3.11 - Added the *max_group_width* and *max_group_depth* parameters. + .. versionchanged:: 3.11 + The exception's notes are now included in the output. :class:`StackSummary` Objects From 2a24328db55e10025aa7f8a148ca5b2a7f910655 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 12 Sep 2023 13:41:39 -0700 Subject: [PATCH 302/632] [3.11] GH-104395: Add a link in 'Meta Information' to the docs download page (GH-104443) (#109346) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Co-authored-by: Hugo van Kemenade --- Doc/tools/templates/indexcontent.html | 1 + 1 file changed, 1 insertion(+) diff --git a/Doc/tools/templates/indexcontent.html b/Doc/tools/templates/indexcontent.html index a96746b69fd41b..1e3ab7cfe02fee 100644 --- a/Doc/tools/templates/indexcontent.html +++ b/Doc/tools/templates/indexcontent.html @@ -62,6 +62,7 @@

{{ docstitle|e }}

+ {% endblock %} From df21fdc97357e8af50839a42ab69e3588db7f6df Mon Sep 17 00:00:00 2001 From: Mariatta Date: Tue, 12 Sep 2023 21:24:43 -0700 Subject: [PATCH 303/632] [3.11] Update workflow permissions in require-pr-label Action (GH-109342) (#109354) Change the permission from `read` to `write`.. (cherry picked from commit 44c8699196c1951037bc549c895ea5af26c7254e) --- .github/workflows/require-pr-label.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/require-pr-label.yml b/.github/workflows/require-pr-label.yml index 916bbeb4352734..080204bcfd3b94 100644 --- a/.github/workflows/require-pr-label.yml +++ b/.github/workflows/require-pr-label.yml @@ -4,6 +4,10 @@ on: pull_request: types: [opened, reopened, labeled, unlabeled, synchronize] +permissions: + issues: write + pull-requests: write + jobs: label: name: DO-NOT-MERGE / unresolved review @@ -11,7 +15,7 @@ jobs: timeout-minutes: 10 steps: - - uses: mheap/github-action-required-labels@v4 + - uses: mheap/github-action-required-labels@v5 with: mode: exactly count: 0 From 66c0d0ac8c55be9c973be1189b0e9ffcfdfb35a4 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 12 Sep 2023 21:48:44 -0700 Subject: [PATCH 304/632] [3.11] gh-104736: Fix test_gdb tests on ppc64le with clang (GH-109360) (#109362) gh-104736: Fix test_gdb tests on ppc64le with clang (GH-109360) Fix test_gdb on Python built with LLVM clang 16 on Linux ppc64le (ex: Fedora 38). Search patterns in gdb "bt" command output to detect when gdb fails to retrieve the traceback. For example, skip a test if "Backtrace stopped: frame did not save the PC" is found. (cherry picked from commit 44d9a71ea246e7c3fb478d9be62c16914be6c545) Co-authored-by: Victor Stinner --- Lib/test/test_gdb.py | 8 ++++++++ .../Tests/2023-09-13-05-58-09.gh-issue-104736.lA25Fu.rst | 4 ++++ 2 files changed, 12 insertions(+) create mode 100644 Misc/NEWS.d/next/Tests/2023-09-13-05-58-09.gh-issue-104736.lA25Fu.rst diff --git a/Lib/test/test_gdb.py b/Lib/test/test_gdb.py index d1f9a03a41fd78..5d042a45587a58 100644 --- a/Lib/test/test_gdb.py +++ b/Lib/test/test_gdb.py @@ -246,6 +246,14 @@ def get_stack_trace(self, source=None, script=None, # gh-91960: On Python built with "clang -Og", gdb gets # "frame=" for _PyEval_EvalFrameDefault() parameter '(unable to read python frame information)', + # gh-104736: On Python built with "clang -Og" on ppc64le, + # "py-bt" displays a truncated or not traceback, but "where" + # logs this error message: + 'Backtrace stopped: frame did not save the PC', + # gh-104736: When "bt" command displays something like: + # "#1 0x0000000000000000 in ?? ()", the traceback is likely + # truncated or wrong. + ' ?? ()', ): if pattern in out: raise unittest.SkipTest(f"{pattern!r} found in gdb output") diff --git a/Misc/NEWS.d/next/Tests/2023-09-13-05-58-09.gh-issue-104736.lA25Fu.rst b/Misc/NEWS.d/next/Tests/2023-09-13-05-58-09.gh-issue-104736.lA25Fu.rst new file mode 100644 index 00000000000000..85c370fc87ac41 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-09-13-05-58-09.gh-issue-104736.lA25Fu.rst @@ -0,0 +1,4 @@ +Fix test_gdb on Python built with LLVM clang 16 on Linux ppc64le (ex: Fedora +38). Search patterns in gdb "bt" command output to detect when gdb fails to +retrieve the traceback. For example, skip a test if ``Backtrace stopped: frame +did not save the PC`` is found. Patch by Victor Stinner. From f1f85a42eafd31720cf905c5407ca3e043946698 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 13 Sep 2023 09:32:08 -0700 Subject: [PATCH 305/632] [3.11] gh-109351: Fix crash when compiling AST with invalid NamedExpr (GH-109352) (#109380) gh-109351: Fix crash when compiling AST with invalid NamedExpr (GH-109352) (cherry picked from commit 79101edb03b7381b514126c68acabfcbbba2f842) Co-authored-by: Jelle Zijlstra --- Lib/test/test_compile.py | 27 +++++++++++++++++++ ...-09-12-16-00-42.gh-issue-109351.kznGeR.rst | 2 ++ Python/ast.c | 5 ++++ 3 files changed, 34 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-09-12-16-00-42.gh-issue-109351.kznGeR.rst diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index b286ab7cfe7e6b..408064919b0b49 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -442,6 +442,33 @@ def f(): self.assertIn("_A__mangled_mod", A.f.__code__.co_varnames) self.assertIn("__package__", A.f.__code__.co_varnames) + def test_compile_invalid_namedexpr(self): + # gh-109351 + m = ast.Module( + body=[ + ast.Expr( + value=ast.ListComp( + elt=ast.NamedExpr( + target=ast.Constant(value=1), + value=ast.Constant(value=3), + ), + generators=[ + ast.comprehension( + target=ast.Name(id="x", ctx=ast.Store()), + iter=ast.Name(id="y", ctx=ast.Load()), + ifs=[], + is_async=0, + ) + ], + ) + ) + ], + type_ignores=[], + ) + + with self.assertRaisesRegex(TypeError, "NamedExpr target must be a Name"): + compile(ast.fix_missing_locations(m), "", "exec") + def test_compile_ast(self): fname = __file__ if fname.lower().endswith('pyc'): diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-09-12-16-00-42.gh-issue-109351.kznGeR.rst b/Misc/NEWS.d/next/Core and Builtins/2023-09-12-16-00-42.gh-issue-109351.kznGeR.rst new file mode 100644 index 00000000000000..23b81c1c0a3baa --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-09-12-16-00-42.gh-issue-109351.kznGeR.rst @@ -0,0 +1,2 @@ +Fix crash when compiling an invalid AST involving a named (walrus) +expression. diff --git a/Python/ast.c b/Python/ast.c index 95179cb70281b2..8bc3c9623731be 100644 --- a/Python/ast.c +++ b/Python/ast.c @@ -379,6 +379,11 @@ validate_expr(struct validator *state, expr_ty exp, expr_context_ty ctx) ret = validate_exprs(state, exp->v.Tuple.elts, ctx, 0); break; case NamedExpr_kind: + if (exp->v.NamedExpr.target->kind != Name_kind) { + PyErr_SetString(PyExc_TypeError, + "NamedExpr target must be a Name"); + return 0; + } ret = validate_expr(state, exp->v.NamedExpr.value, Load); break; /* This last case doesn't have any checking. */ From 8e23cd0bbbd00f73cadb94e3327bd799c70f97d3 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 14 Sep 2023 11:41:26 -0700 Subject: [PATCH 306/632] [3.11] gh-101100: Fix sphinx warnings in `turtle.rst` (GH-109394) (#109417) (cherry picked from commit 21e80f4c1925aaafae199840f8737b5c39a82c70) Co-authored-by: Nikita Sobolev --- Doc/library/turtle.rst | 8 ++++---- Doc/tools/.nitignore | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Doc/library/turtle.rst b/Doc/library/turtle.rst index a36948360de499..b56f84bfe2f942 100644 --- a/Doc/library/turtle.rst +++ b/Doc/library/turtle.rst @@ -2298,7 +2298,7 @@ Public classes .. class:: RawTurtle(canvas) RawPen(canvas) - :param canvas: a :class:`tkinter.Canvas`, a :class:`ScrolledCanvas` or a + :param canvas: a :class:`!tkinter.Canvas`, a :class:`ScrolledCanvas` or a :class:`TurtleScreen` Create a turtle. The turtle has all methods described above as "methods of @@ -2313,7 +2313,7 @@ Public classes .. class:: TurtleScreen(cv) - :param cv: a :class:`tkinter.Canvas` + :param cv: a :class:`!tkinter.Canvas` Provides screen oriented methods like :func:`bgcolor` etc. that are described above. @@ -2397,7 +2397,7 @@ instance if one is not already present. ``Turtle`` is a subclass of :class:`RawTurtle`, which *doesn't* automatically create a drawing surface - a *canvas* will need to be provided or created for -it. The *canvas* can be a :class:`tkinter.Canvas`, :class:`ScrolledCanvas` +it. The *canvas* can be a :class:`!tkinter.Canvas`, :class:`ScrolledCanvas` or :class:`TurtleScreen`. @@ -2405,7 +2405,7 @@ or :class:`TurtleScreen`. turtle. :class:`Screen` is a subclass of ``TurtleScreen``, and includes :ref:`some additional methods ` for managing its appearance (including size and title) and behaviour. ``TurtleScreen``'s -constructor needs a :class:`tkinter.Canvas` or a +constructor needs a :class:`!tkinter.Canvas` or a :class:`ScrolledCanvas` as an argument. The functional interface for turtle graphics uses the various methods of diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index f4bb6315449402..f9494ff51165b1 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -132,7 +132,6 @@ Doc/library/tkinter.scrolledtext.rst Doc/library/tkinter.ttk.rst Doc/library/traceback.rst Doc/library/tty.rst -Doc/library/turtle.rst Doc/library/unittest.mock.rst Doc/library/unittest.rst Doc/library/urllib.parse.rst From f7bfac4b3dd30920f97a542fd78c355ce62aa267 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 14 Sep 2023 15:52:40 -0700 Subject: [PATCH 307/632] [3.11] gh-109396: Fix test_socket.test_hmac_sha1() in FIPS mode (GH-109423) (#109427) gh-109396: Fix test_socket.test_hmac_sha1() in FIPS mode (GH-109423) Use a longer key: FIPS mode requires at least of at least 112 bits. The previous key was only 32 bits. (cherry picked from commit e091b9f20fa8e409003af79f3c468b8225e6dcd3) Co-authored-by: Victor Stinner --- Lib/test/test_socket.py | 10 +++++++--- .../2023-09-14-22-58-47.gh-issue-109396.J1a4jR.rst | 3 +++ 2 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2023-09-14-22-58-47.gh-issue-109396.J1a4jR.rst diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 602603d51b1bfd..af0d2a4e6f630a 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -6357,12 +6357,16 @@ def test_sha256(self): self.assertEqual(op.recv(512), expected) def test_hmac_sha1(self): - expected = bytes.fromhex("effcdf6ae5eb2fa2d27416d5f184df9c259a7c79") + # gh-109396: In FIPS mode, Linux 6.5 requires a key + # of at least 112 bits. Use a key of 152 bits. + key = b"Python loves AF_ALG" + data = b"what do ya want for nothing?" + expected = bytes.fromhex("193dbb43c6297b47ea6277ec0ce67119a3f3aa66") with self.create_alg('hash', 'hmac(sha1)') as algo: - algo.setsockopt(socket.SOL_ALG, socket.ALG_SET_KEY, b"Jefe") + algo.setsockopt(socket.SOL_ALG, socket.ALG_SET_KEY, key) op, _ = algo.accept() with op: - op.sendall(b"what do ya want for nothing?") + op.sendall(data) self.assertEqual(op.recv(512), expected) # Although it should work with 3.19 and newer the test blocks on diff --git a/Misc/NEWS.d/next/Tests/2023-09-14-22-58-47.gh-issue-109396.J1a4jR.rst b/Misc/NEWS.d/next/Tests/2023-09-14-22-58-47.gh-issue-109396.J1a4jR.rst new file mode 100644 index 00000000000000..71150ecae76434 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-09-14-22-58-47.gh-issue-109396.J1a4jR.rst @@ -0,0 +1,3 @@ +Fix ``test_socket.test_hmac_sha1()`` in FIPS mode. Use a longer key: FIPS +mode requires at least of at least 112 bits. The previous key was only 32 +bits. Patch by Victor Stinner. From 7a20797056173b4628b5e23d2122a665ad7f5ee5 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 15 Sep 2023 02:07:28 +0200 Subject: [PATCH 308/632] [3.11] gh-108822: Fix regrtest clear_caches() (#109432) gh-108822: Fix regrtest clear_caches() Python 3.11 doesn't have fractions._hash_algorithm cache. --- Lib/test/libregrtest/utils.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Lib/test/libregrtest/utils.py b/Lib/test/libregrtest/utils.py index bb64f997d3500a..751dc04384da06 100644 --- a/Lib/test/libregrtest/utils.py +++ b/Lib/test/libregrtest/utils.py @@ -212,13 +212,6 @@ def clear_caches(): for f in typing._cleanups: f() - try: - fractions = sys.modules['fractions'] - except KeyError: - pass - else: - fractions._hash_algorithm.cache_clear() - def get_build_info(): # Get most important configure and build options as a list of strings. From a95d159bba5756469222398660c378a7e4d253b7 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Thu, 14 Sep 2023 23:45:23 -0600 Subject: [PATCH 309/632] [3.11] gh-60283: Check for redefined test names in CI (#109161) (#109366) Co-authored-by: Alex Waygood Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> (cherry picked from commit 3cb9a8edca6e3fa0f0045b03a9a6444cf8f7affe) --- .github/CODEOWNERS | 1 + .github/workflows/build.yml | 2 +- .github/workflows/lint.yml | 4 ++++ .pre-commit-config.yaml | 10 ++++++++++ Lib/test/.ruff.toml | 30 ++++++++++++++++++++++++++++++ 5 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 Lib/test/.ruff.toml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 99d701daa1d49a..1b156c829cd9aa 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -9,6 +9,7 @@ # pre-commit .pre-commit-config.yaml @hugovk @AlexWaygood +.ruff.toml @hugovk @AlexWaygood # asyncio **/*asyncio* @1st1 @asvetlov diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 30d2a973a31d3d..4baa65cf948d2d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -62,7 +62,7 @@ jobs: # into the PR branch anyway. # # https://github.com/python/core-workflow/issues/373 - git diff --name-only origin/$GITHUB_BASE_REF.. | grep -qvE '(\.rst$|^Doc|^Misc)' && echo "run_tests=true" >> $GITHUB_OUTPUT || true + git diff --name-only origin/$GITHUB_BASE_REF.. | grep -qvE '(\.rst$|^Doc|^Misc|^\.pre-commit-config\.yaml$|\.ruff\.toml$)' && echo "run_tests=true" >> $GITHUB_OUTPUT || true git diff --name-only origin/$GITHUB_BASE_REF.. | grep -qE '(ssl|hashlib|hmac|^.github)' && echo "run_ssl_tests=true" >> $GITHUB_OUTPUT || true fi - name: Compute hash for config cache key diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 27b04ba1d412e3..89f65816b6969d 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -5,6 +5,10 @@ on: [push, pull_request, workflow_dispatch] permissions: contents: read +env: + FORCE_COLOR: 1 + RUFF_FORMAT: github + concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 889bd8502a10ea..980b616559ff32 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,17 @@ repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.0.288 + hooks: + - id: ruff + name: Run Ruff on Lib/test/ + args: [--exit-non-zero-on-fix] + files: ^Lib/test/ + - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 hooks: + - id: check-toml + exclude: ^Lib/test/test_tomllib/ - id: check-yaml - id: trailing-whitespace types_or: [c, python, rst] diff --git a/Lib/test/.ruff.toml b/Lib/test/.ruff.toml new file mode 100644 index 00000000000000..24f3f8c65dcf5f --- /dev/null +++ b/Lib/test/.ruff.toml @@ -0,0 +1,30 @@ +fix = true +select = [ + "F811", # Redefinition of unused variable (useful for finding test methods with the same name) +] +extend-exclude = [ + # Failed to lint + "badsyntax_pep3120.py", + "encoded_modules/module_iso_8859_1.py", + "encoded_modules/module_koi8_r.py", + "test_source_encoding.py", + # Failed to parse + "badsyntax_3131.py", + "test_fstring.py", + # TODO Fix: F811 Redefinition of unused name + "test_buffer.py", + "test_dataclasses.py", + "test_descr.py", + "test_enum.py", + "test_functools.py", + "test_genericclass.py", + "test_grammar.py", + "test_import/__init__.py", + "test_keywordonlyarg.py", + "test_pkg.py", + "test_subclassinit.py", + "test_tokenize.py", + "test_typing.py", + "test_yield_from.py", + "time_hashlib.py", +] From 72ffd803b8f308868be8a6ca7ed12fe71cce21a3 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 15 Sep 2023 02:10:41 -0700 Subject: [PATCH 310/632] [3.11] gh-109395: Remove skipped coverage job from Azure Pipelines (GH-109412) (#109434) gh-109395: Remove skipped coverage job from Azure Pipelines (GH-109412) (cherry picked from commit fa493900fbf19cbfac44164f3d8acb4f598ff3c1) Co-authored-by: Hugo van Kemenade --- .azure-pipelines/ci.yml | 30 ------------------- .azure-pipelines/posix-steps.yml | 50 ++++++-------------------------- .azure-pipelines/pr.yml | 30 ------------------- 3 files changed, 9 insertions(+), 101 deletions(-) diff --git a/.azure-pipelines/ci.yml b/.azure-pipelines/ci.yml index 63252a76abb69f..ad1576a88fdff7 100644 --- a/.azure-pipelines/ci.yml +++ b/.azure-pipelines/ci.yml @@ -1,6 +1,3 @@ -variables: - coverage: false - trigger: ['main', '3.11', '3.10', '3.9', '3.8', '3.7'] jobs: @@ -51,33 +48,6 @@ jobs: dependencies: apt -- job: Ubuntu_Coverage_CI_Tests - displayName: Ubuntu CI Tests (coverage) - dependsOn: Prebuild - condition: | - and( - and( - succeeded(), - eq(variables['coverage'], 'true') - ), - eq(dependencies.Prebuild.outputs['tests.run'], 'true') - ) - - pool: - vmImage: ubuntu-22.04 - - variables: - testRunTitle: '$(Build.SourceBranchName)-linux-coverage' - testRunPlatform: linux-coverage - openssl_version: 1.1.1u - - steps: - - template: ./posix-steps.yml - parameters: - dependencies: apt - coverage: true - - - job: Windows_CI_Tests displayName: Windows CI Tests dependsOn: Prebuild diff --git a/.azure-pipelines/posix-steps.yml b/.azure-pipelines/posix-steps.yml index 29b43e0934472e..9b6c30e0e934bf 100644 --- a/.azure-pipelines/posix-steps.yml +++ b/.azure-pipelines/posix-steps.yml @@ -1,5 +1,4 @@ parameters: - coverage: false sudo_dependencies: sudo dependencies: apt patchcheck: true @@ -23,47 +22,16 @@ steps: - script: make -j4 displayName: 'Build CPython' -- ${{ if eq(parameters.coverage, 'true') }}: - - script: ./python -m venv venv && ./venv/bin/python -m pip install -U coverage - displayName: 'Set up virtual environment' +- script: make pythoninfo + displayName: 'Display build info' - - script: ./venv/bin/python -m test.pythoninfo - displayName: 'Display build info' - - - script: | - $COMMAND -m coverage run --pylib -m test \ - --fail-env-changed \ - -uall,-cpu \ - --junit-xml=$(build.binariesDirectory)/test-results.xml \ - -x test_multiprocessing_fork \ - -x test_multiprocessing_forkserver \ - -x test_multiprocessing_spawn \ - -x test_concurrent_futures - displayName: 'Tests with coverage' - env: - ${{ if eq(parameters.xvfb, 'true') }}: - COMMAND: xvfb-run ./venv/bin/python - ${{ if ne(parameters.xvfb, 'true') }}: - COMMAND: ./venv/bin/python - - - script: ./venv/bin/python -m coverage xml - displayName: 'Generate coverage.xml' - - - script: source ./venv/bin/activate && bash <(curl -s https://codecov.io/bash) -y .github/codecov.yml - displayName: 'Publish code coverage results' - - -- ${{ if ne(parameters.coverage, 'true') }}: - - script: make pythoninfo - displayName: 'Display build info' - - - script: $COMMAND buildbottest TESTOPTS="-j4 -uall,-cpu --junit-xml=$(build.binariesDirectory)/test-results.xml" - displayName: 'Tests' - env: - ${{ if eq(parameters.xvfb, 'true') }}: - COMMAND: xvfb-run make - ${{ if ne(parameters.xvfb, 'true') }}: - COMMAND: make +- script: $COMMAND buildbottest TESTOPTS="-j4 -uall,-cpu --junit-xml=$(build.binariesDirectory)/test-results.xml" + displayName: 'Tests' + env: + ${{ if eq(parameters.xvfb, 'true') }}: + COMMAND: xvfb-run make + ${{ if ne(parameters.xvfb, 'true') }}: + COMMAND: make - ${{ if eq(parameters.patchcheck, 'true') }}: - script: | diff --git a/.azure-pipelines/pr.yml b/.azure-pipelines/pr.yml index 939c9b4249a3c2..5f052368a469c7 100644 --- a/.azure-pipelines/pr.yml +++ b/.azure-pipelines/pr.yml @@ -1,6 +1,3 @@ -variables: - coverage: false - pr: ['main', '3.11', '3.10', '3.9', '3.8', '3.7'] jobs: @@ -53,33 +50,6 @@ jobs: dependencies: apt -- job: Ubuntu_Coverage_PR_Tests - displayName: Ubuntu PR Tests (coverage) - dependsOn: Prebuild - condition: | - and( - and( - succeeded(), - eq(variables['coverage'], 'true') - ), - eq(dependencies.Prebuild.outputs['tests.run'], 'true') - ) - - pool: - vmImage: ubuntu-22.04 - - variables: - testRunTitle: '$(Build.SourceBranchName)-linux-coverage' - testRunPlatform: linux-coverage - openssl_version: 1.1.1u - - steps: - - template: ./posix-steps.yml - parameters: - dependencies: apt - coverage: true - - - job: Windows_PR_Tests displayName: Windows PR Tests dependsOn: Prebuild From e6e6767d2ed54f9994b2531b5ad277e3d6d420c5 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 15 Sep 2023 02:18:54 -0700 Subject: [PATCH 311/632] [3.11] gh-109395: Remove skipped macOS builds from Azure Pipelines (GH-109400) (#109442) Co-authored-by: Nikita Sobolev --- .azure-pipelines/ci.yml | 18 ------------------ .azure-pipelines/macos-steps.yml | 27 --------------------------- .azure-pipelines/pr.yml | 20 -------------------- 3 files changed, 65 deletions(-) delete mode 100644 .azure-pipelines/macos-steps.yml diff --git a/.azure-pipelines/ci.yml b/.azure-pipelines/ci.yml index ad1576a88fdff7..246e059f5d1842 100644 --- a/.azure-pipelines/ci.yml +++ b/.azure-pipelines/ci.yml @@ -11,24 +11,6 @@ jobs: - template: ./prebuild-checks.yml -- job: macOS_CI_Tests - displayName: macOS CI Tests - dependsOn: Prebuild - #condition: and(succeeded(), eq(dependencies.Prebuild.outputs['tests.run'], 'true')) - # bpo-39837: macOS tests on Azure Pipelines are disabled - condition: false - - variables: - testRunTitle: '$(build.sourceBranchName)-macos' - testRunPlatform: macos - - pool: - vmImage: macos-10.15 - - steps: - - template: ./macos-steps.yml - - - job: Ubuntu_CI_Tests displayName: Ubuntu CI Tests dependsOn: Prebuild diff --git a/.azure-pipelines/macos-steps.yml b/.azure-pipelines/macos-steps.yml deleted file mode 100644 index fa38a0df8c87b8..00000000000000 --- a/.azure-pipelines/macos-steps.yml +++ /dev/null @@ -1,27 +0,0 @@ -steps: -- checkout: self - clean: true - fetchDepth: 5 - -- script: ./configure --with-pydebug --with-openssl=/usr/local/opt/openssl --prefix=/opt/python-azdev - displayName: 'Configure CPython (debug)' - -- script: make -j4 - displayName: 'Build CPython' - -- script: make pythoninfo - displayName: 'Display build info' - -- script: make buildbottest TESTOPTS="-j4 -uall,-cpu --junit-xml=$(build.binariesDirectory)/test-results.xml" - displayName: 'Tests' - continueOnError: true - timeoutInMinutes: 30 - -- task: PublishTestResults@2 - displayName: 'Publish Test Results' - inputs: - testResultsFiles: '$(build.binariesDirectory)/test-results.xml' - mergeTestResults: true - testRunTitle: $(testRunTitle) - platform: $(testRunPlatform) - condition: succeededOrFailed() diff --git a/.azure-pipelines/pr.yml b/.azure-pipelines/pr.yml index 5f052368a469c7..0836c780c1cf95 100644 --- a/.azure-pipelines/pr.yml +++ b/.azure-pipelines/pr.yml @@ -11,26 +11,6 @@ jobs: - template: ./prebuild-checks.yml -- job: macOS_PR_Tests - displayName: macOS PR Tests - dependsOn: Prebuild - #condition: and(succeeded(), eq(dependencies.Prebuild.outputs['tests.run'], 'true')) - # bpo-39837: macOS tests on Azure Pipelines are disabled - condition: false - - variables: - testRunTitle: '$(system.pullRequest.TargetBranch)-macos' - testRunPlatform: macos - - pool: - vmImage: macos-10.15 - - steps: - - template: ./macos-steps.yml - parameters: - targetBranch: $(System.PullRequest.TargetBranch) - - - job: Ubuntu_PR_Tests displayName: Ubuntu PR Tests dependsOn: Prebuild From 5a2550554bedad3c8e38c7c035c5da42f1defffb Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 15 Sep 2023 06:10:07 -0700 Subject: [PATCH 312/632] [3.11] Docs: Superseded modules: list only module names (GH-109439) (#109446) Co-authored-by: Hugo van Kemenade --- Doc/library/superseded.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Doc/library/superseded.rst b/Doc/library/superseded.rst index b38f16691f6ea9..25cbe6c94e8ec5 100644 --- a/Doc/library/superseded.rst +++ b/Doc/library/superseded.rst @@ -9,6 +9,7 @@ backwards compatibility. They have been superseded by other modules. .. toctree:: + :maxdepth: 1 aifc.rst asynchat.rst From 43191d294c6af1d1811ae51db4a3377bf5c955dc Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 15 Sep 2023 15:29:57 +0200 Subject: [PATCH 313/632] [3.11] Fix date.__repr__() docstring (#109422) (#109449) Fix date.__repr__() docstring (#109422) (cherry picked from commit 5eec58a9e57383128ade7b527965b1efc474735b) Co-authored-by: Christopher Yeh --- Lib/datetime.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Lib/datetime.py b/Lib/datetime.py index 60927de6adeb1a..474b4e9ae5312c 100644 --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -1002,13 +1002,9 @@ def fromisocalendar(cls, year, week, day): def __repr__(self): """Convert to formal string, for repr(). - >>> dt = datetime(2010, 1, 1) - >>> repr(dt) - 'datetime.datetime(2010, 1, 1, 0, 0)' - - >>> dt = datetime(2010, 1, 1, tzinfo=timezone.utc) - >>> repr(dt) - 'datetime.datetime(2010, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)' + >>> d = date(2010, 1, 1) + >>> repr(d) + 'datetime.date(2010, 1, 1)' """ return "%s.%s(%d, %d, %d)" % (self.__class__.__module__, self.__class__.__qualname__, From 7cabbb133a37b364888874455e80b1297d5d8246 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 16 Sep 2023 00:56:16 -0700 Subject: [PATCH 314/632] [3.11] gh-109474: Update two Unix packaging URLs (GH-109307) (#109478) gh-109474: Update two Unix packaging URLs (GH-109307) update packaging URLs fix a broken URL for fedora RPM packaging guide and fix a URL redirect for Slackware packaging guide. (cherry picked from commit 0b38ce440bd76b3d25b6d042ee9613841fb4a947) Co-authored-by: partev --- Doc/using/unix.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/using/unix.rst b/Doc/using/unix.rst index 067ff4cce5e48d..7654e3d3ece640 100644 --- a/Doc/using/unix.rst +++ b/Doc/using/unix.rst @@ -30,9 +30,9 @@ following links: for Debian users https://en.opensuse.org/Portal:Packaging for OpenSuse users - https://docs-old.fedoraproject.org/en-US/Fedora_Draft_Documentation/0.1/html/RPM_Guide/ch-creating-rpms.html + https://docs.fedoraproject.org/en-US/package-maintainers/Packaging_Tutorial_GNU_Hello/ for Fedora users - http://www.slackbook.org/html/package-management-making-packages.html + https://slackbook.org/html/package-management-making-packages.html for Slackware users From e16a9af1cd7359f8ae3447da89bfda2e167f5c73 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 16 Sep 2023 03:14:07 -0700 Subject: [PATCH 315/632] [3.11] gh-109414: Add some basic information about venvs in the introduction. (GH-109440) (GH-109480) (cherry picked from commit a6846d45ff3c836bc859c40e7684b57df991dc05) --- Doc/library/venv.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Doc/library/venv.rst b/Doc/library/venv.rst index 0b9787d095d9ad..f66e874d09f31d 100644 --- a/Doc/library/venv.rst +++ b/Doc/library/venv.rst @@ -30,6 +30,25 @@ When used from within a virtual environment, common installation tools such as `pip`_ will install Python packages into a virtual environment without needing to be told to do so explicitly. +A virtual environment is (amongst other things): + +* Used to contain a specific Python interpreter and software libraries and + binaries which are needed to support a project (library or application). These + are by default isolated from software in other virtual environments and Python + interpreters and libraries installed in the operating system. + +* Contained in a directory, conventionally either named ``venv`` or ``.venv`` in + the project directory, or under a container directory for lots of virtual + environments, such as ``~/.virtualenvs``. + +* Not checked into source control systems such as Git. + +* Considered as disposable -- it should be simple to delete and recreate it from + scratch. You don't place any project code in the environment + +* Not considered as movable or copyable -- you just recreate the same + environment in the target location. + See :pep:`405` for more background on Python virtual environments. .. seealso:: From 9b38cbee9004f9f6aaf5974f71b72b1898ba1dd9 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 16 Sep 2023 07:39:06 -0700 Subject: [PATCH 316/632] [3.11] gh-109451: Fix wrong format specifier in logging documentation (GH-109465) (GH-109482) (cherry picked from commit 929cc4e4a0999b777e1aa94f9c007db720e67f43) --- Doc/library/logging.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst index 1fd1d0966a6120..8741d64a1dbdeb 100644 --- a/Doc/library/logging.rst +++ b/Doc/library/logging.rst @@ -890,7 +890,7 @@ you want to use. In the case of {}-formatting, you can specify formatting flags by placing them after the attribute name, separated from it with a colon. For example: a -placeholder of ``{msecs:03d}`` would format a millisecond value of ``4`` as +placeholder of ``{msecs:03.0f}`` would format a millisecond value of ``4`` as ``004``. Refer to the :meth:`str.format` documentation for full details on the options available to you. From e15b663ee1ef8ddeecf82ef11c69b44b3d40d96b Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 17 Sep 2023 13:24:26 -0600 Subject: [PATCH 317/632] [3.11] gh-109408: Remove Ubuntu unit tests from Azure Pipelines (GH-109452) (#109520) (cherry picked from commit a75daed7e004ee9a53b160307c4c072656176a02) --- .azure-pipelines/ci.yml | 19 ----------------- .azure-pipelines/posix-steps.yml | 36 ++++++-------------------------- .azure-pipelines/pr.yml | 6 ++---- 3 files changed, 8 insertions(+), 53 deletions(-) diff --git a/.azure-pipelines/ci.yml b/.azure-pipelines/ci.yml index 246e059f5d1842..42eb448bc1c66b 100644 --- a/.azure-pipelines/ci.yml +++ b/.azure-pipelines/ci.yml @@ -11,25 +11,6 @@ jobs: - template: ./prebuild-checks.yml -- job: Ubuntu_CI_Tests - displayName: Ubuntu CI Tests - dependsOn: Prebuild - condition: and(succeeded(), eq(dependencies.Prebuild.outputs['tests.run'], 'true')) - - pool: - vmImage: ubuntu-22.04 - - variables: - testRunTitle: '$(build.sourceBranchName)-linux' - testRunPlatform: linux - openssl_version: 1.1.1u - - steps: - - template: ./posix-steps.yml - parameters: - dependencies: apt - - - job: Windows_CI_Tests displayName: Windows CI Tests dependsOn: Prebuild diff --git a/.azure-pipelines/posix-steps.yml b/.azure-pipelines/posix-steps.yml index 9b6c30e0e934bf..db44255e4422ff 100644 --- a/.azure-pipelines/posix-steps.yml +++ b/.azure-pipelines/posix-steps.yml @@ -1,9 +1,3 @@ -parameters: - sudo_dependencies: sudo - dependencies: apt - patchcheck: true - xvfb: true - steps: - checkout: self clean: true @@ -13,7 +7,7 @@ steps: - script: sudo setfacl -Rb /home/vsts displayName: 'Workaround ACL issue' -- script: ${{ parameters.sudo_dependencies }} ./.azure-pipelines/posix-deps-${{ parameters.dependencies }}.sh $(openssl_version) +- script: sudo ./.azure-pipelines/posix-deps-apt.sh $(openssl_version) displayName: 'Install dependencies' - script: ./configure --with-pydebug @@ -25,27 +19,9 @@ steps: - script: make pythoninfo displayName: 'Display build info' -- script: $COMMAND buildbottest TESTOPTS="-j4 -uall,-cpu --junit-xml=$(build.binariesDirectory)/test-results.xml" - displayName: 'Tests' - env: - ${{ if eq(parameters.xvfb, 'true') }}: - COMMAND: xvfb-run make - ${{ if ne(parameters.xvfb, 'true') }}: - COMMAND: make - -- ${{ if eq(parameters.patchcheck, 'true') }}: - - script: | - git fetch origin - ./python Tools/scripts/patchcheck.py --ci true - displayName: 'Run patchcheck.py' - condition: and(succeeded(), eq(variables['Build.Reason'], 'PullRequest')) - +- script: | + git fetch origin + ./python Tools/scripts/patchcheck.py --ci true + displayName: 'Run patchcheck.py' + condition: and(succeeded(), eq(variables['Build.Reason'], 'PullRequest')) -- task: PublishTestResults@2 - displayName: 'Publish Test Results' - inputs: - testResultsFiles: '$(build.binariesDirectory)/test-results.xml' - mergeTestResults: true - testRunTitle: $(testRunTitle) - platform: $(testRunPlatform) - condition: succeededOrFailed() diff --git a/.azure-pipelines/pr.yml b/.azure-pipelines/pr.yml index 0836c780c1cf95..3a8728bcda9272 100644 --- a/.azure-pipelines/pr.yml +++ b/.azure-pipelines/pr.yml @@ -11,8 +11,8 @@ jobs: - template: ./prebuild-checks.yml -- job: Ubuntu_PR_Tests - displayName: Ubuntu PR Tests +- job: Ubuntu_Patchcheck + displayName: Ubuntu patchcheck dependsOn: Prebuild condition: and(succeeded(), eq(dependencies.Prebuild.outputs['tests.run'], 'true')) @@ -26,8 +26,6 @@ jobs: steps: - template: ./posix-steps.yml - parameters: - dependencies: apt - job: Windows_PR_Tests From 355a5cd26295a8be70d7de992efaff52af944746 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 18 Sep 2023 00:34:20 -0700 Subject: [PATCH 318/632] [3.11] Fix extraneous backslashes in hashlib docs (GH-109468) (#109531) Fix extraneous backslashes in hashlib docs (GH-109468) (cherry picked from commit ce5b3e19e6fb940fa72db1b98a8df80f6e464265) Co-authored-by: Anthony Sottile --- Doc/library/hashlib.rst | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Doc/library/hashlib.rst b/Doc/library/hashlib.rst index c77b728dbf3056..957d51978d6d59 100644 --- a/Doc/library/hashlib.rst +++ b/Doc/library/hashlib.rst @@ -131,16 +131,16 @@ Using :func:`new` with an algorithm name: '031edd7d41651593c5fe5c006fa5752b37fddff7bc4e843aa6af0c950f4b9406' -.. function:: md5([, data], \*, usedforsecurity=True) -.. function:: sha1([, data], \*, usedforsecurity=True) -.. function:: sha224([, data], \*, usedforsecurity=True) -.. function:: sha256([, data], \*, usedforsecurity=True) -.. function:: sha384([, data], \*, usedforsecurity=True) -.. function:: sha512([, data], \*, usedforsecurity=True) -.. function:: sha3_224([, data], \*, usedforsecurity=True) -.. function:: sha3_256([, data], \*, usedforsecurity=True) -.. function:: sha3_384([, data], \*, usedforsecurity=True) -.. function:: sha3_512([, data], \*, usedforsecurity=True) +.. function:: md5([, data], *, usedforsecurity=True) +.. function:: sha1([, data], *, usedforsecurity=True) +.. function:: sha224([, data], *, usedforsecurity=True) +.. function:: sha256([, data], *, usedforsecurity=True) +.. function:: sha384([, data], *, usedforsecurity=True) +.. function:: sha512([, data], *, usedforsecurity=True) +.. function:: sha3_224([, data], *, usedforsecurity=True) +.. function:: sha3_256([, data], *, usedforsecurity=True) +.. function:: sha3_384([, data], *, usedforsecurity=True) +.. function:: sha3_512([, data], *, usedforsecurity=True) Named constructors such as these are faster than passing an algorithm name to :func:`new`. @@ -234,8 +234,8 @@ A hash object has the following methods: SHAKE variable length digests ----------------------------- -.. function:: shake_128([, data], \*, usedforsecurity=True) -.. function:: shake_256([, data], \*, usedforsecurity=True) +.. function:: shake_128([, data], *, usedforsecurity=True) +.. function:: shake_256([, data], *, usedforsecurity=True) The :func:`shake_128` and :func:`shake_256` algorithms provide variable length digests with length_in_bits//2 up to 128 or 256 bits of security. From 26b75b82995149ff6ecaece00f2b3025a52f0735 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 18 Sep 2023 06:03:44 -0700 Subject: [PATCH 319/632] [3.11] gh-109408: Azure Pipelines: test 3.12 branch (GH-109453) (#109536) Co-authored-by: Hugo van Kemenade --- .azure-pipelines/ci.yml | 2 +- .azure-pipelines/pr.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.azure-pipelines/ci.yml b/.azure-pipelines/ci.yml index 42eb448bc1c66b..b5b2765e43844f 100644 --- a/.azure-pipelines/ci.yml +++ b/.azure-pipelines/ci.yml @@ -1,4 +1,4 @@ -trigger: ['main', '3.11', '3.10', '3.9', '3.8', '3.7'] +trigger: ['main', '3.12', '3.11', '3.10', '3.9', '3.8', '3.7'] jobs: - job: Prebuild diff --git a/.azure-pipelines/pr.yml b/.azure-pipelines/pr.yml index 3a8728bcda9272..daa2c7ca97df6a 100644 --- a/.azure-pipelines/pr.yml +++ b/.azure-pipelines/pr.yml @@ -1,4 +1,4 @@ -pr: ['main', '3.11', '3.10', '3.9', '3.8', '3.7'] +pr: ['main', '3.12', '3.11', '3.10', '3.9', '3.8', '3.7'] jobs: - job: Prebuild From 2184b76496f8bb39b1b499c821bd6d9dbc81548e Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 18 Sep 2023 07:11:56 -0700 Subject: [PATCH 320/632] [3.11] gh-108843: fix ast.unparse for f-string with many quotes (#108980) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [3.11] gh-108843: fix ast.unparse for f-string with many quotes * 📜🤖 Added by blurb_it. * simplify --------- Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> --- Lib/ast.py | 18 +++++++++++++++++- Lib/test/test_unparse.py | 5 +++++ ...3-09-06-04-30-05.gh-issue-108843.WJMhsS.rst | 1 + 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2023-09-06-04-30-05.gh-issue-108843.WJMhsS.rst diff --git a/Lib/ast.py b/Lib/ast.py index 623b9a1b805d0e..d84d75e1f3a8e1 100644 --- a/Lib/ast.py +++ b/Lib/ast.py @@ -1175,13 +1175,29 @@ def visit_JoinedStr(self, node): new_fstring_parts = [] quote_types = list(_ALL_QUOTES) + fallback_to_repr = False for value, is_constant in fstring_parts: - value, quote_types = self._str_literal_helper( + value, new_quote_types = self._str_literal_helper( value, quote_types=quote_types, escape_special_whitespace=is_constant, ) new_fstring_parts.append(value) + if set(new_quote_types).isdisjoint(quote_types): + fallback_to_repr = True + break + quote_types = new_quote_types + + if fallback_to_repr: + # If we weren't able to find a quote type that works for all parts + # of the JoinedStr, fallback to using repr and triple single quotes. + quote_types = ["'''"] + new_fstring_parts.clear() + for value, is_constant in fstring_parts: + value = repr('"' + value) # force repr to use single quotes + expected_prefix = "'\"" + assert value.startswith(expected_prefix), repr(value) + new_fstring_parts.append(value[len(expected_prefix):-1]) value = "".join(new_fstring_parts) quote_type = quote_types[0] diff --git a/Lib/test/test_unparse.py b/Lib/test/test_unparse.py index f1f1dd5dc26be8..d8f0060b3a4ef9 100644 --- a/Lib/test/test_unparse.py +++ b/Lib/test/test_unparse.py @@ -662,6 +662,11 @@ def test_star_expr_assign_target_multiple(self): self.check_src_roundtrip("[a, b] = [c, d] = [e, f] = g") self.check_src_roundtrip("a, b = [c, d] = e, f = g") + def test_multiquote_joined_string(self): + self.check_ast_roundtrip("f\"'''{1}\\\"\\\"\\\"\" ") + self.check_ast_roundtrip("""f"'''{1}""\\"" """) + self.check_ast_roundtrip("""f'""\"{1}''' """) + self.check_ast_roundtrip("""f'""\"{1}""\\"' """) class DirectoryTestCase(ASTTestCase): diff --git a/Misc/NEWS.d/next/Library/2023-09-06-04-30-05.gh-issue-108843.WJMhsS.rst b/Misc/NEWS.d/next/Library/2023-09-06-04-30-05.gh-issue-108843.WJMhsS.rst new file mode 100644 index 00000000000000..0f15761c14bb7d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-09-06-04-30-05.gh-issue-108843.WJMhsS.rst @@ -0,0 +1 @@ +Fix an issue in :func:`ast.unparse` when unparsing f-strings containing many quote types. From 17a335dd0291d09e1510157a4ebe02932ec632dd Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 18 Sep 2023 11:24:41 -0700 Subject: [PATCH 321/632] [3.11] Fix error handling in _PySys_UpdateConfig() (GH-109524) (GH-109551) (cherry picked from commit c829975428253568d47ebfc3104fa7386b5e0b58) Co-authored-by: Serhiy Storchaka --- Python/sysmodule.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 6e9ec90c9d1c3e..d0941e8cfebeb6 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -3113,7 +3113,9 @@ _PySys_UpdateConfig(PyThreadState *tstate) if (config->pycache_prefix != NULL) { SET_SYS_FROM_WSTR("pycache_prefix", config->pycache_prefix); } else { - PyDict_SetItemString(sysdict, "pycache_prefix", Py_None); + if (PyDict_SetItemString(sysdict, "pycache_prefix", Py_None) < 0) { + return -1; + } } COPY_LIST("argv", config->argv); @@ -3127,7 +3129,9 @@ _PySys_UpdateConfig(PyThreadState *tstate) SET_SYS_FROM_WSTR("_stdlib_dir", stdlibdir); } else { - PyDict_SetItemString(sysdict, "_stdlib_dir", Py_None); + if (PyDict_SetItemString(sysdict, "_stdlib_dir", Py_None) < 0) { + return -1; + } } #undef SET_SYS_FROM_WSTR @@ -3137,6 +3141,9 @@ _PySys_UpdateConfig(PyThreadState *tstate) // sys.flags PyObject *flags = _PySys_GetObject(interp, "flags"); // borrowed ref if (flags == NULL) { + if (!_PyErr_Occurred(tstate)) { + _PyErr_SetString(tstate, PyExc_RuntimeError, "lost sys.flags"); + } return -1; } if (set_flags_from_config(interp, flags) < 0) { From 336dbe56b292f26e14e70975401a3a954fcbd76b Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 20 Sep 2023 16:15:08 +0200 Subject: [PATCH 322/632] [3.11] gh-108973: Fix asyncio SubprocessProtocol doc (#109431) (#109610) gh-108973: Fix asyncio SubprocessProtocol doc (#109431) SubprocessProtocol process_exited() method can be called before pipe_data_received() and pipe_connection_lost() methods. Document it and adapt the example in the doc. Co-authored-by: Davide Rizzo (cherry picked from commit ced6924630037f1e5b3d1dbef2b600152fb07fbb) --- Doc/library/asyncio-llapi-index.rst | 10 +++++----- Doc/library/asyncio-protocol.rst | 19 ++++++++++++++++++- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/Doc/library/asyncio-llapi-index.rst b/Doc/library/asyncio-llapi-index.rst index 9ce48a24444e66..67136ba69ec875 100644 --- a/Doc/library/asyncio-llapi-index.rst +++ b/Doc/library/asyncio-llapi-index.rst @@ -484,19 +484,19 @@ Protocol classes can implement the following **callback methods**: :widths: 50 50 :class: full-width-table - * - ``callback`` :meth:`pipe_data_received() - ` + * - ``callback`` :meth:`~SubprocessProtocol.pipe_data_received` - Called when the child process writes data into its *stdout* or *stderr* pipe. - * - ``callback`` :meth:`pipe_connection_lost() - ` + * - ``callback`` :meth:`~SubprocessProtocol.pipe_connection_lost` - Called when one of the pipes communicating with the child process is closed. * - ``callback`` :meth:`process_exited() ` - - Called when the child process has exited. + - Called when the child process has exited. It can be called before + :meth:`~SubprocessProtocol.pipe_data_received` and + :meth:`~SubprocessProtocol.pipe_connection_lost` methods. Event Loop Policies diff --git a/Doc/library/asyncio-protocol.rst b/Doc/library/asyncio-protocol.rst index 7bc906eaafc1f2..48fa02937b5237 100644 --- a/Doc/library/asyncio-protocol.rst +++ b/Doc/library/asyncio-protocol.rst @@ -708,6 +708,9 @@ factories passed to the :meth:`loop.subprocess_exec` and Called when the child process has exited. + It can be called before :meth:`~SubprocessProtocol.pipe_data_received` and + :meth:`~SubprocessProtocol.pipe_connection_lost` methods. + Examples ======== @@ -1003,12 +1006,26 @@ The subprocess is created by the :meth:`loop.subprocess_exec` method:: def __init__(self, exit_future): self.exit_future = exit_future self.output = bytearray() + self.pipe_closed = False + self.exited = False + + def pipe_connection_lost(self, fd, exc): + self.pipe_closed = True + self.check_for_exit() def pipe_data_received(self, fd, data): self.output.extend(data) def process_exited(self): - self.exit_future.set_result(True) + self.exited = True + # process_exited() method can be called before + # pipe_connection_lost() method: wait until both methods are + # called. + self.check_for_exit() + + def check_for_exit(self): + if self.pipe_closed and self.exited: + self.exit_future.set_result(True) async def get_date(): # Get a reference to the event loop as we plan to use From 8c7fadb9bf86b4be0aad70a121b4d487d7729a64 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 20 Sep 2023 17:45:23 +0200 Subject: [PATCH 323/632] [3.11] gh-103053: Skip test_freeze_simple_script() on PGO build (#109591) (#109616) gh-103053: Skip test_freeze_simple_script() on PGO build (#109591) Skip test_freeze_simple_script() of test_tools.test_freeze if Python is built with "./configure --enable-optimizations", which means with Profile Guided Optimization (PGO): it just makes the test too slow. The freeze tool is tested by many other CIs with other (faster) compiler flags. test.pythoninfo now gets also get_build_info() of test.libregrtests.utils. (cherry picked from commit 81cd1bd713624c3d26b647f3d28f2fd905887a0d) --- Lib/test/libregrtest/utils.py | 12 ++---------- Lib/test/pythoninfo.py | 10 ++++++++++ Lib/test/support/__init__.py | 15 +++++++++++++++ Lib/test/test_tools/test_freeze.py | 4 ++++ ...2023-09-20-02-32-17.gh-issue-103053.AoUJuK.rst | 4 ++++ 5 files changed, 35 insertions(+), 10 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2023-09-20-02-32-17.gh-issue-103053.AoUJuK.rst diff --git a/Lib/test/libregrtest/utils.py b/Lib/test/libregrtest/utils.py index 751dc04384da06..49f3a236f3d14b 100644 --- a/Lib/test/libregrtest/utils.py +++ b/Lib/test/libregrtest/utils.py @@ -259,16 +259,8 @@ def get_build_info(): elif '-flto' in ldflags_nodist: optimizations.append('LTO') - # --enable-optimizations - pgo_options = ( - # GCC - '-fprofile-use', - # clang: -fprofile-instr-use=code.profclangd - '-fprofile-instr-use', - # ICC - "-prof-use", - ) - if any(option in cflags_nodist for option in pgo_options): + if support.check_cflags_pgo(): + # PGO (--enable-optimizations) optimizations.append('PGO') if optimizations: build.append('+'.join(optimizations)) diff --git a/Lib/test/pythoninfo.py b/Lib/test/pythoninfo.py index 7a9c01b8c74a62..69d2fc0d061a55 100644 --- a/Lib/test/pythoninfo.py +++ b/Lib/test/pythoninfo.py @@ -875,6 +875,15 @@ def collect_fips(info_add): pass +def collect_libregrtest_utils(info_add): + try: + from test.libregrtest import utils + except ImportError: + return + + info_add('libregrtests.build_info', ' '.join(utils.get_build_info())) + + def collect_info(info): error = False info_add = info.add @@ -912,6 +921,7 @@ def collect_info(info): collect_tkinter, collect_windows, collect_zlib, + collect_libregrtest_utils, # Collecting from tests should be last as they have side effects. collect_test_socket, diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 05b33753724dbc..b4fa727aae3413 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -765,6 +765,21 @@ def python_is_optimized(): return final_opt not in ('', '-O0', '-Og') +def check_cflags_pgo(): + # Check if Python was built with ./configure --enable-optimizations: + # with Profile Guided Optimization (PGO). + cflags_nodist = sysconfig.get_config_var('PY_CFLAGS_NODIST') or '' + pgo_options = ( + # GCC + '-fprofile-use', + # clang: -fprofile-instr-use=code.profclangd + '-fprofile-instr-use', + # ICC + "-prof-use", + ) + return any(option in cflags_nodist for option in pgo_options) + + _header = 'nP' _align = '0n' if hasattr(sys, "getobjects"): diff --git a/Lib/test/test_tools/test_freeze.py b/Lib/test/test_tools/test_freeze.py index 922e74b441457a..671ec2961e7f8f 100644 --- a/Lib/test/test_tools/test_freeze.py +++ b/Lib/test/test_tools/test_freeze.py @@ -15,6 +15,10 @@ @support.requires_zlib() @unittest.skipIf(sys.platform.startswith('win'), 'not supported on Windows') @support.skip_if_buildbot('not all buildbots have enough space') +# gh-103053: Skip test if Python is built with Profile Guided Optimization +# (PGO), since the test is just too slow in this case. +@unittest.skipIf(support.check_cflags_pgo(), + 'test is too slow with PGO') class TestFreeze(unittest.TestCase): @support.requires_resource('cpu') # Building Python is slow diff --git a/Misc/NEWS.d/next/Tests/2023-09-20-02-32-17.gh-issue-103053.AoUJuK.rst b/Misc/NEWS.d/next/Tests/2023-09-20-02-32-17.gh-issue-103053.AoUJuK.rst new file mode 100644 index 00000000000000..6d67bf237bdbb2 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-09-20-02-32-17.gh-issue-103053.AoUJuK.rst @@ -0,0 +1,4 @@ +Skip test_freeze_simple_script() of test_tools.test_freeze if Python is built +with ``./configure --enable-optimizations``, which means with Profile Guided +Optimization (PGO): it just makes the test too slow. The freeze tool is tested +by many other CIs with other (faster) compiler flags. Patch by Victor Stinner. From a023de65fc29401f95b77750a29af522c24ebab5 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Wed, 20 Sep 2023 18:50:48 +0100 Subject: [PATCH 324/632] [3.11] Fix typos in docs and comments (#109619) (#109622) Co-authored-by: Heinz-Alexander Fuetterer <35225576+afuetterer@users.noreply.github.com> --- Doc/library/typing.rst | 2 +- Doc/whatsnew/3.5.rst | 2 +- Lib/test/test_descr.py | 2 +- Lib/test/test_frame.py | 2 +- Lib/test/test_unpack.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 09332c7c213374..79f3abd865f6eb 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1233,7 +1233,7 @@ These can be used as types in annotations. They all support subscription using completely disables typechecking for a function or class. The responsibility of how to interpret the metadata - lies with the the tool or library encountering an + lies with the tool or library encountering an ``Annotated`` annotation. A tool or library encountering an ``Annotated`` type can scan through the metadata elements to determine if they are of interest (e.g., using :func:`isinstance`). diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst index 6d911096c17398..f104e4da5649fb 100644 --- a/Doc/whatsnew/3.5.rst +++ b/Doc/whatsnew/3.5.rst @@ -921,7 +921,7 @@ and improves their substitutability for lists. Docstrings produced by :func:`~collections.namedtuple` can now be updated:: Point = namedtuple('Point', ['x', 'y']) - Point.__doc__ += ': Cartesian coodinate' + Point.__doc__ += ': Cartesian coordinate' Point.x.__doc__ = 'abscissa' Point.y.__doc__ = 'ordinate' diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 2a12695031d34a..e356624af04e7e 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -1980,7 +1980,7 @@ def __getattr__(self, attr): ns = {} exec(code, ns) number_attrs = ns["number_attrs"] - # Warm up the the function for quickening (PEP 659) + # Warm up the function for quickening (PEP 659) for _ in range(30): self.assertEqual(number_attrs(Numbers()), list(range(280))) diff --git a/Lib/test/test_frame.py b/Lib/test/test_frame.py index 9cb2686f17597a..1e52164a6ba0fc 100644 --- a/Lib/test/test_frame.py +++ b/Lib/test/test_frame.py @@ -317,7 +317,7 @@ def f(): sneaky_frame_object = None gc.enable() next(g) - # g.gi_frame should be the the frame object from the callback (the + # g.gi_frame should be the frame object from the callback (the # one that was *requested* second, but *created* first): self.assertIs(g.gi_frame, sneaky_frame_object) finally: diff --git a/Lib/test/test_unpack.py b/Lib/test/test_unpack.py index f5ca1d455b5c6f..515ec128a08a9c 100644 --- a/Lib/test/test_unpack.py +++ b/Lib/test/test_unpack.py @@ -162,7 +162,7 @@ def test_extended_oparg_not_ignored(self): ns = {} exec(code, ns) unpack_400 = ns["unpack_400"] - # Warm up the the function for quickening (PEP 659) + # Warm up the function for quickening (PEP 659) for _ in range(30): y = unpack_400(range(400)) self.assertEqual(y, 399) From 9414ddf91898892f3f6a672ae946931ee4b3ceb7 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 20 Sep 2023 13:27:39 -0600 Subject: [PATCH 325/632] [3.11] gh-109408: Move Windows builds from Azure Pipelines PR to GitHub Actions (GH-109569) (#109624) --- .azure-pipelines/pr.yml | 31 ------------------------------- .github/workflows/build.yml | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+), 31 deletions(-) diff --git a/.azure-pipelines/pr.yml b/.azure-pipelines/pr.yml index daa2c7ca97df6a..335a4b407cb83c 100644 --- a/.azure-pipelines/pr.yml +++ b/.azure-pipelines/pr.yml @@ -26,34 +26,3 @@ jobs: steps: - template: ./posix-steps.yml - - -- job: Windows_PR_Tests - displayName: Windows PR Tests - dependsOn: Prebuild - condition: and(succeeded(), eq(dependencies.Prebuild.outputs['tests.run'], 'true')) - - pool: - vmImage: windows-2022 - - strategy: - matrix: - win32: - arch: win32 - buildOpt: '-p Win32' - testRunTitle: '$(System.PullRequest.TargetBranch)-win32' - testRunPlatform: win32 - win64: - arch: amd64 - buildOpt: '-p x64' - testRunTitle: '$(System.PullRequest.TargetBranch)-win64' - testRunPlatform: win64 - winarm64: - arch: arm64 - buildOpt: '-p arm64' - maxParallel: 4 - - steps: - - template: ./windows-steps.yml - parameters: - targetBranch: $(System.PullRequest.TargetBranch) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4baa65cf948d2d..4709c3d7659c6a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -150,6 +150,8 @@ jobs: path: config.cache key: ${{ github.job }}-${{ runner.os }}-${{ needs.check_source.outputs.config_hash }} - uses: actions/setup-python@v4 + with: + python-version: '3.x' - name: Install Dependencies run: sudo ./.github/workflows/posix-deps-apt.sh - name: Add ccache to PATH @@ -230,6 +232,21 @@ jobs: - name: Tests run: .\PCbuild\rt.bat -p x64 -d -q -uall -u-cpu -rwW --slowest --timeout=1200 -j0 + build_win_arm64: + name: 'Windows (arm64)' + runs-on: windows-latest + timeout-minutes: 60 + needs: check_source + if: needs.check_source.outputs.run_tests == 'true' + env: + IncludeUwp: 'true' + steps: + - uses: actions/checkout@v4 + - name: Register MSVC problem matcher + run: echo "::add-matcher::.github/problem-matchers/msvc.json" + - name: Build CPython + run: .\PCbuild\build.bat -e -d -p arm64 + build_macos: name: 'macOS' runs-on: macos-latest @@ -450,6 +467,7 @@ jobs: - check_generated_files - build_win32 - build_win_amd64 + - build_win_arm64 - build_macos - build_ubuntu - build_ubuntu_ssltests @@ -465,6 +483,7 @@ jobs: build_macos, build_ubuntu_ssltests, build_win32, + build_win_arm64, allowed-skips: >- ${{ !fromJSON(needs.check_source.outputs.run-docs) @@ -479,6 +498,7 @@ jobs: check_generated_files, build_win32, build_win_amd64, + build_win_arm64, build_macos, build_ubuntu, build_ubuntu_ssltests, From e918b19f420740fbc19bf29975362178d97ab209 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 20 Sep 2023 23:32:10 -0700 Subject: [PATCH 326/632] [3.11] GH-109209: Bump the minimum Sphinx version to 4.2 (GH-109210) (#109637) GH-109209: Bump the minimum Sphinx version to 4.2 (GH-109210) (cherry picked from commit 712cb173f8e1d02c625a40ae03bba57b0c1c032a) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- .github/workflows/reusable-docs.yml | 2 +- Doc/conf.py | 2 +- Doc/requirements-oldest-sphinx.txt | 10 ++++------ .../2023-09-10-02-39-06.gh-issue-109209.0LBewo.rst | 1 + 4 files changed, 7 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/Documentation/2023-09-10-02-39-06.gh-issue-109209.0LBewo.rst diff --git a/.github/workflows/reusable-docs.yml b/.github/workflows/reusable-docs.yml index b434d3dfeb6da6..d0618516bedddf 100644 --- a/.github/workflows/reusable-docs.yml +++ b/.github/workflows/reusable-docs.yml @@ -44,7 +44,7 @@ jobs: - name: 'Set up Python' uses: actions/setup-python@v4 with: - python-version: '3.11' # known to work with Sphinx 3.2 + python-version: '3.11' # known to work with Sphinx 4.2 cache: 'pip' cache-dependency-path: 'Doc/requirements-oldest-sphinx.txt' - name: 'Install build dependencies' diff --git a/Doc/conf.py b/Doc/conf.py index 161a8dec69b5d2..8c92799ecbbf3b 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -61,7 +61,7 @@ highlight_language = 'python3' # Minimum version of sphinx required -needs_sphinx = '3.2' +needs_sphinx = '4.2' # Ignore any .rst files in the includes/ directory; # they're embedded in pages but not rendered individually. diff --git a/Doc/requirements-oldest-sphinx.txt b/Doc/requirements-oldest-sphinx.txt index 94611ca22f09fe..d3ef5bc17650ae 100644 --- a/Doc/requirements-oldest-sphinx.txt +++ b/Doc/requirements-oldest-sphinx.txt @@ -7,12 +7,10 @@ blurb python-docs-theme>=2022.1 # Generated from: -# pip install "Sphinx~=3.2.0" "docutils<0.17" "Jinja2<3" "MarkupSafe<2" +# pip install "Sphinx~=4.2.0" # pip freeze # -# Sphinx 3.2 comes from ``needs_sphinx = '3.2'`` in ``Doc/conf.py``. -# Docutils<0.17, Jinja2<3, and MarkupSafe<2 are additionally specified as -# Sphinx 3.2 is incompatible with newer releases of these packages. +# Sphinx 4.2 comes from ``needs_sphinx = '4.2'`` in ``Doc/conf.py``. alabaster==0.7.13 Babel==2.12.1 @@ -25,10 +23,10 @@ imagesize==1.4.1 Jinja2==2.11.3 MarkupSafe==1.1.1 packaging==23.1 -Pygments==2.15.1 +Pygments==2.16.1 requests==2.31.0 snowballstemmer==2.2.0 -Sphinx==3.2.1 +Sphinx==4.2.0 sphinxcontrib-applehelp==1.0.4 sphinxcontrib-devhelp==1.0.2 sphinxcontrib-htmlhelp==2.0.1 diff --git a/Misc/NEWS.d/next/Documentation/2023-09-10-02-39-06.gh-issue-109209.0LBewo.rst b/Misc/NEWS.d/next/Documentation/2023-09-10-02-39-06.gh-issue-109209.0LBewo.rst new file mode 100644 index 00000000000000..79cc0b72ec742f --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2023-09-10-02-39-06.gh-issue-109209.0LBewo.rst @@ -0,0 +1 @@ +The minimum Sphinx version required for the documentation is now 4.2. From 0c79353e1154eeb6a8a46e42a1a5d6884bd3f2ab Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 21 Sep 2023 20:00:38 +0200 Subject: [PATCH 327/632] [3.11] gh-109613: _pystat_fromstructstat() checks for exceptions (#109618) (#109668) gh-109613: _pystat_fromstructstat() checks for exceptions (#109618) Fix os.stat() and os.DirEntry.stat(): check for exceptions. Previously, on Python built in debug mode, these functions could trigger a fatal Python error (and abort the process) when a function succeeded with an exception set. _pystat_fromstructstat() now exits immediately if an exception is raised, rather only checking for exceptions at the end. It fix following fatal error in fill_time(): Fatal Python error: _Py_CheckSlotResult: Slot * of type int succeeded with an exception set (cherry picked from commit d4cea794a7b9b745817d2bd982d35412aef04710) --- ...-09-20-17-45-46.gh-issue-109613.P13ogN.rst | 4 + Modules/posixmodule.c | 113 +++++++++++------- 2 files changed, 71 insertions(+), 46 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-09-20-17-45-46.gh-issue-109613.P13ogN.rst diff --git a/Misc/NEWS.d/next/Library/2023-09-20-17-45-46.gh-issue-109613.P13ogN.rst b/Misc/NEWS.d/next/Library/2023-09-20-17-45-46.gh-issue-109613.P13ogN.rst new file mode 100644 index 00000000000000..e21a758fc2eb05 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-09-20-17-45-46.gh-issue-109613.P13ogN.rst @@ -0,0 +1,4 @@ +Fix :func:`os.stat` and :meth:`os.DirEntry.stat`: check for exceptions. +Previously, on Python built in debug mode, these functions could trigger a +fatal Python error (and abort the process) when a function succeeded with an +exception set. Patch by Victor Stinner. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 7c5c21398befce..2e4a66ab1bb64a 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -2363,21 +2363,26 @@ _posix_free(void *module) _posix_clear((PyObject *)module); } -static void +static int fill_time(PyObject *module, PyObject *v, int index, time_t sec, unsigned long nsec) { - PyObject *s = _PyLong_FromTime_t(sec); - PyObject *ns_fractional = PyLong_FromUnsignedLong(nsec); + assert(!PyErr_Occurred()); + + int res = -1; PyObject *s_in_ns = NULL; PyObject *ns_total = NULL; PyObject *float_s = NULL; - if (!(s && ns_fractional)) + PyObject *s = _PyLong_FromTime_t(sec); + PyObject *ns_fractional = PyLong_FromUnsignedLong(nsec); + if (!(s && ns_fractional)) { goto exit; + } s_in_ns = PyNumber_Multiply(s, get_posix_state(module)->billion); - if (!s_in_ns) + if (!s_in_ns) { goto exit; + } ns_total = PyNumber_Add(s_in_ns, ns_fractional); if (!ns_total) @@ -2394,12 +2399,17 @@ fill_time(PyObject *module, PyObject *v, int index, time_t sec, unsigned long ns s = NULL; float_s = NULL; ns_total = NULL; + + assert(!PyErr_Occurred()); + res = 0; + exit: Py_XDECREF(s); Py_XDECREF(ns_fractional); Py_XDECREF(s_in_ns); Py_XDECREF(ns_total); Py_XDECREF(float_s); + return res; } /* pack a system stat C structure into the Python stat tuple @@ -2407,33 +2417,46 @@ fill_time(PyObject *module, PyObject *v, int index, time_t sec, unsigned long ns static PyObject* _pystat_fromstructstat(PyObject *module, STRUCT_STAT *st) { - unsigned long ansec, mnsec, cnsec; + assert(!PyErr_Occurred()); + PyObject *StatResultType = get_posix_state(module)->StatResultType; PyObject *v = PyStructSequence_New((PyTypeObject *)StatResultType); - if (v == NULL) + if (v == NULL) { return NULL; + } - PyStructSequence_SET_ITEM(v, 0, PyLong_FromLong((long)st->st_mode)); +#define SET_ITEM(pos, expr) \ + do { \ + PyObject *obj = (expr); \ + if (obj == NULL) { \ + goto error; \ + } \ + PyStructSequence_SET_ITEM(v, (pos), obj); \ + } while (0) + + SET_ITEM(0, PyLong_FromLong((long)st->st_mode)); static_assert(sizeof(unsigned long long) >= sizeof(st->st_ino), "stat.st_ino is larger than unsigned long long"); - PyStructSequence_SET_ITEM(v, 1, PyLong_FromUnsignedLongLong(st->st_ino)); + SET_ITEM(1, PyLong_FromUnsignedLongLong(st->st_ino)); #ifdef MS_WINDOWS - PyStructSequence_SET_ITEM(v, 2, PyLong_FromUnsignedLong(st->st_dev)); + SET_ITEM(2, PyLong_FromUnsignedLong(st->st_dev)); #else - PyStructSequence_SET_ITEM(v, 2, _PyLong_FromDev(st->st_dev)); + SET_ITEM(2, _PyLong_FromDev(st->st_dev)); #endif - PyStructSequence_SET_ITEM(v, 3, PyLong_FromLong((long)st->st_nlink)); + SET_ITEM(3, PyLong_FromLong((long)st->st_nlink)); #if defined(MS_WINDOWS) - PyStructSequence_SET_ITEM(v, 4, PyLong_FromLong(0)); - PyStructSequence_SET_ITEM(v, 5, PyLong_FromLong(0)); + SET_ITEM(4, PyLong_FromLong(0)); + SET_ITEM(5, PyLong_FromLong(0)); #else - PyStructSequence_SET_ITEM(v, 4, _PyLong_FromUid(st->st_uid)); - PyStructSequence_SET_ITEM(v, 5, _PyLong_FromGid(st->st_gid)); + SET_ITEM(4, _PyLong_FromUid(st->st_uid)); + SET_ITEM(5, _PyLong_FromGid(st->st_gid)); #endif static_assert(sizeof(long long) >= sizeof(st->st_size), "stat.st_size is larger than long long"); - PyStructSequence_SET_ITEM(v, 6, PyLong_FromLongLong(st->st_size)); + SET_ITEM(6, PyLong_FromLongLong(st->st_size)); + // Set st_atime, st_mtime and st_ctime + unsigned long ansec, mnsec, cnsec; #if defined(HAVE_STAT_TV_NSEC) ansec = st->st_atim.tv_nsec; mnsec = st->st_mtim.tv_nsec; @@ -2449,64 +2472,62 @@ _pystat_fromstructstat(PyObject *module, STRUCT_STAT *st) #else ansec = mnsec = cnsec = 0; #endif - fill_time(module, v, 7, st->st_atime, ansec); - fill_time(module, v, 8, st->st_mtime, mnsec); - fill_time(module, v, 9, st->st_ctime, cnsec); + if (fill_time(module, v, 7, st->st_atime, ansec) < 0) { + goto error; + } + if (fill_time(module, v, 8, st->st_mtime, mnsec) < 0) { + goto error; + } + if (fill_time(module, v, 9, st->st_ctime, cnsec) < 0) { + goto error; + } #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE - PyStructSequence_SET_ITEM(v, ST_BLKSIZE_IDX, - PyLong_FromLong((long)st->st_blksize)); + SET_ITEM(ST_BLKSIZE_IDX, PyLong_FromLong((long)st->st_blksize)); #endif #ifdef HAVE_STRUCT_STAT_ST_BLOCKS - PyStructSequence_SET_ITEM(v, ST_BLOCKS_IDX, - PyLong_FromLong((long)st->st_blocks)); + SET_ITEM(ST_BLOCKS_IDX, PyLong_FromLong((long)st->st_blocks)); #endif #ifdef HAVE_STRUCT_STAT_ST_RDEV - PyStructSequence_SET_ITEM(v, ST_RDEV_IDX, - PyLong_FromLong((long)st->st_rdev)); + SET_ITEM(ST_RDEV_IDX, PyLong_FromLong((long)st->st_rdev)); #endif #ifdef HAVE_STRUCT_STAT_ST_GEN - PyStructSequence_SET_ITEM(v, ST_GEN_IDX, - PyLong_FromLong((long)st->st_gen)); + SET_ITEM(ST_GEN_IDX, PyLong_FromLong((long)st->st_gen)); #endif #ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME { - PyObject *val; - unsigned long bsec,bnsec; + unsigned long bsec, bnsec; bsec = (long)st->st_birthtime; #ifdef HAVE_STAT_TV_NSEC2 bnsec = st->st_birthtimespec.tv_nsec; #else bnsec = 0; #endif - val = PyFloat_FromDouble(bsec + 1e-9*bnsec); - PyStructSequence_SET_ITEM(v, ST_BIRTHTIME_IDX, - val); + SET_ITEM(ST_BIRTHTIME_IDX, PyFloat_FromDouble(bsec + bnsec * 1e-9)); } #endif #ifdef HAVE_STRUCT_STAT_ST_FLAGS - PyStructSequence_SET_ITEM(v, ST_FLAGS_IDX, - PyLong_FromLong((long)st->st_flags)); + SET_ITEM(ST_FLAGS_IDX, PyLong_FromLong((long)st->st_flags)); #endif #ifdef HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES - PyStructSequence_SET_ITEM(v, ST_FILE_ATTRIBUTES_IDX, - PyLong_FromUnsignedLong(st->st_file_attributes)); + SET_ITEM(ST_FILE_ATTRIBUTES_IDX, + PyLong_FromUnsignedLong(st->st_file_attributes)); #endif #ifdef HAVE_STRUCT_STAT_ST_FSTYPE - PyStructSequence_SET_ITEM(v, ST_FSTYPE_IDX, - PyUnicode_FromString(st->st_fstype)); + SET_ITEM(ST_FSTYPE_IDX, PyUnicode_FromString(st->st_fstype)); #endif #ifdef HAVE_STRUCT_STAT_ST_REPARSE_TAG - PyStructSequence_SET_ITEM(v, ST_REPARSE_TAG_IDX, - PyLong_FromUnsignedLong(st->st_reparse_tag)); + SET_ITEM(ST_REPARSE_TAG_IDX, PyLong_FromUnsignedLong(st->st_reparse_tag)); #endif - if (PyErr_Occurred()) { - Py_DECREF(v); - return NULL; - } - + assert(!PyErr_Occurred()); return v; + +error: + Py_DECREF(v); + return NULL; + +#undef SET_ITEM } /* POSIX methods */ From 3ba4dfe9cd2587a891d6059553992f46e35db114 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 21 Sep 2023 11:07:14 -0700 Subject: [PATCH 328/632] [3.11] gh-108303: Remove unused Lib/test/sgml_input.html (GH-108305) (#109670) gh-108303: Remove unused Lib/test/sgml_input.html (GH-108305) In Python 2.7, the file was used by Lib/test/test_sgmllib.py to test Lib/sgmllib.py. The sgmllib module and its tests have been removed in Python 3.0. (cherry picked from commit d2879f2095abd5c8186c7f69c964a341c2053572) Co-authored-by: Victor Stinner --- Lib/test/sgml_input.html | 212 --------------------------------------- 1 file changed, 212 deletions(-) delete mode 100644 Lib/test/sgml_input.html diff --git a/Lib/test/sgml_input.html b/Lib/test/sgml_input.html deleted file mode 100644 index f4d2e6cc805e6a..00000000000000 --- a/Lib/test/sgml_input.html +++ /dev/null @@ -1,212 +0,0 @@ - - - - - - - - -
- - - - - -
-
- - - - - -
- - -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - -
  MetalCristalDeuterioEnerga  
160.6363.40639.230-80/3.965
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Flotas (max. 9)
Num.MisinCantidadComienzoSalidaObjetivoLlegadaOrden
1 - Espionaje - (F) - 3[2:250:6]Wed Aug 9 18:00:02[2:242:5]Wed Aug 9 18:01:02 -
- - -
-
2 - Espionaje - (V) - 3[2:250:6]Wed Aug 9 17:59:55[2:242:1]Wed Aug 9 18:01:55 -
- - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Nueva misin: elegir naves
NavesDisponibles--
Nave pequea de carga10mx
Nave grande de carga19mx
Crucero6mx
Reciclador1mx
Sonda de espionaje139mx
Ninguna naveTodas las naves
- -

-
- - From e59ee6c59e8894f96907240c0b4a17a97178780d Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 21 Sep 2023 20:18:39 +0200 Subject: [PATCH 329/632] [3.11] gh-108303: Move `ann_module*.py` files to `typinganndata/` folder (#108354) (#109673) gh-108303: Move `ann_module*.py` files to `typinganndata/` folder (#108354) (cherry picked from commit 3f61cf646d0506baa0c0c2118f05110446519c62) Co-authored-by: Nikita Sobolev --- Lib/test/test_grammar.py | 7 +++---- Lib/test/test_inspect.py | 2 +- Lib/test/test_module/__init__.py | 4 +++- Lib/test/test_opcodes.py | 3 ++- Lib/test/test_typing.py | 6 ++++-- Lib/test/{ => typinganndata}/ann_module.py | 0 Lib/test/{ => typinganndata}/ann_module2.py | 0 Lib/test/{ => typinganndata}/ann_module3.py | 0 Lib/test/{ => typinganndata}/ann_module4.py | 0 Lib/test/{ => typinganndata}/ann_module5.py | 0 Lib/test/{ => typinganndata}/ann_module6.py | 0 Lib/test/{ => typinganndata}/ann_module7.py | 0 Lib/test/{ => typinganndata}/ann_module8.py | 0 13 files changed, 13 insertions(+), 9 deletions(-) rename Lib/test/{ => typinganndata}/ann_module.py (100%) rename Lib/test/{ => typinganndata}/ann_module2.py (100%) rename Lib/test/{ => typinganndata}/ann_module3.py (100%) rename Lib/test/{ => typinganndata}/ann_module4.py (100%) rename Lib/test/{ => typinganndata}/ann_module5.py (100%) rename Lib/test/{ => typinganndata}/ann_module6.py (100%) rename Lib/test/{ => typinganndata}/ann_module7.py (100%) rename Lib/test/{ => typinganndata}/ann_module8.py (100%) diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py index f1f9b5c0075100..a00280b184e696 100644 --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -13,10 +13,9 @@ # different import patterns to check that __annotations__ does not interfere # with import machinery -import test.ann_module as ann_module +import test.typinganndata.ann_module as ann_module import typing -from collections import ChainMap -from test import ann_module2 +from test.typinganndata import ann_module2 import test # These are shared with test_tokenize and other test modules. @@ -472,7 +471,7 @@ def test_var_annot_module_semantics(self): def test_var_annot_in_module(self): # check that functions fail the same way when executed # outside of module where they were defined - ann_module3 = import_helper.import_fresh_module("test.ann_module3") + ann_module3 = import_helper.import_fresh_module("test.typinganndata.ann_module3") with self.assertRaises(NameError): ann_module3.f_bad_ann() with self.assertRaises(NameError): diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index fb4cba05721082..f5824778d1e5d6 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -4353,7 +4353,7 @@ def func(*args, **kwargs): def test_base_class_have_text_signature(self): # see issue 43118 - from test.ann_module7 import BufferedReader + from test.typinganndata.ann_module7 import BufferedReader class MyBufferedReader(BufferedReader): """buffer reader class.""" diff --git a/Lib/test/test_module/__init__.py b/Lib/test/test_module/__init__.py index 87d1a8e3d6c978..c2bca45bb60c44 100644 --- a/Lib/test/test_module/__init__.py +++ b/Lib/test/test_module/__init__.py @@ -326,7 +326,9 @@ def test_annotations_getset_raises(self): del foo.__annotations__ def test_annotations_are_created_correctly(self): - ann_module4 = import_helper.import_fresh_module('test.ann_module4') + ann_module4 = import_helper.import_fresh_module( + 'test.typinganndata.ann_module4', + ) self.assertTrue("__annotations__" in ann_module4.__dict__) del ann_module4.__annotations__ self.assertFalse("__annotations__" in ann_module4.__dict__) diff --git a/Lib/test/test_opcodes.py b/Lib/test/test_opcodes.py index e880c3f1ac875e..72488b2bb6b4ff 100644 --- a/Lib/test/test_opcodes.py +++ b/Lib/test/test_opcodes.py @@ -1,7 +1,8 @@ # Python test set -- part 2, opcodes import unittest -from test import ann_module, support +from test import support +from test.typinganndata import ann_module class OpcodeTest(unittest.TestCase): diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 03988758541f06..149c29283d4bae 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -4641,7 +4641,7 @@ def test_errors(self): # We need this to make sure that `@no_type_check` respects `__module__` attr: -from test import ann_module8 +from test.typinganndata import ann_module8 @no_type_check class NoTypeCheck_Outer: @@ -5228,7 +5228,9 @@ def test_overload_registry_repeated(self): # Definitions needed for features introduced in Python 3.6 -from test import ann_module, ann_module2, ann_module3, ann_module5, ann_module6 +from test.typinganndata import ( + ann_module, ann_module2, ann_module3, ann_module5, ann_module6, +) T_a = TypeVar('T_a') diff --git a/Lib/test/ann_module.py b/Lib/test/typinganndata/ann_module.py similarity index 100% rename from Lib/test/ann_module.py rename to Lib/test/typinganndata/ann_module.py diff --git a/Lib/test/ann_module2.py b/Lib/test/typinganndata/ann_module2.py similarity index 100% rename from Lib/test/ann_module2.py rename to Lib/test/typinganndata/ann_module2.py diff --git a/Lib/test/ann_module3.py b/Lib/test/typinganndata/ann_module3.py similarity index 100% rename from Lib/test/ann_module3.py rename to Lib/test/typinganndata/ann_module3.py diff --git a/Lib/test/ann_module4.py b/Lib/test/typinganndata/ann_module4.py similarity index 100% rename from Lib/test/ann_module4.py rename to Lib/test/typinganndata/ann_module4.py diff --git a/Lib/test/ann_module5.py b/Lib/test/typinganndata/ann_module5.py similarity index 100% rename from Lib/test/ann_module5.py rename to Lib/test/typinganndata/ann_module5.py diff --git a/Lib/test/ann_module6.py b/Lib/test/typinganndata/ann_module6.py similarity index 100% rename from Lib/test/ann_module6.py rename to Lib/test/typinganndata/ann_module6.py diff --git a/Lib/test/ann_module7.py b/Lib/test/typinganndata/ann_module7.py similarity index 100% rename from Lib/test/ann_module7.py rename to Lib/test/typinganndata/ann_module7.py diff --git a/Lib/test/ann_module8.py b/Lib/test/typinganndata/ann_module8.py similarity index 100% rename from Lib/test/ann_module8.py rename to Lib/test/typinganndata/ann_module8.py From ca8da713d5078318efc72059cba15b50d0ea5ccb Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 21 Sep 2023 20:59:11 +0200 Subject: [PATCH 330/632] [3.11] gh-108303: Create Lib/test/test_dataclasses/ directory (#108978) (#109675) gh-108303: Create Lib/test/test_dataclasses/ directory (#108978) Move test_dataclasses.py and its "dataclass_*.py" modules into the new Lib/test/test_dataclasses/ subdirectory. Backport to 3.11: update Lib/test/.ruff.toml. (cherry picked from commit 14d6e197cc56e5256d501839a4e66e3864ab15f0) --- Lib/test/.ruff.toml | 2 +- .../__init__.py} | 10 +++++----- Lib/test/{ => test_dataclasses}/dataclass_module_1.py | 0 .../{ => test_dataclasses}/dataclass_module_1_str.py | 0 Lib/test/{ => test_dataclasses}/dataclass_module_2.py | 0 .../{ => test_dataclasses}/dataclass_module_2_str.py | 0 Lib/test/{ => test_dataclasses}/dataclass_textanno.py | 0 Makefile.pre.in | 1 + 8 files changed, 7 insertions(+), 6 deletions(-) rename Lib/test/{test_dataclasses.py => test_dataclasses/__init__.py} (99%) rename Lib/test/{ => test_dataclasses}/dataclass_module_1.py (100%) rename Lib/test/{ => test_dataclasses}/dataclass_module_1_str.py (100%) rename Lib/test/{ => test_dataclasses}/dataclass_module_2.py (100%) rename Lib/test/{ => test_dataclasses}/dataclass_module_2_str.py (100%) rename Lib/test/{ => test_dataclasses}/dataclass_textanno.py (100%) diff --git a/Lib/test/.ruff.toml b/Lib/test/.ruff.toml index 24f3f8c65dcf5f..08bd5c8a87ee21 100644 --- a/Lib/test/.ruff.toml +++ b/Lib/test/.ruff.toml @@ -13,7 +13,7 @@ extend-exclude = [ "test_fstring.py", # TODO Fix: F811 Redefinition of unused name "test_buffer.py", - "test_dataclasses.py", + "test_dataclasses/__init__.py", "test_descr.py", "test_enum.py", "test_functools.py", diff --git a/Lib/test/test_dataclasses.py b/Lib/test/test_dataclasses/__init__.py similarity index 99% rename from Lib/test/test_dataclasses.py rename to Lib/test/test_dataclasses/__init__.py index 4714d0bca86114..682f351c6bdf5b 100644 --- a/Lib/test/test_dataclasses.py +++ b/Lib/test/test_dataclasses/__init__.py @@ -3568,10 +3568,10 @@ class C: self.assertEqual(C(10).x, 10) def test_classvar_module_level_import(self): - from test import dataclass_module_1 - from test import dataclass_module_1_str - from test import dataclass_module_2 - from test import dataclass_module_2_str + from test.test_dataclasses import dataclass_module_1 + from test.test_dataclasses import dataclass_module_1_str + from test.test_dataclasses import dataclass_module_2 + from test.test_dataclasses import dataclass_module_2_str for m in (dataclass_module_1, dataclass_module_1_str, dataclass_module_2, dataclass_module_2_str, @@ -3609,7 +3609,7 @@ def test_classvar_module_level_import(self): self.assertNotIn('not_iv4', c.__dict__) def test_text_annotations(self): - from test import dataclass_textanno + from test.test_dataclasses import dataclass_textanno self.assertEqual( get_type_hints(dataclass_textanno.Bar), diff --git a/Lib/test/dataclass_module_1.py b/Lib/test/test_dataclasses/dataclass_module_1.py similarity index 100% rename from Lib/test/dataclass_module_1.py rename to Lib/test/test_dataclasses/dataclass_module_1.py diff --git a/Lib/test/dataclass_module_1_str.py b/Lib/test/test_dataclasses/dataclass_module_1_str.py similarity index 100% rename from Lib/test/dataclass_module_1_str.py rename to Lib/test/test_dataclasses/dataclass_module_1_str.py diff --git a/Lib/test/dataclass_module_2.py b/Lib/test/test_dataclasses/dataclass_module_2.py similarity index 100% rename from Lib/test/dataclass_module_2.py rename to Lib/test/test_dataclasses/dataclass_module_2.py diff --git a/Lib/test/dataclass_module_2_str.py b/Lib/test/test_dataclasses/dataclass_module_2_str.py similarity index 100% rename from Lib/test/dataclass_module_2_str.py rename to Lib/test/test_dataclasses/dataclass_module_2_str.py diff --git a/Lib/test/dataclass_textanno.py b/Lib/test/test_dataclasses/dataclass_textanno.py similarity index 100% rename from Lib/test/dataclass_textanno.py rename to Lib/test/test_dataclasses/dataclass_textanno.py diff --git a/Makefile.pre.in b/Makefile.pre.in index 99b1be2206c723..885089b994f7fa 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1962,6 +1962,7 @@ TESTSUBDIRS= ctypes/test \ test/test_asyncio \ test/test_capi \ test/test_cppext \ + test/test_dataclasses \ test/test_email \ test/test_email/data \ test/test_import \ From 84d8fdb2661220ee6af8f23637363bed38a90ba7 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 21 Sep 2023 21:45:18 +0200 Subject: [PATCH 331/632] [3.11] gh-109546: Add more tests for formatting floats (GH-109548) (#109685) gh-109546: Add more tests for formatting floats (GH-109548) (cherry picked from commit beb5ec5817b645562ebbdd59f25683a93061c32c) Co-authored-by: Serhiy Storchaka --- Lib/test/test_float.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py index 304388e1f78c97..c6af0b92a73ad8 100644 --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -734,8 +734,13 @@ def test_format_testfile(self): lhs, rhs = map(str.strip, line.split('->')) fmt, arg = lhs.split() - self.assertEqual(fmt % float(arg), rhs) - self.assertEqual(fmt % -float(arg), '-' + rhs) + f = float(arg) + self.assertEqual(fmt % f, rhs) + self.assertEqual(fmt % -f, '-' + rhs) + if fmt != '%r': + fmt2 = fmt[1:] + self.assertEqual(format(f, fmt2), rhs) + self.assertEqual(format(-f, fmt2), '-' + rhs) def test_issue5864(self): self.assertEqual(format(123.456, '.4'), '123.5') From 37b261799b2fc49e06a673b2a34ee5958ff07d86 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 21 Sep 2023 21:45:37 +0200 Subject: [PATCH 332/632] [3.11] gh-108303: Move `test_future` into its own test_future_stmt subdir (#109368) (#109680) gh-108303: Move `test_future` into its own test_future_stmt subdir (#109368) (cherry picked from commit 82505dc351b2f7e37aa395218709b432d83292cd) Co-authored-by: Nikita Sobolev --- Lib/test/libregrtest/runtest.py | 1 + Lib/test/test_future_stmt/__init__.py | 6 +++ .../badsyntax_future10.py | 0 .../badsyntax_future3.py | 0 .../badsyntax_future4.py | 0 .../badsyntax_future5.py | 0 .../badsyntax_future6.py | 0 .../badsyntax_future7.py | 0 .../badsyntax_future8.py | 0 .../badsyntax_future9.py | 0 .../{ => test_future_stmt}/future_test1.py | 0 .../{ => test_future_stmt}/future_test2.py | 0 .../{ => test_future_stmt}/test_future.py | 48 +++++++++++-------- .../test_future_flags.py} | 0 .../test_future_multiple_features.py} | 0 .../test_future_multiple_imports.py} | 0 .../test_future_single_import.py} | 0 Makefile.pre.in | 1 + 18 files changed, 37 insertions(+), 19 deletions(-) create mode 100644 Lib/test/test_future_stmt/__init__.py rename Lib/test/{ => test_future_stmt}/badsyntax_future10.py (100%) rename Lib/test/{ => test_future_stmt}/badsyntax_future3.py (100%) rename Lib/test/{ => test_future_stmt}/badsyntax_future4.py (100%) rename Lib/test/{ => test_future_stmt}/badsyntax_future5.py (100%) rename Lib/test/{ => test_future_stmt}/badsyntax_future6.py (100%) rename Lib/test/{ => test_future_stmt}/badsyntax_future7.py (100%) rename Lib/test/{ => test_future_stmt}/badsyntax_future8.py (100%) rename Lib/test/{ => test_future_stmt}/badsyntax_future9.py (100%) rename Lib/test/{ => test_future_stmt}/future_test1.py (100%) rename Lib/test/{ => test_future_stmt}/future_test2.py (100%) rename Lib/test/{ => test_future_stmt}/test_future.py (91%) rename Lib/test/{test___future__.py => test_future_stmt/test_future_flags.py} (100%) rename Lib/test/{test_future5.py => test_future_stmt/test_future_multiple_features.py} (100%) rename Lib/test/{test_future4.py => test_future_stmt/test_future_multiple_imports.py} (100%) rename Lib/test/{test_future3.py => test_future_stmt/test_future_single_import.py} (100%) diff --git a/Lib/test/libregrtest/runtest.py b/Lib/test/libregrtest/runtest.py index e73a8f1a855dc1..c85d73170ba559 100644 --- a/Lib/test/libregrtest/runtest.py +++ b/Lib/test/libregrtest/runtest.py @@ -141,6 +141,7 @@ def set_env_changed(self): SPLITTESTDIRS = { "test_asyncio", + "test_future_stmt", } # Storage of uncollectable objects diff --git a/Lib/test/test_future_stmt/__init__.py b/Lib/test/test_future_stmt/__init__.py new file mode 100644 index 00000000000000..f2a39a3fe29c7f --- /dev/null +++ b/Lib/test/test_future_stmt/__init__.py @@ -0,0 +1,6 @@ +import os +from test import support + + +def load_tests(*args): + return support.load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/badsyntax_future10.py b/Lib/test/test_future_stmt/badsyntax_future10.py similarity index 100% rename from Lib/test/badsyntax_future10.py rename to Lib/test/test_future_stmt/badsyntax_future10.py diff --git a/Lib/test/badsyntax_future3.py b/Lib/test/test_future_stmt/badsyntax_future3.py similarity index 100% rename from Lib/test/badsyntax_future3.py rename to Lib/test/test_future_stmt/badsyntax_future3.py diff --git a/Lib/test/badsyntax_future4.py b/Lib/test/test_future_stmt/badsyntax_future4.py similarity index 100% rename from Lib/test/badsyntax_future4.py rename to Lib/test/test_future_stmt/badsyntax_future4.py diff --git a/Lib/test/badsyntax_future5.py b/Lib/test/test_future_stmt/badsyntax_future5.py similarity index 100% rename from Lib/test/badsyntax_future5.py rename to Lib/test/test_future_stmt/badsyntax_future5.py diff --git a/Lib/test/badsyntax_future6.py b/Lib/test/test_future_stmt/badsyntax_future6.py similarity index 100% rename from Lib/test/badsyntax_future6.py rename to Lib/test/test_future_stmt/badsyntax_future6.py diff --git a/Lib/test/badsyntax_future7.py b/Lib/test/test_future_stmt/badsyntax_future7.py similarity index 100% rename from Lib/test/badsyntax_future7.py rename to Lib/test/test_future_stmt/badsyntax_future7.py diff --git a/Lib/test/badsyntax_future8.py b/Lib/test/test_future_stmt/badsyntax_future8.py similarity index 100% rename from Lib/test/badsyntax_future8.py rename to Lib/test/test_future_stmt/badsyntax_future8.py diff --git a/Lib/test/badsyntax_future9.py b/Lib/test/test_future_stmt/badsyntax_future9.py similarity index 100% rename from Lib/test/badsyntax_future9.py rename to Lib/test/test_future_stmt/badsyntax_future9.py diff --git a/Lib/test/future_test1.py b/Lib/test/test_future_stmt/future_test1.py similarity index 100% rename from Lib/test/future_test1.py rename to Lib/test/test_future_stmt/future_test1.py diff --git a/Lib/test/future_test2.py b/Lib/test/test_future_stmt/future_test2.py similarity index 100% rename from Lib/test/future_test2.py rename to Lib/test/test_future_stmt/future_test2.py diff --git a/Lib/test/test_future.py b/Lib/test/test_future_stmt/test_future.py similarity index 91% rename from Lib/test/test_future.py rename to Lib/test/test_future_stmt/test_future.py index c3e8420d6672c5..13cee3ff6e49da 100644 --- a/Lib/test/test_future.py +++ b/Lib/test/test_future_stmt/test_future.py @@ -25,57 +25,71 @@ def check_syntax_error(self, err, basename, lineno, offset=1): self.assertEqual(err.offset, offset) def test_future1(self): - with import_helper.CleanImport('future_test1'): - from test import future_test1 + with import_helper.CleanImport('test.test_future_stmt.future_test1'): + from test.test_future_stmt import future_test1 self.assertEqual(future_test1.result, 6) def test_future2(self): - with import_helper.CleanImport('future_test2'): - from test import future_test2 + with import_helper.CleanImport('test.test_future_stmt.future_test2'): + from test.test_future_stmt import future_test2 self.assertEqual(future_test2.result, 6) - def test_future3(self): - with import_helper.CleanImport('test_future3'): - from test import test_future3 + def test_future_single_import(self): + with import_helper.CleanImport( + 'test.test_future_stmt.test_future_single_import', + ): + from test.test_future_stmt import test_future_single_import + + def test_future_multiple_imports(self): + with import_helper.CleanImport( + 'test.test_future_stmt.test_future_multiple_imports', + ): + from test.test_future_stmt import test_future_multiple_imports + + def test_future_multiple_features(self): + with import_helper.CleanImport( + "test.test_future_stmt.test_future_multiple_features", + ): + from test.test_future_stmt import test_future_multiple_features def test_badfuture3(self): with self.assertRaises(SyntaxError) as cm: - from test import badsyntax_future3 + from test.test_future_stmt import badsyntax_future3 self.check_syntax_error(cm.exception, "badsyntax_future3", 3) def test_badfuture4(self): with self.assertRaises(SyntaxError) as cm: - from test import badsyntax_future4 + from test.test_future_stmt import badsyntax_future4 self.check_syntax_error(cm.exception, "badsyntax_future4", 3) def test_badfuture5(self): with self.assertRaises(SyntaxError) as cm: - from test import badsyntax_future5 + from test.test_future_stmt import badsyntax_future5 self.check_syntax_error(cm.exception, "badsyntax_future5", 4) def test_badfuture6(self): with self.assertRaises(SyntaxError) as cm: - from test import badsyntax_future6 + from test.test_future_stmt import badsyntax_future6 self.check_syntax_error(cm.exception, "badsyntax_future6", 3) def test_badfuture7(self): with self.assertRaises(SyntaxError) as cm: - from test import badsyntax_future7 + from test.test_future_stmt import badsyntax_future7 self.check_syntax_error(cm.exception, "badsyntax_future7", 3, 53) def test_badfuture8(self): with self.assertRaises(SyntaxError) as cm: - from test import badsyntax_future8 + from test.test_future_stmt import badsyntax_future8 self.check_syntax_error(cm.exception, "badsyntax_future8", 3) def test_badfuture9(self): with self.assertRaises(SyntaxError) as cm: - from test import badsyntax_future9 + from test.test_future_stmt import badsyntax_future9 self.check_syntax_error(cm.exception, "badsyntax_future9", 3) def test_badfuture10(self): with self.assertRaises(SyntaxError) as cm: - from test import badsyntax_future10 + from test.test_future_stmt import badsyntax_future10 self.check_syntax_error(cm.exception, "badsyntax_future10", 3) def test_ensure_flags_dont_clash(self): @@ -113,10 +127,6 @@ def test_parserhack(self): else: self.fail("syntax error didn't occur") - def test_multiple_features(self): - with import_helper.CleanImport("test.test_future5"): - from test import test_future5 - def test_unicode_literals_exec(self): scope = {} exec("from __future__ import unicode_literals; x = ''", {}, scope) diff --git a/Lib/test/test___future__.py b/Lib/test/test_future_stmt/test_future_flags.py similarity index 100% rename from Lib/test/test___future__.py rename to Lib/test/test_future_stmt/test_future_flags.py diff --git a/Lib/test/test_future5.py b/Lib/test/test_future_stmt/test_future_multiple_features.py similarity index 100% rename from Lib/test/test_future5.py rename to Lib/test/test_future_stmt/test_future_multiple_features.py diff --git a/Lib/test/test_future4.py b/Lib/test/test_future_stmt/test_future_multiple_imports.py similarity index 100% rename from Lib/test/test_future4.py rename to Lib/test/test_future_stmt/test_future_multiple_imports.py diff --git a/Lib/test/test_future3.py b/Lib/test/test_future_stmt/test_future_single_import.py similarity index 100% rename from Lib/test/test_future3.py rename to Lib/test/test_future_stmt/test_future_single_import.py diff --git a/Makefile.pre.in b/Makefile.pre.in index 885089b994f7fa..254820a2509bd7 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1965,6 +1965,7 @@ TESTSUBDIRS= ctypes/test \ test/test_dataclasses \ test/test_email \ test/test_email/data \ + test/test_future_stmt \ test/test_import \ test/test_import/data \ test/test_import/data/circular_imports \ From 8d99502aac3de9badf072b3e56c1d03116a4693c Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 21 Sep 2023 21:46:05 +0200 Subject: [PATCH 333/632] [3.11] gh-108303: Move tokenize-related data to Lib/test/tokenizedata (GH-109265) (#109678) * gh-108303: Move tokenize-related data to Lib/test/tokenizedata (GH-109265) (cherry picked from commit 1110c5bc828218086f6397ec05a9312fb73ea30a) * gh-108303: Add `Lib/test/tokenizedata` to `TESTSUBDIRS` (#109314) (cherry picked from commit 42ab2cbd7b5e76e919b70883ae683e789dbd913d) --------- Co-authored-by: Nikita Sobolev --- .gitattributes | 2 +- Lib/test/test_py_compile.py | 16 +++++++--- Lib/test/test_source_encoding.py | 5 ++-- Lib/test/test_tarfile.py | 29 ++++++++++++------- Lib/test/test_tokenize.py | 7 ++--- Lib/test/test_tools/test_reindent.py | 2 +- Lib/test/test_unicode_identifiers.py | 2 +- Lib/test/tokenizedata/__init__.py | 0 Lib/test/{ => tokenizedata}/bad_coding.py | 0 Lib/test/{ => tokenizedata}/bad_coding2.py | 0 Lib/test/{ => tokenizedata}/badsyntax_3131.py | 0 Lib/test/{ => tokenizedata}/coding20731.py | 0 ...-latin1-coding-cookie-and-utf8-bom-sig.txt | 0 ...no-coding-cookie-and-utf8-bom-sig-only.txt | 0 ...utf8-coding-cookie-and-no-utf8-bom-sig.txt | 0 ...ts-utf8-coding-cookie-and-utf8-bom-sig.txt | 0 .../{ => tokenizedata}/tokenize_tests.txt | 0 Makefile.pre.in | 1 + 18 files changed, 40 insertions(+), 24 deletions(-) create mode 100644 Lib/test/tokenizedata/__init__.py rename Lib/test/{ => tokenizedata}/bad_coding.py (100%) rename Lib/test/{ => tokenizedata}/bad_coding2.py (100%) rename Lib/test/{ => tokenizedata}/badsyntax_3131.py (100%) rename Lib/test/{ => tokenizedata}/coding20731.py (100%) rename Lib/test/{ => tokenizedata}/tokenize_tests-latin1-coding-cookie-and-utf8-bom-sig.txt (100%) rename Lib/test/{ => tokenizedata}/tokenize_tests-no-coding-cookie-and-utf8-bom-sig-only.txt (100%) rename Lib/test/{ => tokenizedata}/tokenize_tests-utf8-coding-cookie-and-no-utf8-bom-sig.txt (100%) rename Lib/test/{ => tokenizedata}/tokenize_tests-utf8-coding-cookie-and-utf8-bom-sig.txt (100%) rename Lib/test/{ => tokenizedata}/tokenize_tests.txt (100%) diff --git a/.gitattributes b/.gitattributes index 6da587a4c6ea5a..1479ca074cda12 100644 --- a/.gitattributes +++ b/.gitattributes @@ -25,7 +25,7 @@ PC/classicAppCompat.* binary [attr]noeol -text Lib/test/cjkencodings/* noeol -Lib/test/coding20731.py noeol +Lib/test/tokenizedata/coding20731.py noeol Lib/test/decimaltestdata/*.decTest noeol Lib/test/test_email/data/*.txt noeol Lib/test/test_importlib/data01/* noeol diff --git a/Lib/test/test_py_compile.py b/Lib/test/test_py_compile.py index a4a52b180dbb55..6a54d48eb16e93 100644 --- a/Lib/test/test_py_compile.py +++ b/Lib/test/test_py_compile.py @@ -132,7 +132,9 @@ def test_exceptions_propagate(self): os.chmod(self.directory, mode.st_mode) def test_bad_coding(self): - bad_coding = os.path.join(os.path.dirname(__file__), 'bad_coding2.py') + bad_coding = os.path.join(os.path.dirname(__file__), + 'tokenizedata', + 'bad_coding2.py') with support.captured_stderr(): self.assertIsNone(py_compile.compile(bad_coding, doraise=False)) self.assertFalse(os.path.exists( @@ -195,7 +197,9 @@ def test_invalidation_mode(self): self.assertEqual(flags, 0b1) def test_quiet(self): - bad_coding = os.path.join(os.path.dirname(__file__), 'bad_coding2.py') + bad_coding = os.path.join(os.path.dirname(__file__), + 'tokenizedata', + 'bad_coding2.py') with support.captured_stderr() as stderr: self.assertIsNone(py_compile.compile(bad_coding, doraise=False, quiet=2)) self.assertIsNone(py_compile.compile(bad_coding, doraise=True, quiet=2)) @@ -259,14 +263,18 @@ def test_with_files(self): self.assertTrue(os.path.exists(self.cache_path)) def test_bad_syntax(self): - bad_syntax = os.path.join(os.path.dirname(__file__), 'badsyntax_3131.py') + bad_syntax = os.path.join(os.path.dirname(__file__), + 'tokenizedata', + 'badsyntax_3131.py') rc, stdout, stderr = self.pycompilecmd_failure(bad_syntax) self.assertEqual(rc, 1) self.assertEqual(stdout, b'') self.assertIn(b'SyntaxError', stderr) def test_bad_syntax_with_quiet(self): - bad_syntax = os.path.join(os.path.dirname(__file__), 'badsyntax_3131.py') + bad_syntax = os.path.join(os.path.dirname(__file__), + 'tokenizedata', + 'badsyntax_3131.py') rc, stdout, stderr = self.pycompilecmd_failure('-q', bad_syntax) self.assertEqual(rc, 1) self.assertEqual(stdout, b'') diff --git a/Lib/test/test_source_encoding.py b/Lib/test/test_source_encoding.py index ae680c516400a3..0c7f8ee861cfe2 100644 --- a/Lib/test/test_source_encoding.py +++ b/Lib/test/test_source_encoding.py @@ -69,6 +69,7 @@ def test_issue7820(self): def test_20731(self): sub = subprocess.Popen([sys.executable, os.path.join(os.path.dirname(__file__), + 'tokenizedata', 'coding20731.py')], stderr=subprocess.PIPE) err = sub.communicate()[1] @@ -101,10 +102,10 @@ def test_bad_coding2(self): self.verify_bad_module(module_name) def verify_bad_module(self, module_name): - self.assertRaises(SyntaxError, __import__, 'test.' + module_name) + self.assertRaises(SyntaxError, __import__, 'test.tokenizedata.' + module_name) path = os.path.dirname(__file__) - filename = os.path.join(path, module_name + '.py') + filename = os.path.join(path, 'tokenizedata', module_name + '.py') with open(filename, "rb") as fp: bytes = fp.read() self.assertRaises(SyntaxError, compile, bytes, filename, 'exec') diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index 13a75f39f9ba52..623452d50ce842 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -2493,16 +2493,17 @@ def tarfilecmd_failure(self, *args): return script_helper.assert_python_failure('-m', 'tarfile', *args) def make_simple_tarfile(self, tar_name): - files = [support.findfile('tokenize_tests.txt'), + files = [support.findfile('tokenize_tests.txt', + subdir='tokenizedata'), support.findfile('tokenize_tests-no-coding-cookie-' - 'and-utf8-bom-sig-only.txt')] + 'and-utf8-bom-sig-only.txt', + subdir='tokenizedata')] self.addCleanup(os_helper.unlink, tar_name) with tarfile.open(tar_name, 'w') as tf: for tardata in files: tf.add(tardata, arcname=os.path.basename(tardata)) def make_evil_tarfile(self, tar_name): - files = [support.findfile('tokenize_tests.txt')] self.addCleanup(os_helper.unlink, tar_name) with tarfile.open(tar_name, 'w') as tf: benign = tarfile.TarInfo('benign') @@ -2583,9 +2584,11 @@ def test_list_command_invalid_file(self): self.assertEqual(rc, 1) def test_create_command(self): - files = [support.findfile('tokenize_tests.txt'), + files = [support.findfile('tokenize_tests.txt', + subdir='tokenizedata'), support.findfile('tokenize_tests-no-coding-cookie-' - 'and-utf8-bom-sig-only.txt')] + 'and-utf8-bom-sig-only.txt', + subdir='tokenizedata')] for opt in '-c', '--create': try: out = self.tarfilecmd(opt, tmpname, *files) @@ -2596,9 +2599,11 @@ def test_create_command(self): os_helper.unlink(tmpname) def test_create_command_verbose(self): - files = [support.findfile('tokenize_tests.txt'), + files = [support.findfile('tokenize_tests.txt', + subdir='tokenizedata'), support.findfile('tokenize_tests-no-coding-cookie-' - 'and-utf8-bom-sig-only.txt')] + 'and-utf8-bom-sig-only.txt', + subdir='tokenizedata')] for opt in '-v', '--verbose': try: out = self.tarfilecmd(opt, '-c', tmpname, *files, @@ -2610,7 +2615,7 @@ def test_create_command_verbose(self): os_helper.unlink(tmpname) def test_create_command_dotless_filename(self): - files = [support.findfile('tokenize_tests.txt')] + files = [support.findfile('tokenize_tests.txt', subdir='tokenizedata')] try: out = self.tarfilecmd('-c', dotlessname, *files) self.assertEqual(out, b'') @@ -2621,7 +2626,7 @@ def test_create_command_dotless_filename(self): def test_create_command_dot_started_filename(self): tar_name = os.path.join(TEMPDIR, ".testtar") - files = [support.findfile('tokenize_tests.txt')] + files = [support.findfile('tokenize_tests.txt', subdir='tokenizedata')] try: out = self.tarfilecmd('-c', tar_name, *files) self.assertEqual(out, b'') @@ -2631,9 +2636,11 @@ def test_create_command_dot_started_filename(self): os_helper.unlink(tar_name) def test_create_command_compressed(self): - files = [support.findfile('tokenize_tests.txt'), + files = [support.findfile('tokenize_tests.txt', + subdir='tokenizedata'), support.findfile('tokenize_tests-no-coding-cookie-' - 'and-utf8-bom-sig-only.txt')] + 'and-utf8-bom-sig-only.txt', + subdir='tokenizedata')] for filetype in (GzipTest, Bz2Test, LzmaTest): if not filetype.open: continue diff --git a/Lib/test/test_tokenize.py b/Lib/test/test_tokenize.py index 63c2501cfe2338..62f152fe431d5b 100644 --- a/Lib/test/test_tokenize.py +++ b/Lib/test/test_tokenize.py @@ -999,7 +999,7 @@ class TestTokenizerAdheresToPep0263(TestCase): """ def _testFile(self, filename): - path = os.path.join(os.path.dirname(__file__), filename) + path = os.path.join(os.path.dirname(__file__), 'tokenizedata', filename) TestRoundtrip.check_roundtrip(self, open(path, 'rb')) def test_utf8_coding_cookie_and_no_utf8_bom(self): @@ -1560,7 +1560,7 @@ def test_roundtrip(self): self.check_roundtrip("if x == 1 : \n" " print(x)\n") - fn = support.findfile("tokenize_tests.txt") + fn = support.findfile("tokenize_tests.txt", subdir="tokenizedata") with open(fn, 'rb') as f: self.check_roundtrip(f) self.check_roundtrip("if x == 1:\n" @@ -1615,8 +1615,7 @@ def test_random_files(self): # pass the '-ucpu' option to process the full directory. import glob, random - fn = support.findfile("tokenize_tests.txt") - tempdir = os.path.dirname(fn) or os.curdir + tempdir = os.path.dirname(__file__) or os.curdir testfiles = glob.glob(os.path.join(glob.escape(tempdir), "test*.py")) # Tokenize is broken on test_pep3131.py because regular expressions are diff --git a/Lib/test/test_tools/test_reindent.py b/Lib/test/test_tools/test_reindent.py index 34df0c5d511904..3aa09130a5c5eb 100644 --- a/Lib/test/test_tools/test_reindent.py +++ b/Lib/test/test_tools/test_reindent.py @@ -25,7 +25,7 @@ def test_help(self): self.assertGreater(err, b'') def test_reindent_file_with_bad_encoding(self): - bad_coding_path = findfile('bad_coding.py') + bad_coding_path = findfile('bad_coding.py', subdir='tokenizedata') rc, out, err = assert_python_ok(self.script, '-r', bad_coding_path) self.assertEqual(out, b'') self.assertNotEqual(err, b'') diff --git a/Lib/test/test_unicode_identifiers.py b/Lib/test/test_unicode_identifiers.py index 5b9ced5d1cb837..63c6c055824b20 100644 --- a/Lib/test/test_unicode_identifiers.py +++ b/Lib/test/test_unicode_identifiers.py @@ -19,7 +19,7 @@ def test_non_bmp_normalized(self): def test_invalid(self): try: - from test import badsyntax_3131 + from test.tokenizedata import badsyntax_3131 except SyntaxError as err: self.assertEqual(str(err), "invalid character '€' (U+20AC) (badsyntax_3131.py, line 2)") diff --git a/Lib/test/tokenizedata/__init__.py b/Lib/test/tokenizedata/__init__.py new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/Lib/test/bad_coding.py b/Lib/test/tokenizedata/bad_coding.py similarity index 100% rename from Lib/test/bad_coding.py rename to Lib/test/tokenizedata/bad_coding.py diff --git a/Lib/test/bad_coding2.py b/Lib/test/tokenizedata/bad_coding2.py similarity index 100% rename from Lib/test/bad_coding2.py rename to Lib/test/tokenizedata/bad_coding2.py diff --git a/Lib/test/badsyntax_3131.py b/Lib/test/tokenizedata/badsyntax_3131.py similarity index 100% rename from Lib/test/badsyntax_3131.py rename to Lib/test/tokenizedata/badsyntax_3131.py diff --git a/Lib/test/coding20731.py b/Lib/test/tokenizedata/coding20731.py similarity index 100% rename from Lib/test/coding20731.py rename to Lib/test/tokenizedata/coding20731.py diff --git a/Lib/test/tokenize_tests-latin1-coding-cookie-and-utf8-bom-sig.txt b/Lib/test/tokenizedata/tokenize_tests-latin1-coding-cookie-and-utf8-bom-sig.txt similarity index 100% rename from Lib/test/tokenize_tests-latin1-coding-cookie-and-utf8-bom-sig.txt rename to Lib/test/tokenizedata/tokenize_tests-latin1-coding-cookie-and-utf8-bom-sig.txt diff --git a/Lib/test/tokenize_tests-no-coding-cookie-and-utf8-bom-sig-only.txt b/Lib/test/tokenizedata/tokenize_tests-no-coding-cookie-and-utf8-bom-sig-only.txt similarity index 100% rename from Lib/test/tokenize_tests-no-coding-cookie-and-utf8-bom-sig-only.txt rename to Lib/test/tokenizedata/tokenize_tests-no-coding-cookie-and-utf8-bom-sig-only.txt diff --git a/Lib/test/tokenize_tests-utf8-coding-cookie-and-no-utf8-bom-sig.txt b/Lib/test/tokenizedata/tokenize_tests-utf8-coding-cookie-and-no-utf8-bom-sig.txt similarity index 100% rename from Lib/test/tokenize_tests-utf8-coding-cookie-and-no-utf8-bom-sig.txt rename to Lib/test/tokenizedata/tokenize_tests-utf8-coding-cookie-and-no-utf8-bom-sig.txt diff --git a/Lib/test/tokenize_tests-utf8-coding-cookie-and-utf8-bom-sig.txt b/Lib/test/tokenizedata/tokenize_tests-utf8-coding-cookie-and-utf8-bom-sig.txt similarity index 100% rename from Lib/test/tokenize_tests-utf8-coding-cookie-and-utf8-bom-sig.txt rename to Lib/test/tokenizedata/tokenize_tests-utf8-coding-cookie-and-utf8-bom-sig.txt diff --git a/Lib/test/tokenize_tests.txt b/Lib/test/tokenizedata/tokenize_tests.txt similarity index 100% rename from Lib/test/tokenize_tests.txt rename to Lib/test/tokenizedata/tokenize_tests.txt diff --git a/Makefile.pre.in b/Makefile.pre.in index 254820a2509bd7..0d0ac1315bdc7d 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -2043,6 +2043,7 @@ TESTSUBDIRS= ctypes/test \ test/test_warnings/data \ test/test_zoneinfo \ test/test_zoneinfo/data \ + test/tokenizedata \ test/tracedmodules \ test/typinganndata \ test/xmltestdata \ From b3af888342db12915f0cdaaacbdc61aadfb62eff Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 21 Sep 2023 13:44:21 -0700 Subject: [PATCH 334/632] [3.11] gh-108388: Split test_multiprocessing_spawn (GH-108396) (#109688) gh-108388: Split test_multiprocessing_spawn (GH-108396) Split test_multiprocessing_fork, test_multiprocessing_forkserver and test_multiprocessing_spawn into test packages. Each package is made of 4 sub-tests: processes, threads, manager and misc. It allows running more tests in parallel and so reduce the total test duration. (cherry picked from commit aa9a359ca2663195b0f04eef46109c28c4ff74d3) Co-authored-by: Victor Stinner --- Lib/test/_test_multiprocessing.py | 10 +++++++++- Lib/test/libregrtest/runtest.py | 3 +++ .../__init__.py} | 11 ++++------- Lib/test/test_multiprocessing_fork/test_manager.py | 7 +++++++ Lib/test/test_multiprocessing_fork/test_misc.py | 7 +++++++ Lib/test/test_multiprocessing_fork/test_processes.py | 7 +++++++ Lib/test/test_multiprocessing_fork/test_threads.py | 7 +++++++ .../__init__.py} | 11 ++++------- .../test_multiprocessing_forkserver/test_manager.py | 7 +++++++ .../test_multiprocessing_forkserver/test_misc.py | 7 +++++++ .../test_processes.py | 7 +++++++ .../test_multiprocessing_forkserver/test_threads.py | 7 +++++++ Lib/test/test_multiprocessing_spawn.py | 12 ------------ Lib/test/test_multiprocessing_spawn/__init__.py | 9 +++++++++ Lib/test/test_multiprocessing_spawn/test_manager.py | 7 +++++++ Lib/test/test_multiprocessing_spawn/test_misc.py | 7 +++++++ .../test_multiprocessing_spawn/test_processes.py | 7 +++++++ Lib/test/test_multiprocessing_spawn/test_threads.py | 7 +++++++ .../2023-08-24-04-23-35.gh-issue-108388.mr0MeE.rst | 4 ++++ 19 files changed, 117 insertions(+), 27 deletions(-) rename Lib/test/{test_multiprocessing_fork.py => test_multiprocessing_fork/__init__.py} (66%) create mode 100644 Lib/test/test_multiprocessing_fork/test_manager.py create mode 100644 Lib/test/test_multiprocessing_fork/test_misc.py create mode 100644 Lib/test/test_multiprocessing_fork/test_processes.py create mode 100644 Lib/test/test_multiprocessing_fork/test_threads.py rename Lib/test/{test_multiprocessing_forkserver.py => test_multiprocessing_forkserver/__init__.py} (58%) create mode 100644 Lib/test/test_multiprocessing_forkserver/test_manager.py create mode 100644 Lib/test/test_multiprocessing_forkserver/test_misc.py create mode 100644 Lib/test/test_multiprocessing_forkserver/test_processes.py create mode 100644 Lib/test/test_multiprocessing_forkserver/test_threads.py delete mode 100644 Lib/test/test_multiprocessing_spawn.py create mode 100644 Lib/test/test_multiprocessing_spawn/__init__.py create mode 100644 Lib/test/test_multiprocessing_spawn/test_manager.py create mode 100644 Lib/test/test_multiprocessing_spawn/test_misc.py create mode 100644 Lib/test/test_multiprocessing_spawn/test_processes.py create mode 100644 Lib/test/test_multiprocessing_spawn/test_threads.py create mode 100644 Misc/NEWS.d/next/Tests/2023-08-24-04-23-35.gh-issue-108388.mr0MeE.rst diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 54719f2586bb40..ac0ed397684f42 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -6080,7 +6080,8 @@ class ThreadsMixin(BaseMixin): # Functions used to create test cases from the base ones in this module # -def install_tests_in_module_dict(remote_globs, start_method): +def install_tests_in_module_dict(remote_globs, start_method, + only_type=None, exclude_types=False): __module__ = remote_globs['__name__'] local_globs = globals() ALL_TYPES = {'processes', 'threads', 'manager'} @@ -6093,6 +6094,10 @@ def install_tests_in_module_dict(remote_globs, start_method): continue assert set(base.ALLOWED_TYPES) <= ALL_TYPES, base.ALLOWED_TYPES for type_ in base.ALLOWED_TYPES: + if only_type and type_ != only_type: + continue + if exclude_types: + continue newname = 'With' + type_.capitalize() + name[1:] Mixin = local_globs[type_.capitalize() + 'Mixin'] class Temp(base, Mixin, unittest.TestCase): @@ -6103,6 +6108,9 @@ class Temp(base, Mixin, unittest.TestCase): Temp.__module__ = __module__ remote_globs[newname] = Temp elif issubclass(base, unittest.TestCase): + if only_type: + continue + class Temp(base, object): pass Temp.__name__ = Temp.__qualname__ = name diff --git a/Lib/test/libregrtest/runtest.py b/Lib/test/libregrtest/runtest.py index c85d73170ba559..74e28e27e1abac 100644 --- a/Lib/test/libregrtest/runtest.py +++ b/Lib/test/libregrtest/runtest.py @@ -142,6 +142,9 @@ def set_env_changed(self): SPLITTESTDIRS = { "test_asyncio", "test_future_stmt", + "test_multiprocessing_fork", + "test_multiprocessing_forkserver", + "test_multiprocessing_spawn", } # Storage of uncollectable objects diff --git a/Lib/test/test_multiprocessing_fork.py b/Lib/test/test_multiprocessing_fork/__init__.py similarity index 66% rename from Lib/test/test_multiprocessing_fork.py rename to Lib/test/test_multiprocessing_fork/__init__.py index 5000edb7c5c299..aa1fff50b28f5f 100644 --- a/Lib/test/test_multiprocessing_fork.py +++ b/Lib/test/test_multiprocessing_fork/__init__.py @@ -1,7 +1,6 @@ -import unittest -import test._test_multiprocessing - +import os.path import sys +import unittest from test import support if support.PGO: @@ -13,7 +12,5 @@ if sys.platform == 'darwin': raise unittest.SkipTest("test may crash on macOS (bpo-33725)") -test._test_multiprocessing.install_tests_in_module_dict(globals(), 'fork') - -if __name__ == '__main__': - unittest.main() +def load_tests(*args): + return support.load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/test_multiprocessing_fork/test_manager.py b/Lib/test/test_multiprocessing_fork/test_manager.py new file mode 100644 index 00000000000000..9efbb83bbb73bf --- /dev/null +++ b/Lib/test/test_multiprocessing_fork/test_manager.py @@ -0,0 +1,7 @@ +import unittest +from test._test_multiprocessing import install_tests_in_module_dict + +install_tests_in_module_dict(globals(), 'fork', only_type="manager") + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_multiprocessing_fork/test_misc.py b/Lib/test/test_multiprocessing_fork/test_misc.py new file mode 100644 index 00000000000000..891a494020c3a1 --- /dev/null +++ b/Lib/test/test_multiprocessing_fork/test_misc.py @@ -0,0 +1,7 @@ +import unittest +from test._test_multiprocessing import install_tests_in_module_dict + +install_tests_in_module_dict(globals(), 'fork', exclude_types=True) + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_multiprocessing_fork/test_processes.py b/Lib/test/test_multiprocessing_fork/test_processes.py new file mode 100644 index 00000000000000..e64e9afc0100e8 --- /dev/null +++ b/Lib/test/test_multiprocessing_fork/test_processes.py @@ -0,0 +1,7 @@ +import unittest +from test._test_multiprocessing import install_tests_in_module_dict + +install_tests_in_module_dict(globals(), 'fork', only_type="processes") + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_multiprocessing_fork/test_threads.py b/Lib/test/test_multiprocessing_fork/test_threads.py new file mode 100644 index 00000000000000..1670e34cb17f25 --- /dev/null +++ b/Lib/test/test_multiprocessing_fork/test_threads.py @@ -0,0 +1,7 @@ +import unittest +from test._test_multiprocessing import install_tests_in_module_dict + +install_tests_in_module_dict(globals(), 'fork', only_type="threads") + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_multiprocessing_forkserver.py b/Lib/test/test_multiprocessing_forkserver/__init__.py similarity index 58% rename from Lib/test/test_multiprocessing_forkserver.py rename to Lib/test/test_multiprocessing_forkserver/__init__.py index 6ad5faf9e8a329..d91715a344dfa7 100644 --- a/Lib/test/test_multiprocessing_forkserver.py +++ b/Lib/test/test_multiprocessing_forkserver/__init__.py @@ -1,7 +1,6 @@ -import unittest -import test._test_multiprocessing - +import os.path import sys +import unittest from test import support if support.PGO: @@ -10,7 +9,5 @@ if sys.platform == "win32": raise unittest.SkipTest("forkserver is not available on Windows") -test._test_multiprocessing.install_tests_in_module_dict(globals(), 'forkserver') - -if __name__ == '__main__': - unittest.main() +def load_tests(*args): + return support.load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/test_multiprocessing_forkserver/test_manager.py b/Lib/test/test_multiprocessing_forkserver/test_manager.py new file mode 100644 index 00000000000000..14f8f10dfb4e2f --- /dev/null +++ b/Lib/test/test_multiprocessing_forkserver/test_manager.py @@ -0,0 +1,7 @@ +import unittest +from test._test_multiprocessing import install_tests_in_module_dict + +install_tests_in_module_dict(globals(), 'forkserver', only_type="manager") + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_multiprocessing_forkserver/test_misc.py b/Lib/test/test_multiprocessing_forkserver/test_misc.py new file mode 100644 index 00000000000000..9cae1b50f71e00 --- /dev/null +++ b/Lib/test/test_multiprocessing_forkserver/test_misc.py @@ -0,0 +1,7 @@ +import unittest +from test._test_multiprocessing import install_tests_in_module_dict + +install_tests_in_module_dict(globals(), 'forkserver', exclude_types=True) + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_multiprocessing_forkserver/test_processes.py b/Lib/test/test_multiprocessing_forkserver/test_processes.py new file mode 100644 index 00000000000000..360967cf1ae152 --- /dev/null +++ b/Lib/test/test_multiprocessing_forkserver/test_processes.py @@ -0,0 +1,7 @@ +import unittest +from test._test_multiprocessing import install_tests_in_module_dict + +install_tests_in_module_dict(globals(), 'forkserver', only_type="processes") + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_multiprocessing_forkserver/test_threads.py b/Lib/test/test_multiprocessing_forkserver/test_threads.py new file mode 100644 index 00000000000000..719c752aa05350 --- /dev/null +++ b/Lib/test/test_multiprocessing_forkserver/test_threads.py @@ -0,0 +1,7 @@ +import unittest +from test._test_multiprocessing import install_tests_in_module_dict + +install_tests_in_module_dict(globals(), 'forkserver', only_type="threads") + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_multiprocessing_spawn.py b/Lib/test/test_multiprocessing_spawn.py deleted file mode 100644 index 6558952308f25c..00000000000000 --- a/Lib/test/test_multiprocessing_spawn.py +++ /dev/null @@ -1,12 +0,0 @@ -import unittest -import test._test_multiprocessing - -from test import support - -if support.PGO: - raise unittest.SkipTest("test is not helpful for PGO") - -test._test_multiprocessing.install_tests_in_module_dict(globals(), 'spawn') - -if __name__ == '__main__': - unittest.main() diff --git a/Lib/test/test_multiprocessing_spawn/__init__.py b/Lib/test/test_multiprocessing_spawn/__init__.py new file mode 100644 index 00000000000000..3fd0f9b390612a --- /dev/null +++ b/Lib/test/test_multiprocessing_spawn/__init__.py @@ -0,0 +1,9 @@ +import os.path +import unittest +from test import support + +if support.PGO: + raise unittest.SkipTest("test is not helpful for PGO") + +def load_tests(*args): + return support.load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/test_multiprocessing_spawn/test_manager.py b/Lib/test/test_multiprocessing_spawn/test_manager.py new file mode 100644 index 00000000000000..b40bea0bf61581 --- /dev/null +++ b/Lib/test/test_multiprocessing_spawn/test_manager.py @@ -0,0 +1,7 @@ +import unittest +from test._test_multiprocessing import install_tests_in_module_dict + +install_tests_in_module_dict(globals(), 'spawn', only_type="manager") + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_multiprocessing_spawn/test_misc.py b/Lib/test/test_multiprocessing_spawn/test_misc.py new file mode 100644 index 00000000000000..32f37c5cc81ee1 --- /dev/null +++ b/Lib/test/test_multiprocessing_spawn/test_misc.py @@ -0,0 +1,7 @@ +import unittest +from test._test_multiprocessing import install_tests_in_module_dict + +install_tests_in_module_dict(globals(), 'spawn', exclude_types=True) + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_multiprocessing_spawn/test_processes.py b/Lib/test/test_multiprocessing_spawn/test_processes.py new file mode 100644 index 00000000000000..af764b0d8483ab --- /dev/null +++ b/Lib/test/test_multiprocessing_spawn/test_processes.py @@ -0,0 +1,7 @@ +import unittest +from test._test_multiprocessing import install_tests_in_module_dict + +install_tests_in_module_dict(globals(), 'spawn', only_type="processes") + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_multiprocessing_spawn/test_threads.py b/Lib/test/test_multiprocessing_spawn/test_threads.py new file mode 100644 index 00000000000000..c1257749b9c419 --- /dev/null +++ b/Lib/test/test_multiprocessing_spawn/test_threads.py @@ -0,0 +1,7 @@ +import unittest +from test._test_multiprocessing import install_tests_in_module_dict + +install_tests_in_module_dict(globals(), 'spawn', only_type="threads") + +if __name__ == '__main__': + unittest.main() diff --git a/Misc/NEWS.d/next/Tests/2023-08-24-04-23-35.gh-issue-108388.mr0MeE.rst b/Misc/NEWS.d/next/Tests/2023-08-24-04-23-35.gh-issue-108388.mr0MeE.rst new file mode 100644 index 00000000000000..8cf77b1cc187f1 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-08-24-04-23-35.gh-issue-108388.mr0MeE.rst @@ -0,0 +1,4 @@ +Split test_multiprocessing_fork, test_multiprocessing_forkserver and +test_multiprocessing_spawn into test packages. Each package is made of 4 +sub-tests: processes, threads, manager and misc. It allows running more tests +in parallel and so reduce the total test duration. Patch by Victor Stinner. From 3a6d8e615f4094328f6ed67e8bf314409dd3df26 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 22 Sep 2023 01:29:49 +0200 Subject: [PATCH 335/632] [3.11] gh-108948: Skip test_tarfile.test_modes() on EFTYPE error (#109697) (#109699) gh-108948: Skip test_tarfile.test_modes() on EFTYPE error (#109697) On FreeBSD, regular users cannot set the sticky bit. Skip the test if chmod() fails with EFTYPE error. (cherry picked from commit 26e06ad617bb416201c769fea91cd33d544c6a1c) --- Lib/test/test_tarfile.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index 623452d50ce842..cad13a9e071ee2 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -1,3 +1,4 @@ +import errno import sys import os import io @@ -3723,9 +3724,21 @@ def test_modes(self): tmp_filename = os.path.join(TEMPDIR, "tmp.file") with open(tmp_filename, 'w'): pass - os.chmod(tmp_filename, os.stat(tmp_filename).st_mode | stat.S_ISVTX) - have_sticky_files = (os.stat(tmp_filename).st_mode & stat.S_ISVTX) - os.unlink(tmp_filename) + try: + try: + os.chmod(tmp_filename, + os.stat(tmp_filename).st_mode | stat.S_ISVTX) + except OSError as exc: + if exc.errno == getattr(errno, "EFTYPE", 0): + # gh-108948: On FreeBSD, regular users cannot set + # the sticky bit. + self.skipTest("chmod() failed with EFTYPE: " + "regular users cannot set sticky bit") + else: + raise + have_sticky_files = (os.stat(tmp_filename).st_mode & stat.S_ISVTX) + finally: + os.unlink(tmp_filename) os.mkdir(tmp_filename) os.chmod(tmp_filename, os.stat(tmp_filename).st_mode | stat.S_ISVTX) From 66a973a09efbf6056579ee15a1b38f3860a8e47d Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 22 Sep 2023 03:37:25 +0200 Subject: [PATCH 336/632] [3.11] gh-108388: Convert test_concurrent_futures to package (#108401) (#109704) * gh-108388: Convert test_concurrent_futures to package (#108401) Convert test_concurrent_futures to a package of sub-tests. (cherry picked from commit aa6f787faa4bc45006da4dc2f942fb9b82c98836) Notes on backport to 3.11: * AsCompletedTests: Revert test_future_times_out() => test_zero_timeout() * Restore TODO comment * ThreadPoolExecutorTest.test_hang_global_shutdown_lock(): add @support.requires_resource('cpu'). --- Lib/test/libregrtest/runtest.py | 1 + Lib/test/test_concurrent_futures.py | 1673 ----------------- Lib/test/test_concurrent_futures/__init__.py | 16 + Lib/test/test_concurrent_futures/executor.py | 1 - .../test_as_completed.py | 111 ++ .../test_concurrent_futures/test_deadlock.py | 253 +++ .../test_concurrent_futures/test_future.py | 291 +++ Lib/test/test_concurrent_futures/test_init.py | 117 ++ .../test_process_pool.py | 202 ++ .../test_concurrent_futures/test_shutdown.py | 343 ++++ .../test_thread_pool.py | 100 + Lib/test/test_concurrent_futures/test_wait.py | 3 - Lib/test/test_concurrent_futures/util.py | 141 ++ ...-08-24-06-10-36.gh-issue-108388.YCVB0D.rst | 2 + 14 files changed, 1577 insertions(+), 1677 deletions(-) delete mode 100644 Lib/test/test_concurrent_futures.py create mode 100644 Lib/test/test_concurrent_futures/__init__.py create mode 100644 Lib/test/test_concurrent_futures/test_as_completed.py create mode 100644 Lib/test/test_concurrent_futures/test_deadlock.py create mode 100644 Lib/test/test_concurrent_futures/test_future.py create mode 100644 Lib/test/test_concurrent_futures/test_init.py create mode 100644 Lib/test/test_concurrent_futures/test_process_pool.py create mode 100644 Lib/test/test_concurrent_futures/test_shutdown.py create mode 100644 Lib/test/test_concurrent_futures/test_thread_pool.py create mode 100644 Lib/test/test_concurrent_futures/util.py create mode 100644 Misc/NEWS.d/next/Tests/2023-08-24-06-10-36.gh-issue-108388.YCVB0D.rst diff --git a/Lib/test/libregrtest/runtest.py b/Lib/test/libregrtest/runtest.py index 74e28e27e1abac..000a4aa75c44a0 100644 --- a/Lib/test/libregrtest/runtest.py +++ b/Lib/test/libregrtest/runtest.py @@ -141,6 +141,7 @@ def set_env_changed(self): SPLITTESTDIRS = { "test_asyncio", + "test_concurrent_futures", "test_future_stmt", "test_multiprocessing_fork", "test_multiprocessing_forkserver", diff --git a/Lib/test/test_concurrent_futures.py b/Lib/test/test_concurrent_futures.py deleted file mode 100644 index a2a4d873b1389c..00000000000000 --- a/Lib/test/test_concurrent_futures.py +++ /dev/null @@ -1,1673 +0,0 @@ -from test import support -from test.support import import_helper -from test.support import threading_helper - -# Skip tests if _multiprocessing wasn't built. -import_helper.import_module('_multiprocessing') - -from test.support import hashlib_helper -from test.support.script_helper import assert_python_ok - -import contextlib -import itertools -import logging -from logging.handlers import QueueHandler -import os -import queue -import signal -import sys -import threading -import time -import unittest -import weakref -from pickle import PicklingError - -from concurrent import futures -from concurrent.futures._base import ( - PENDING, RUNNING, CANCELLED, CANCELLED_AND_NOTIFIED, FINISHED, Future, - BrokenExecutor) -from concurrent.futures.process import BrokenProcessPool, _check_system_limits - -import multiprocessing.process -import multiprocessing.util -import multiprocessing as mp - - -if support.check_sanitizer(address=True, memory=True): - # bpo-46633: Skip the test because it is too slow when Python is built - # with ASAN/MSAN: between 5 and 20 minutes on GitHub Actions. - raise unittest.SkipTest("test too slow on ASAN/MSAN build") - - -def create_future(state=PENDING, exception=None, result=None): - f = Future() - f._state = state - f._exception = exception - f._result = result - return f - - -PENDING_FUTURE = create_future(state=PENDING) -RUNNING_FUTURE = create_future(state=RUNNING) -CANCELLED_FUTURE = create_future(state=CANCELLED) -CANCELLED_AND_NOTIFIED_FUTURE = create_future(state=CANCELLED_AND_NOTIFIED) -EXCEPTION_FUTURE = create_future(state=FINISHED, exception=OSError()) -SUCCESSFUL_FUTURE = create_future(state=FINISHED, result=42) - -INITIALIZER_STATUS = 'uninitialized' - -def mul(x, y): - return x * y - -def capture(*args, **kwargs): - return args, kwargs - -def sleep_and_raise(t): - time.sleep(t) - raise Exception('this is an exception') - -def sleep_and_print(t, msg): - time.sleep(t) - print(msg) - sys.stdout.flush() - -def init(x): - global INITIALIZER_STATUS - INITIALIZER_STATUS = x - -def get_init_status(): - return INITIALIZER_STATUS - -def init_fail(log_queue=None): - if log_queue is not None: - logger = logging.getLogger('concurrent.futures') - logger.addHandler(QueueHandler(log_queue)) - logger.setLevel('CRITICAL') - logger.propagate = False - time.sleep(0.1) # let some futures be scheduled - raise ValueError('error in initializer') - - -class MyObject(object): - def my_method(self): - pass - - -class EventfulGCObj(): - def __init__(self, mgr): - self.event = mgr.Event() - - def __del__(self): - self.event.set() - - -def make_dummy_object(_): - return MyObject() - - -class BaseTestCase(unittest.TestCase): - def setUp(self): - self._thread_key = threading_helper.threading_setup() - - def tearDown(self): - support.reap_children() - threading_helper.threading_cleanup(*self._thread_key) - - -class ExecutorMixin: - worker_count = 5 - executor_kwargs = {} - - def setUp(self): - super().setUp() - - self.t1 = time.monotonic() - if hasattr(self, "ctx"): - self.executor = self.executor_type( - max_workers=self.worker_count, - mp_context=self.get_context(), - **self.executor_kwargs) - else: - self.executor = self.executor_type( - max_workers=self.worker_count, - **self.executor_kwargs) - - def tearDown(self): - self.executor.shutdown(wait=True) - self.executor = None - - dt = time.monotonic() - self.t1 - if support.verbose: - print("%.2fs" % dt, end=' ') - self.assertLess(dt, 300, "synchronization issue: test lasted too long") - - super().tearDown() - - def get_context(self): - return mp.get_context(self.ctx) - - -class ThreadPoolMixin(ExecutorMixin): - executor_type = futures.ThreadPoolExecutor - - -class ProcessPoolForkMixin(ExecutorMixin): - executor_type = futures.ProcessPoolExecutor - ctx = "fork" - - def get_context(self): - try: - _check_system_limits() - except NotImplementedError: - self.skipTest("ProcessPoolExecutor unavailable on this system") - if sys.platform == "win32": - self.skipTest("require unix system") - return super().get_context() - - -class ProcessPoolSpawnMixin(ExecutorMixin): - executor_type = futures.ProcessPoolExecutor - ctx = "spawn" - - def get_context(self): - try: - _check_system_limits() - except NotImplementedError: - self.skipTest("ProcessPoolExecutor unavailable on this system") - return super().get_context() - - -class ProcessPoolForkserverMixin(ExecutorMixin): - executor_type = futures.ProcessPoolExecutor - ctx = "forkserver" - - def get_context(self): - try: - _check_system_limits() - except NotImplementedError: - self.skipTest("ProcessPoolExecutor unavailable on this system") - if sys.platform == "win32": - self.skipTest("require unix system") - return super().get_context() - - -def create_executor_tests(mixin, bases=(BaseTestCase,), - executor_mixins=(ThreadPoolMixin, - ProcessPoolForkMixin, - ProcessPoolForkserverMixin, - ProcessPoolSpawnMixin)): - def strip_mixin(name): - if name.endswith(('Mixin', 'Tests')): - return name[:-5] - elif name.endswith('Test'): - return name[:-4] - else: - return name - - for exe in executor_mixins: - name = ("%s%sTest" - % (strip_mixin(exe.__name__), strip_mixin(mixin.__name__))) - cls = type(name, (mixin,) + (exe,) + bases, {}) - globals()[name] = cls - - -class InitializerMixin(ExecutorMixin): - worker_count = 2 - - def setUp(self): - global INITIALIZER_STATUS - INITIALIZER_STATUS = 'uninitialized' - self.executor_kwargs = dict(initializer=init, - initargs=('initialized',)) - super().setUp() - - def test_initializer(self): - futures = [self.executor.submit(get_init_status) - for _ in range(self.worker_count)] - - for f in futures: - self.assertEqual(f.result(), 'initialized') - - -class FailingInitializerMixin(ExecutorMixin): - worker_count = 2 - - def setUp(self): - if hasattr(self, "ctx"): - # Pass a queue to redirect the child's logging output - self.mp_context = self.get_context() - self.log_queue = self.mp_context.Queue() - self.executor_kwargs = dict(initializer=init_fail, - initargs=(self.log_queue,)) - else: - # In a thread pool, the child shares our logging setup - # (see _assert_logged()) - self.mp_context = None - self.log_queue = None - self.executor_kwargs = dict(initializer=init_fail) - super().setUp() - - def test_initializer(self): - with self._assert_logged('ValueError: error in initializer'): - try: - future = self.executor.submit(get_init_status) - except BrokenExecutor: - # Perhaps the executor is already broken - pass - else: - with self.assertRaises(BrokenExecutor): - future.result() - # At some point, the executor should break - t1 = time.monotonic() - while not self.executor._broken: - if time.monotonic() - t1 > 5: - self.fail("executor not broken after 5 s.") - time.sleep(0.01) - # ... and from this point submit() is guaranteed to fail - with self.assertRaises(BrokenExecutor): - self.executor.submit(get_init_status) - - @contextlib.contextmanager - def _assert_logged(self, msg): - if self.log_queue is not None: - yield - output = [] - try: - while True: - output.append(self.log_queue.get_nowait().getMessage()) - except queue.Empty: - pass - else: - with self.assertLogs('concurrent.futures', 'CRITICAL') as cm: - yield - output = cm.output - self.assertTrue(any(msg in line for line in output), - output) - - -create_executor_tests(InitializerMixin) -create_executor_tests(FailingInitializerMixin) - - -class ExecutorShutdownTest: - def test_run_after_shutdown(self): - self.executor.shutdown() - self.assertRaises(RuntimeError, - self.executor.submit, - pow, 2, 5) - - def test_interpreter_shutdown(self): - # Test the atexit hook for shutdown of worker threads and processes - rc, out, err = assert_python_ok('-c', """if 1: - from concurrent.futures import {executor_type} - from time import sleep - from test.test_concurrent_futures import sleep_and_print - if __name__ == "__main__": - context = '{context}' - if context == "": - t = {executor_type}(5) - else: - from multiprocessing import get_context - context = get_context(context) - t = {executor_type}(5, mp_context=context) - t.submit(sleep_and_print, 1.0, "apple") - """.format(executor_type=self.executor_type.__name__, - context=getattr(self, "ctx", ""))) - # Errors in atexit hooks don't change the process exit code, check - # stderr manually. - self.assertFalse(err) - self.assertEqual(out.strip(), b"apple") - - def test_submit_after_interpreter_shutdown(self): - # Test the atexit hook for shutdown of worker threads and processes - rc, out, err = assert_python_ok('-c', """if 1: - import atexit - @atexit.register - def run_last(): - try: - t.submit(id, None) - except RuntimeError: - print("runtime-error") - raise - from concurrent.futures import {executor_type} - if __name__ == "__main__": - context = '{context}' - if not context: - t = {executor_type}(5) - else: - from multiprocessing import get_context - context = get_context(context) - t = {executor_type}(5, mp_context=context) - t.submit(id, 42).result() - """.format(executor_type=self.executor_type.__name__, - context=getattr(self, "ctx", ""))) - # Errors in atexit hooks don't change the process exit code, check - # stderr manually. - self.assertIn("RuntimeError: cannot schedule new futures", err.decode()) - self.assertEqual(out.strip(), b"runtime-error") - - def test_hang_issue12364(self): - fs = [self.executor.submit(time.sleep, 0.1) for _ in range(50)] - self.executor.shutdown() - for f in fs: - f.result() - - def test_cancel_futures(self): - assert self.worker_count <= 5, "test needs few workers" - fs = [self.executor.submit(time.sleep, .1) for _ in range(50)] - self.executor.shutdown(cancel_futures=True) - # We can't guarantee the exact number of cancellations, but we can - # guarantee that *some* were cancelled. With few workers, many of - # the submitted futures should have been cancelled. - cancelled = [fut for fut in fs if fut.cancelled()] - self.assertGreater(len(cancelled), 20) - - # Ensure the other futures were able to finish. - # Use "not fut.cancelled()" instead of "fut.done()" to include futures - # that may have been left in a pending state. - others = [fut for fut in fs if not fut.cancelled()] - for fut in others: - self.assertTrue(fut.done(), msg=f"{fut._state=}") - self.assertIsNone(fut.exception()) - - # Similar to the number of cancelled futures, we can't guarantee the - # exact number that completed. But, we can guarantee that at least - # one finished. - self.assertGreater(len(others), 0) - - def test_hang_gh83386(self): - """shutdown(wait=False) doesn't hang at exit with running futures. - - See https://github.com/python/cpython/issues/83386. - """ - if self.executor_type == futures.ProcessPoolExecutor: - raise unittest.SkipTest( - "Hangs, see https://github.com/python/cpython/issues/83386") - - rc, out, err = assert_python_ok('-c', """if True: - from concurrent.futures import {executor_type} - from test.test_concurrent_futures import sleep_and_print - if __name__ == "__main__": - if {context!r}: multiprocessing.set_start_method({context!r}) - t = {executor_type}(max_workers=3) - t.submit(sleep_and_print, 1.0, "apple") - t.shutdown(wait=False) - """.format(executor_type=self.executor_type.__name__, - context=getattr(self, 'ctx', None))) - self.assertFalse(err) - self.assertEqual(out.strip(), b"apple") - - def test_hang_gh94440(self): - """shutdown(wait=True) doesn't hang when a future was submitted and - quickly canceled right before shutdown. - - See https://github.com/python/cpython/issues/94440. - """ - if not hasattr(signal, 'alarm'): - raise unittest.SkipTest( - "Tested platform does not support the alarm signal") - - def timeout(_signum, _frame): - raise RuntimeError("timed out waiting for shutdown") - - kwargs = {} - if getattr(self, 'ctx', None): - kwargs['mp_context'] = self.get_context() - executor = self.executor_type(max_workers=1, **kwargs) - executor.submit(int).result() - old_handler = signal.signal(signal.SIGALRM, timeout) - try: - signal.alarm(5) - executor.submit(int).cancel() - executor.shutdown(wait=True) - finally: - signal.alarm(0) - signal.signal(signal.SIGALRM, old_handler) - - -class ThreadPoolShutdownTest(ThreadPoolMixin, ExecutorShutdownTest, BaseTestCase): - def test_threads_terminate(self): - def acquire_lock(lock): - lock.acquire() - - sem = threading.Semaphore(0) - for i in range(3): - self.executor.submit(acquire_lock, sem) - self.assertEqual(len(self.executor._threads), 3) - for i in range(3): - sem.release() - self.executor.shutdown() - for t in self.executor._threads: - t.join() - - def test_context_manager_shutdown(self): - with futures.ThreadPoolExecutor(max_workers=5) as e: - executor = e - self.assertEqual(list(e.map(abs, range(-5, 5))), - [5, 4, 3, 2, 1, 0, 1, 2, 3, 4]) - - for t in executor._threads: - t.join() - - def test_del_shutdown(self): - executor = futures.ThreadPoolExecutor(max_workers=5) - res = executor.map(abs, range(-5, 5)) - threads = executor._threads - del executor - - for t in threads: - t.join() - - # Make sure the results were all computed before the - # executor got shutdown. - assert all([r == abs(v) for r, v in zip(res, range(-5, 5))]) - - def test_shutdown_no_wait(self): - # Ensure that the executor cleans up the threads when calling - # shutdown with wait=False - executor = futures.ThreadPoolExecutor(max_workers=5) - res = executor.map(abs, range(-5, 5)) - threads = executor._threads - executor.shutdown(wait=False) - for t in threads: - t.join() - - # Make sure the results were all computed before the - # executor got shutdown. - assert all([r == abs(v) for r, v in zip(res, range(-5, 5))]) - - - def test_thread_names_assigned(self): - executor = futures.ThreadPoolExecutor( - max_workers=5, thread_name_prefix='SpecialPool') - executor.map(abs, range(-5, 5)) - threads = executor._threads - del executor - support.gc_collect() # For PyPy or other GCs. - - for t in threads: - self.assertRegex(t.name, r'^SpecialPool_[0-4]$') - t.join() - - def test_thread_names_default(self): - executor = futures.ThreadPoolExecutor(max_workers=5) - executor.map(abs, range(-5, 5)) - threads = executor._threads - del executor - support.gc_collect() # For PyPy or other GCs. - - for t in threads: - # Ensure that our default name is reasonably sane and unique when - # no thread_name_prefix was supplied. - self.assertRegex(t.name, r'ThreadPoolExecutor-\d+_[0-4]$') - t.join() - - def test_cancel_futures_wait_false(self): - # Can only be reliably tested for TPE, since PPE often hangs with - # `wait=False` (even without *cancel_futures*). - rc, out, err = assert_python_ok('-c', """if True: - from concurrent.futures import ThreadPoolExecutor - from test.test_concurrent_futures import sleep_and_print - if __name__ == "__main__": - t = ThreadPoolExecutor() - t.submit(sleep_and_print, .1, "apple") - t.shutdown(wait=False, cancel_futures=True) - """) - # Errors in atexit hooks don't change the process exit code, check - # stderr manually. - self.assertFalse(err) - self.assertEqual(out.strip(), b"apple") - - -class ProcessPoolShutdownTest(ExecutorShutdownTest): - def test_processes_terminate(self): - def acquire_lock(lock): - lock.acquire() - - mp_context = self.get_context() - if mp_context.get_start_method(allow_none=False) == "fork": - # fork pre-spawns, not on demand. - expected_num_processes = self.worker_count - else: - expected_num_processes = 3 - - sem = mp_context.Semaphore(0) - for _ in range(3): - self.executor.submit(acquire_lock, sem) - self.assertEqual(len(self.executor._processes), expected_num_processes) - for _ in range(3): - sem.release() - processes = self.executor._processes - self.executor.shutdown() - - for p in processes.values(): - p.join() - - def test_context_manager_shutdown(self): - with futures.ProcessPoolExecutor( - max_workers=5, mp_context=self.get_context()) as e: - processes = e._processes - self.assertEqual(list(e.map(abs, range(-5, 5))), - [5, 4, 3, 2, 1, 0, 1, 2, 3, 4]) - - for p in processes.values(): - p.join() - - def test_del_shutdown(self): - executor = futures.ProcessPoolExecutor( - max_workers=5, mp_context=self.get_context()) - res = executor.map(abs, range(-5, 5)) - executor_manager_thread = executor._executor_manager_thread - processes = executor._processes - call_queue = executor._call_queue - executor_manager_thread = executor._executor_manager_thread - del executor - support.gc_collect() # For PyPy or other GCs. - - # Make sure that all the executor resources were properly cleaned by - # the shutdown process - executor_manager_thread.join() - for p in processes.values(): - p.join() - call_queue.join_thread() - - # Make sure the results were all computed before the - # executor got shutdown. - assert all([r == abs(v) for r, v in zip(res, range(-5, 5))]) - - def test_shutdown_no_wait(self): - # Ensure that the executor cleans up the processes when calling - # shutdown with wait=False - executor = futures.ProcessPoolExecutor( - max_workers=5, mp_context=self.get_context()) - res = executor.map(abs, range(-5, 5)) - processes = executor._processes - call_queue = executor._call_queue - executor_manager_thread = executor._executor_manager_thread - executor.shutdown(wait=False) - - # Make sure that all the executor resources were properly cleaned by - # the shutdown process - executor_manager_thread.join() - for p in processes.values(): - p.join() - call_queue.join_thread() - - # Make sure the results were all computed before the executor got - # shutdown. - assert all([r == abs(v) for r, v in zip(res, range(-5, 5))]) - - -create_executor_tests(ProcessPoolShutdownTest, - executor_mixins=(ProcessPoolForkMixin, - ProcessPoolForkserverMixin, - ProcessPoolSpawnMixin)) - - -class WaitTests: - def test_20369(self): - # See https://bugs.python.org/issue20369 - future = self.executor.submit(time.sleep, 1.5) - done, not_done = futures.wait([future, future], - return_when=futures.ALL_COMPLETED) - self.assertEqual({future}, done) - self.assertEqual(set(), not_done) - - - def test_first_completed(self): - future1 = self.executor.submit(mul, 21, 2) - future2 = self.executor.submit(time.sleep, 1.5) - - done, not_done = futures.wait( - [CANCELLED_FUTURE, future1, future2], - return_when=futures.FIRST_COMPLETED) - - self.assertEqual(set([future1]), done) - self.assertEqual(set([CANCELLED_FUTURE, future2]), not_done) - - def test_first_completed_some_already_completed(self): - future1 = self.executor.submit(time.sleep, 1.5) - - finished, pending = futures.wait( - [CANCELLED_AND_NOTIFIED_FUTURE, SUCCESSFUL_FUTURE, future1], - return_when=futures.FIRST_COMPLETED) - - self.assertEqual( - set([CANCELLED_AND_NOTIFIED_FUTURE, SUCCESSFUL_FUTURE]), - finished) - self.assertEqual(set([future1]), pending) - - def test_first_exception(self): - future1 = self.executor.submit(mul, 2, 21) - future2 = self.executor.submit(sleep_and_raise, 1.5) - future3 = self.executor.submit(time.sleep, 3) - - finished, pending = futures.wait( - [future1, future2, future3], - return_when=futures.FIRST_EXCEPTION) - - self.assertEqual(set([future1, future2]), finished) - self.assertEqual(set([future3]), pending) - - def test_first_exception_some_already_complete(self): - future1 = self.executor.submit(divmod, 21, 0) - future2 = self.executor.submit(time.sleep, 1.5) - - finished, pending = futures.wait( - [SUCCESSFUL_FUTURE, - CANCELLED_FUTURE, - CANCELLED_AND_NOTIFIED_FUTURE, - future1, future2], - return_when=futures.FIRST_EXCEPTION) - - self.assertEqual(set([SUCCESSFUL_FUTURE, - CANCELLED_AND_NOTIFIED_FUTURE, - future1]), finished) - self.assertEqual(set([CANCELLED_FUTURE, future2]), pending) - - def test_first_exception_one_already_failed(self): - future1 = self.executor.submit(time.sleep, 2) - - finished, pending = futures.wait( - [EXCEPTION_FUTURE, future1], - return_when=futures.FIRST_EXCEPTION) - - self.assertEqual(set([EXCEPTION_FUTURE]), finished) - self.assertEqual(set([future1]), pending) - - def test_all_completed(self): - future1 = self.executor.submit(divmod, 2, 0) - future2 = self.executor.submit(mul, 2, 21) - - finished, pending = futures.wait( - [SUCCESSFUL_FUTURE, - CANCELLED_AND_NOTIFIED_FUTURE, - EXCEPTION_FUTURE, - future1, - future2], - return_when=futures.ALL_COMPLETED) - - self.assertEqual(set([SUCCESSFUL_FUTURE, - CANCELLED_AND_NOTIFIED_FUTURE, - EXCEPTION_FUTURE, - future1, - future2]), finished) - self.assertEqual(set(), pending) - - def test_timeout(self): - future1 = self.executor.submit(mul, 6, 7) - future2 = self.executor.submit(time.sleep, 6) - - finished, pending = futures.wait( - [CANCELLED_AND_NOTIFIED_FUTURE, - EXCEPTION_FUTURE, - SUCCESSFUL_FUTURE, - future1, future2], - timeout=5, - return_when=futures.ALL_COMPLETED) - - self.assertEqual(set([CANCELLED_AND_NOTIFIED_FUTURE, - EXCEPTION_FUTURE, - SUCCESSFUL_FUTURE, - future1]), finished) - self.assertEqual(set([future2]), pending) - - -class ThreadPoolWaitTests(ThreadPoolMixin, WaitTests, BaseTestCase): - - def test_pending_calls_race(self): - # Issue #14406: multi-threaded race condition when waiting on all - # futures. - event = threading.Event() - def future_func(): - event.wait() - oldswitchinterval = sys.getswitchinterval() - sys.setswitchinterval(1e-6) - try: - fs = {self.executor.submit(future_func) for i in range(100)} - event.set() - futures.wait(fs, return_when=futures.ALL_COMPLETED) - finally: - sys.setswitchinterval(oldswitchinterval) - - -create_executor_tests(WaitTests, - executor_mixins=(ProcessPoolForkMixin, - ProcessPoolForkserverMixin, - ProcessPoolSpawnMixin)) - - -class AsCompletedTests: - # TODO(brian@sweetapp.com): Should have a test with a non-zero timeout. - def test_no_timeout(self): - future1 = self.executor.submit(mul, 2, 21) - future2 = self.executor.submit(mul, 7, 6) - - completed = set(futures.as_completed( - [CANCELLED_AND_NOTIFIED_FUTURE, - EXCEPTION_FUTURE, - SUCCESSFUL_FUTURE, - future1, future2])) - self.assertEqual(set( - [CANCELLED_AND_NOTIFIED_FUTURE, - EXCEPTION_FUTURE, - SUCCESSFUL_FUTURE, - future1, future2]), - completed) - - def test_zero_timeout(self): - future1 = self.executor.submit(time.sleep, 2) - completed_futures = set() - try: - for future in futures.as_completed( - [CANCELLED_AND_NOTIFIED_FUTURE, - EXCEPTION_FUTURE, - SUCCESSFUL_FUTURE, - future1], - timeout=0): - completed_futures.add(future) - except futures.TimeoutError: - pass - - self.assertEqual(set([CANCELLED_AND_NOTIFIED_FUTURE, - EXCEPTION_FUTURE, - SUCCESSFUL_FUTURE]), - completed_futures) - - def test_duplicate_futures(self): - # Issue 20367. Duplicate futures should not raise exceptions or give - # duplicate responses. - # Issue #31641: accept arbitrary iterables. - future1 = self.executor.submit(time.sleep, 2) - completed = [ - f for f in futures.as_completed(itertools.repeat(future1, 3)) - ] - self.assertEqual(len(completed), 1) - - def test_free_reference_yielded_future(self): - # Issue #14406: Generator should not keep references - # to finished futures. - futures_list = [Future() for _ in range(8)] - futures_list.append(create_future(state=CANCELLED_AND_NOTIFIED)) - futures_list.append(create_future(state=FINISHED, result=42)) - - with self.assertRaises(futures.TimeoutError): - for future in futures.as_completed(futures_list, timeout=0): - futures_list.remove(future) - wr = weakref.ref(future) - del future - support.gc_collect() # For PyPy or other GCs. - self.assertIsNone(wr()) - - futures_list[0].set_result("test") - for future in futures.as_completed(futures_list): - futures_list.remove(future) - wr = weakref.ref(future) - del future - support.gc_collect() # For PyPy or other GCs. - self.assertIsNone(wr()) - if futures_list: - futures_list[0].set_result("test") - - def test_correct_timeout_exception_msg(self): - futures_list = [CANCELLED_AND_NOTIFIED_FUTURE, PENDING_FUTURE, - RUNNING_FUTURE, SUCCESSFUL_FUTURE] - - with self.assertRaises(futures.TimeoutError) as cm: - list(futures.as_completed(futures_list, timeout=0)) - - self.assertEqual(str(cm.exception), '2 (of 4) futures unfinished') - - -create_executor_tests(AsCompletedTests) - - -class ExecutorTest: - # Executor.shutdown() and context manager usage is tested by - # ExecutorShutdownTest. - def test_submit(self): - future = self.executor.submit(pow, 2, 8) - self.assertEqual(256, future.result()) - - def test_submit_keyword(self): - future = self.executor.submit(mul, 2, y=8) - self.assertEqual(16, future.result()) - future = self.executor.submit(capture, 1, self=2, fn=3) - self.assertEqual(future.result(), ((1,), {'self': 2, 'fn': 3})) - with self.assertRaises(TypeError): - self.executor.submit(fn=capture, arg=1) - with self.assertRaises(TypeError): - self.executor.submit(arg=1) - - def test_map(self): - self.assertEqual( - list(self.executor.map(pow, range(10), range(10))), - list(map(pow, range(10), range(10)))) - - self.assertEqual( - list(self.executor.map(pow, range(10), range(10), chunksize=3)), - list(map(pow, range(10), range(10)))) - - def test_map_exception(self): - i = self.executor.map(divmod, [1, 1, 1, 1], [2, 3, 0, 5]) - self.assertEqual(i.__next__(), (0, 1)) - self.assertEqual(i.__next__(), (0, 1)) - self.assertRaises(ZeroDivisionError, i.__next__) - - def test_map_timeout(self): - results = [] - try: - for i in self.executor.map(time.sleep, - [0, 0, 6], - timeout=5): - results.append(i) - except futures.TimeoutError: - pass - else: - self.fail('expected TimeoutError') - - self.assertEqual([None, None], results) - - def test_shutdown_race_issue12456(self): - # Issue #12456: race condition at shutdown where trying to post a - # sentinel in the call queue blocks (the queue is full while processes - # have exited). - self.executor.map(str, [2] * (self.worker_count + 1)) - self.executor.shutdown() - - @support.cpython_only - def test_no_stale_references(self): - # Issue #16284: check that the executors don't unnecessarily hang onto - # references. - my_object = MyObject() - my_object_collected = threading.Event() - my_object_callback = weakref.ref( - my_object, lambda obj: my_object_collected.set()) - # Deliberately discarding the future. - self.executor.submit(my_object.my_method) - del my_object - - collected = my_object_collected.wait(timeout=support.SHORT_TIMEOUT) - self.assertTrue(collected, - "Stale reference not collected within timeout.") - - def test_max_workers_negative(self): - for number in (0, -1): - with self.assertRaisesRegex(ValueError, - "max_workers must be greater " - "than 0"): - self.executor_type(max_workers=number) - - def test_free_reference(self): - # Issue #14406: Result iterator should not keep an internal - # reference to result objects. - for obj in self.executor.map(make_dummy_object, range(10)): - wr = weakref.ref(obj) - del obj - support.gc_collect() # For PyPy or other GCs. - self.assertIsNone(wr()) - - -class ThreadPoolExecutorTest(ThreadPoolMixin, ExecutorTest, BaseTestCase): - def test_map_submits_without_iteration(self): - """Tests verifying issue 11777.""" - finished = [] - def record_finished(n): - finished.append(n) - - self.executor.map(record_finished, range(10)) - self.executor.shutdown(wait=True) - self.assertCountEqual(finished, range(10)) - - def test_default_workers(self): - executor = self.executor_type() - expected = min(32, (os.cpu_count() or 1) + 4) - self.assertEqual(executor._max_workers, expected) - - def test_saturation(self): - executor = self.executor_type(4) - def acquire_lock(lock): - lock.acquire() - - sem = threading.Semaphore(0) - for i in range(15 * executor._max_workers): - executor.submit(acquire_lock, sem) - self.assertEqual(len(executor._threads), executor._max_workers) - for i in range(15 * executor._max_workers): - sem.release() - executor.shutdown(wait=True) - - def test_idle_thread_reuse(self): - executor = self.executor_type() - executor.submit(mul, 21, 2).result() - executor.submit(mul, 6, 7).result() - executor.submit(mul, 3, 14).result() - self.assertEqual(len(executor._threads), 1) - executor.shutdown(wait=True) - - @unittest.skipUnless(hasattr(os, 'register_at_fork'), 'need os.register_at_fork') - @support.requires_resource('cpu') - def test_hang_global_shutdown_lock(self): - # bpo-45021: _global_shutdown_lock should be reinitialized in the child - # process, otherwise it will never exit - def submit(pool): - pool.submit(submit, pool) - - with futures.ThreadPoolExecutor(1) as pool: - pool.submit(submit, pool) - - for _ in range(50): - with futures.ProcessPoolExecutor(1, mp_context=mp.get_context('fork')) as workers: - workers.submit(tuple) - - def test_executor_map_current_future_cancel(self): - stop_event = threading.Event() - log = [] - - def log_n_wait(ident): - log.append(f"{ident=} started") - try: - stop_event.wait() - finally: - log.append(f"{ident=} stopped") - - with self.executor_type(max_workers=1) as pool: - # submit work to saturate the pool - fut = pool.submit(log_n_wait, ident="first") - try: - with contextlib.closing( - pool.map(log_n_wait, ["second", "third"], timeout=0) - ) as gen: - with self.assertRaises(TimeoutError): - next(gen) - finally: - stop_event.set() - fut.result() - # ident='second' is cancelled as a result of raising a TimeoutError - # ident='third' is cancelled because it remained in the collection of futures - self.assertListEqual(log, ["ident='first' started", "ident='first' stopped"]) - - -class ProcessPoolExecutorTest(ExecutorTest): - - @unittest.skipUnless(sys.platform=='win32', 'Windows-only process limit') - def test_max_workers_too_large(self): - with self.assertRaisesRegex(ValueError, - "max_workers must be <= 61"): - futures.ProcessPoolExecutor(max_workers=62) - - def test_killed_child(self): - # When a child process is abruptly terminated, the whole pool gets - # "broken". - futures = [self.executor.submit(time.sleep, 3)] - # Get one of the processes, and terminate (kill) it - p = next(iter(self.executor._processes.values())) - p.terminate() - for fut in futures: - self.assertRaises(BrokenProcessPool, fut.result) - # Submitting other jobs fails as well. - self.assertRaises(BrokenProcessPool, self.executor.submit, pow, 2, 8) - - def test_map_chunksize(self): - def bad_map(): - list(self.executor.map(pow, range(40), range(40), chunksize=-1)) - - ref = list(map(pow, range(40), range(40))) - self.assertEqual( - list(self.executor.map(pow, range(40), range(40), chunksize=6)), - ref) - self.assertEqual( - list(self.executor.map(pow, range(40), range(40), chunksize=50)), - ref) - self.assertEqual( - list(self.executor.map(pow, range(40), range(40), chunksize=40)), - ref) - self.assertRaises(ValueError, bad_map) - - @classmethod - def _test_traceback(cls): - raise RuntimeError(123) # some comment - - def test_traceback(self): - # We want ensure that the traceback from the child process is - # contained in the traceback raised in the main process. - future = self.executor.submit(self._test_traceback) - with self.assertRaises(Exception) as cm: - future.result() - - exc = cm.exception - self.assertIs(type(exc), RuntimeError) - self.assertEqual(exc.args, (123,)) - cause = exc.__cause__ - self.assertIs(type(cause), futures.process._RemoteTraceback) - self.assertIn('raise RuntimeError(123) # some comment', cause.tb) - - with support.captured_stderr() as f1: - try: - raise exc - except RuntimeError: - sys.excepthook(*sys.exc_info()) - self.assertIn('raise RuntimeError(123) # some comment', - f1.getvalue()) - - @hashlib_helper.requires_hashdigest('md5') - def test_ressources_gced_in_workers(self): - # Ensure that argument for a job are correctly gc-ed after the job - # is finished - mgr = self.get_context().Manager() - obj = EventfulGCObj(mgr) - future = self.executor.submit(id, obj) - future.result() - - self.assertTrue(obj.event.wait(timeout=1)) - - # explicitly destroy the object to ensure that EventfulGCObj.__del__() - # is called while manager is still running. - obj = None - support.gc_collect() - - mgr.shutdown() - mgr.join() - - def test_saturation(self): - executor = self.executor - mp_context = self.get_context() - sem = mp_context.Semaphore(0) - job_count = 15 * executor._max_workers - for _ in range(job_count): - executor.submit(sem.acquire) - self.assertEqual(len(executor._processes), executor._max_workers) - for _ in range(job_count): - sem.release() - - def test_idle_process_reuse_one(self): - executor = self.executor - assert executor._max_workers >= 4 - if self.get_context().get_start_method(allow_none=False) == "fork": - raise unittest.SkipTest("Incompatible with the fork start method.") - executor.submit(mul, 21, 2).result() - executor.submit(mul, 6, 7).result() - executor.submit(mul, 3, 14).result() - self.assertEqual(len(executor._processes), 1) - - def test_idle_process_reuse_multiple(self): - executor = self.executor - assert executor._max_workers <= 5 - if self.get_context().get_start_method(allow_none=False) == "fork": - raise unittest.SkipTest("Incompatible with the fork start method.") - executor.submit(mul, 12, 7).result() - executor.submit(mul, 33, 25) - executor.submit(mul, 25, 26).result() - executor.submit(mul, 18, 29) - executor.submit(mul, 1, 2).result() - executor.submit(mul, 0, 9) - self.assertLessEqual(len(executor._processes), 3) - executor.shutdown() - - def test_max_tasks_per_child(self): - context = self.get_context() - if context.get_start_method(allow_none=False) == "fork": - with self.assertRaises(ValueError): - self.executor_type(1, mp_context=context, max_tasks_per_child=3) - return - # not using self.executor as we need to control construction. - # arguably this could go in another class w/o that mixin. - executor = self.executor_type( - 1, mp_context=context, max_tasks_per_child=3) - f1 = executor.submit(os.getpid) - original_pid = f1.result() - # The worker pid remains the same as the worker could be reused - f2 = executor.submit(os.getpid) - self.assertEqual(f2.result(), original_pid) - self.assertEqual(len(executor._processes), 1) - f3 = executor.submit(os.getpid) - self.assertEqual(f3.result(), original_pid) - - # A new worker is spawned, with a statistically different pid, - # while the previous was reaped. - f4 = executor.submit(os.getpid) - new_pid = f4.result() - self.assertNotEqual(original_pid, new_pid) - self.assertEqual(len(executor._processes), 1) - - executor.shutdown() - - def test_max_tasks_per_child_defaults_to_spawn_context(self): - # not using self.executor as we need to control construction. - # arguably this could go in another class w/o that mixin. - executor = self.executor_type(1, max_tasks_per_child=3) - self.assertEqual(executor._mp_context.get_start_method(), "spawn") - - def test_max_tasks_early_shutdown(self): - context = self.get_context() - if context.get_start_method(allow_none=False) == "fork": - raise unittest.SkipTest("Incompatible with the fork start method.") - # not using self.executor as we need to control construction. - # arguably this could go in another class w/o that mixin. - executor = self.executor_type( - 3, mp_context=context, max_tasks_per_child=1) - futures = [] - for i in range(6): - futures.append(executor.submit(mul, i, i)) - executor.shutdown() - for i, future in enumerate(futures): - self.assertEqual(future.result(), mul(i, i)) - - -create_executor_tests(ProcessPoolExecutorTest, - executor_mixins=(ProcessPoolForkMixin, - ProcessPoolForkserverMixin, - ProcessPoolSpawnMixin)) - -def _crash(delay=None): - """Induces a segfault.""" - if delay: - time.sleep(delay) - import faulthandler - faulthandler.disable() - faulthandler._sigsegv() - - -def _crash_with_data(data): - """Induces a segfault with dummy data in input.""" - _crash() - - -def _exit(): - """Induces a sys exit with exitcode 1.""" - sys.exit(1) - - -def _raise_error(Err): - """Function that raises an Exception in process.""" - raise Err() - - -def _raise_error_ignore_stderr(Err): - """Function that raises an Exception in process and ignores stderr.""" - import io - sys.stderr = io.StringIO() - raise Err() - - -def _return_instance(cls): - """Function that returns a instance of cls.""" - return cls() - - -class CrashAtPickle(object): - """Bad object that triggers a segfault at pickling time.""" - def __reduce__(self): - _crash() - - -class CrashAtUnpickle(object): - """Bad object that triggers a segfault at unpickling time.""" - def __reduce__(self): - return _crash, () - - -class ExitAtPickle(object): - """Bad object that triggers a process exit at pickling time.""" - def __reduce__(self): - _exit() - - -class ExitAtUnpickle(object): - """Bad object that triggers a process exit at unpickling time.""" - def __reduce__(self): - return _exit, () - - -class ErrorAtPickle(object): - """Bad object that triggers an error at pickling time.""" - def __reduce__(self): - from pickle import PicklingError - raise PicklingError("Error in pickle") - - -class ErrorAtUnpickle(object): - """Bad object that triggers an error at unpickling time.""" - def __reduce__(self): - from pickle import UnpicklingError - return _raise_error_ignore_stderr, (UnpicklingError, ) - - -class ExecutorDeadlockTest: - TIMEOUT = support.SHORT_TIMEOUT - - def _fail_on_deadlock(self, executor): - # If we did not recover before TIMEOUT seconds, consider that the - # executor is in a deadlock state and forcefully clean all its - # composants. - import faulthandler - from tempfile import TemporaryFile - with TemporaryFile(mode="w+") as f: - faulthandler.dump_traceback(file=f) - f.seek(0) - tb = f.read() - for p in executor._processes.values(): - p.terminate() - # This should be safe to call executor.shutdown here as all possible - # deadlocks should have been broken. - executor.shutdown(wait=True) - print(f"\nTraceback:\n {tb}", file=sys.__stderr__) - self.fail(f"Executor deadlock:\n\n{tb}") - - - def _check_crash(self, error, func, *args, ignore_stderr=False): - # test for deadlock caused by crashes in a pool - self.executor.shutdown(wait=True) - - executor = self.executor_type( - max_workers=2, mp_context=self.get_context()) - res = executor.submit(func, *args) - - if ignore_stderr: - cm = support.captured_stderr() - else: - cm = contextlib.nullcontext() - - try: - with self.assertRaises(error): - with cm: - res.result(timeout=self.TIMEOUT) - except futures.TimeoutError: - # If we did not recover before TIMEOUT seconds, - # consider that the executor is in a deadlock state - self._fail_on_deadlock(executor) - executor.shutdown(wait=True) - - def test_error_at_task_pickle(self): - # Check problem occurring while pickling a task in - # the task_handler thread - self._check_crash(PicklingError, id, ErrorAtPickle()) - - def test_exit_at_task_unpickle(self): - # Check problem occurring while unpickling a task on workers - self._check_crash(BrokenProcessPool, id, ExitAtUnpickle()) - - def test_error_at_task_unpickle(self): - # Check problem occurring while unpickling a task on workers - self._check_crash(BrokenProcessPool, id, ErrorAtUnpickle()) - - def test_crash_at_task_unpickle(self): - # Check problem occurring while unpickling a task on workers - self._check_crash(BrokenProcessPool, id, CrashAtUnpickle()) - - def test_crash_during_func_exec_on_worker(self): - # Check problem occurring during func execution on workers - self._check_crash(BrokenProcessPool, _crash) - - def test_exit_during_func_exec_on_worker(self): - # Check problem occurring during func execution on workers - self._check_crash(SystemExit, _exit) - - def test_error_during_func_exec_on_worker(self): - # Check problem occurring during func execution on workers - self._check_crash(RuntimeError, _raise_error, RuntimeError) - - def test_crash_during_result_pickle_on_worker(self): - # Check problem occurring while pickling a task result - # on workers - self._check_crash(BrokenProcessPool, _return_instance, CrashAtPickle) - - def test_exit_during_result_pickle_on_worker(self): - # Check problem occurring while pickling a task result - # on workers - self._check_crash(SystemExit, _return_instance, ExitAtPickle) - - def test_error_during_result_pickle_on_worker(self): - # Check problem occurring while pickling a task result - # on workers - self._check_crash(PicklingError, _return_instance, ErrorAtPickle) - - def test_error_during_result_unpickle_in_result_handler(self): - # Check problem occurring while unpickling a task in - # the result_handler thread - self._check_crash(BrokenProcessPool, - _return_instance, ErrorAtUnpickle, - ignore_stderr=True) - - def test_exit_during_result_unpickle_in_result_handler(self): - # Check problem occurring while unpickling a task in - # the result_handler thread - self._check_crash(BrokenProcessPool, _return_instance, ExitAtUnpickle) - - def test_shutdown_deadlock(self): - # Test that the pool calling shutdown do not cause deadlock - # if a worker fails after the shutdown call. - self.executor.shutdown(wait=True) - with self.executor_type(max_workers=2, - mp_context=self.get_context()) as executor: - self.executor = executor # Allow clean up in fail_on_deadlock - f = executor.submit(_crash, delay=.1) - executor.shutdown(wait=True) - with self.assertRaises(BrokenProcessPool): - f.result() - - def test_shutdown_deadlock_pickle(self): - # Test that the pool calling shutdown with wait=False does not cause - # a deadlock if a task fails at pickle after the shutdown call. - # Reported in bpo-39104. - self.executor.shutdown(wait=True) - with self.executor_type(max_workers=2, - mp_context=self.get_context()) as executor: - self.executor = executor # Allow clean up in fail_on_deadlock - - # Start the executor and get the executor_manager_thread to collect - # the threads and avoid dangling thread that should be cleaned up - # asynchronously. - executor.submit(id, 42).result() - executor_manager = executor._executor_manager_thread - - # Submit a task that fails at pickle and shutdown the executor - # without waiting - f = executor.submit(id, ErrorAtPickle()) - executor.shutdown(wait=False) - with self.assertRaises(PicklingError): - f.result() - - # Make sure the executor is eventually shutdown and do not leave - # dangling threads - executor_manager.join() - - def test_crash_big_data(self): - # Test that there is a clean exception instad of a deadlock when a - # child process crashes while some data is being written into the - # queue. - # https://github.com/python/cpython/issues/94777 - self.executor.shutdown(wait=True) - data = "a" * support.PIPE_MAX_SIZE - with self.executor_type(max_workers=2, - mp_context=self.get_context()) as executor: - self.executor = executor # Allow clean up in fail_on_deadlock - with self.assertRaises(BrokenProcessPool): - list(executor.map(_crash_with_data, [data] * 10)) - - -create_executor_tests(ExecutorDeadlockTest, - executor_mixins=(ProcessPoolForkMixin, - ProcessPoolForkserverMixin, - ProcessPoolSpawnMixin)) - - -class FutureTests(BaseTestCase): - def test_done_callback_with_result(self): - callback_result = None - def fn(callback_future): - nonlocal callback_result - callback_result = callback_future.result() - - f = Future() - f.add_done_callback(fn) - f.set_result(5) - self.assertEqual(5, callback_result) - - def test_done_callback_with_exception(self): - callback_exception = None - def fn(callback_future): - nonlocal callback_exception - callback_exception = callback_future.exception() - - f = Future() - f.add_done_callback(fn) - f.set_exception(Exception('test')) - self.assertEqual(('test',), callback_exception.args) - - def test_done_callback_with_cancel(self): - was_cancelled = None - def fn(callback_future): - nonlocal was_cancelled - was_cancelled = callback_future.cancelled() - - f = Future() - f.add_done_callback(fn) - self.assertTrue(f.cancel()) - self.assertTrue(was_cancelled) - - def test_done_callback_raises(self): - with support.captured_stderr() as stderr: - raising_was_called = False - fn_was_called = False - - def raising_fn(callback_future): - nonlocal raising_was_called - raising_was_called = True - raise Exception('doh!') - - def fn(callback_future): - nonlocal fn_was_called - fn_was_called = True - - f = Future() - f.add_done_callback(raising_fn) - f.add_done_callback(fn) - f.set_result(5) - self.assertTrue(raising_was_called) - self.assertTrue(fn_was_called) - self.assertIn('Exception: doh!', stderr.getvalue()) - - def test_done_callback_already_successful(self): - callback_result = None - def fn(callback_future): - nonlocal callback_result - callback_result = callback_future.result() - - f = Future() - f.set_result(5) - f.add_done_callback(fn) - self.assertEqual(5, callback_result) - - def test_done_callback_already_failed(self): - callback_exception = None - def fn(callback_future): - nonlocal callback_exception - callback_exception = callback_future.exception() - - f = Future() - f.set_exception(Exception('test')) - f.add_done_callback(fn) - self.assertEqual(('test',), callback_exception.args) - - def test_done_callback_already_cancelled(self): - was_cancelled = None - def fn(callback_future): - nonlocal was_cancelled - was_cancelled = callback_future.cancelled() - - f = Future() - self.assertTrue(f.cancel()) - f.add_done_callback(fn) - self.assertTrue(was_cancelled) - - def test_done_callback_raises_already_succeeded(self): - with support.captured_stderr() as stderr: - def raising_fn(callback_future): - raise Exception('doh!') - - f = Future() - - # Set the result first to simulate a future that runs instantly, - # effectively allowing the callback to be run immediately. - f.set_result(5) - f.add_done_callback(raising_fn) - - self.assertIn('exception calling callback for', stderr.getvalue()) - self.assertIn('doh!', stderr.getvalue()) - - - def test_repr(self): - self.assertRegex(repr(PENDING_FUTURE), - '') - self.assertRegex(repr(RUNNING_FUTURE), - '') - self.assertRegex(repr(CANCELLED_FUTURE), - '') - self.assertRegex(repr(CANCELLED_AND_NOTIFIED_FUTURE), - '') - self.assertRegex( - repr(EXCEPTION_FUTURE), - '') - self.assertRegex( - repr(SUCCESSFUL_FUTURE), - '') - - - def test_cancel(self): - f1 = create_future(state=PENDING) - f2 = create_future(state=RUNNING) - f3 = create_future(state=CANCELLED) - f4 = create_future(state=CANCELLED_AND_NOTIFIED) - f5 = create_future(state=FINISHED, exception=OSError()) - f6 = create_future(state=FINISHED, result=5) - - self.assertTrue(f1.cancel()) - self.assertEqual(f1._state, CANCELLED) - - self.assertFalse(f2.cancel()) - self.assertEqual(f2._state, RUNNING) - - self.assertTrue(f3.cancel()) - self.assertEqual(f3._state, CANCELLED) - - self.assertTrue(f4.cancel()) - self.assertEqual(f4._state, CANCELLED_AND_NOTIFIED) - - self.assertFalse(f5.cancel()) - self.assertEqual(f5._state, FINISHED) - - self.assertFalse(f6.cancel()) - self.assertEqual(f6._state, FINISHED) - - def test_cancelled(self): - self.assertFalse(PENDING_FUTURE.cancelled()) - self.assertFalse(RUNNING_FUTURE.cancelled()) - self.assertTrue(CANCELLED_FUTURE.cancelled()) - self.assertTrue(CANCELLED_AND_NOTIFIED_FUTURE.cancelled()) - self.assertFalse(EXCEPTION_FUTURE.cancelled()) - self.assertFalse(SUCCESSFUL_FUTURE.cancelled()) - - def test_done(self): - self.assertFalse(PENDING_FUTURE.done()) - self.assertFalse(RUNNING_FUTURE.done()) - self.assertTrue(CANCELLED_FUTURE.done()) - self.assertTrue(CANCELLED_AND_NOTIFIED_FUTURE.done()) - self.assertTrue(EXCEPTION_FUTURE.done()) - self.assertTrue(SUCCESSFUL_FUTURE.done()) - - def test_running(self): - self.assertFalse(PENDING_FUTURE.running()) - self.assertTrue(RUNNING_FUTURE.running()) - self.assertFalse(CANCELLED_FUTURE.running()) - self.assertFalse(CANCELLED_AND_NOTIFIED_FUTURE.running()) - self.assertFalse(EXCEPTION_FUTURE.running()) - self.assertFalse(SUCCESSFUL_FUTURE.running()) - - def test_result_with_timeout(self): - self.assertRaises(futures.TimeoutError, - PENDING_FUTURE.result, timeout=0) - self.assertRaises(futures.TimeoutError, - RUNNING_FUTURE.result, timeout=0) - self.assertRaises(futures.CancelledError, - CANCELLED_FUTURE.result, timeout=0) - self.assertRaises(futures.CancelledError, - CANCELLED_AND_NOTIFIED_FUTURE.result, timeout=0) - self.assertRaises(OSError, EXCEPTION_FUTURE.result, timeout=0) - self.assertEqual(SUCCESSFUL_FUTURE.result(timeout=0), 42) - - def test_result_with_success(self): - # TODO(brian@sweetapp.com): This test is timing dependent. - def notification(): - # Wait until the main thread is waiting for the result. - time.sleep(1) - f1.set_result(42) - - f1 = create_future(state=PENDING) - t = threading.Thread(target=notification) - t.start() - - self.assertEqual(f1.result(timeout=5), 42) - t.join() - - def test_result_with_cancel(self): - # TODO(brian@sweetapp.com): This test is timing dependent. - def notification(): - # Wait until the main thread is waiting for the result. - time.sleep(1) - f1.cancel() - - f1 = create_future(state=PENDING) - t = threading.Thread(target=notification) - t.start() - - self.assertRaises(futures.CancelledError, - f1.result, timeout=support.SHORT_TIMEOUT) - t.join() - - def test_exception_with_timeout(self): - self.assertRaises(futures.TimeoutError, - PENDING_FUTURE.exception, timeout=0) - self.assertRaises(futures.TimeoutError, - RUNNING_FUTURE.exception, timeout=0) - self.assertRaises(futures.CancelledError, - CANCELLED_FUTURE.exception, timeout=0) - self.assertRaises(futures.CancelledError, - CANCELLED_AND_NOTIFIED_FUTURE.exception, timeout=0) - self.assertTrue(isinstance(EXCEPTION_FUTURE.exception(timeout=0), - OSError)) - self.assertEqual(SUCCESSFUL_FUTURE.exception(timeout=0), None) - - def test_exception_with_success(self): - def notification(): - # Wait until the main thread is waiting for the exception. - time.sleep(1) - with f1._condition: - f1._state = FINISHED - f1._exception = OSError() - f1._condition.notify_all() - - f1 = create_future(state=PENDING) - t = threading.Thread(target=notification) - t.start() - - self.assertTrue(isinstance(f1.exception(timeout=support.SHORT_TIMEOUT), OSError)) - t.join() - - def test_multiple_set_result(self): - f = create_future(state=PENDING) - f.set_result(1) - - with self.assertRaisesRegex( - futures.InvalidStateError, - 'FINISHED: ' - ): - f.set_result(2) - - self.assertTrue(f.done()) - self.assertEqual(f.result(), 1) - - def test_multiple_set_exception(self): - f = create_future(state=PENDING) - e = ValueError() - f.set_exception(e) - - with self.assertRaisesRegex( - futures.InvalidStateError, - 'FINISHED: ' - ): - f.set_exception(Exception()) - - self.assertEqual(f.exception(), e) - - -def setUpModule(): - unittest.addModuleCleanup(multiprocessing.util._cleanup_tests) - thread_info = threading_helper.threading_setup() - unittest.addModuleCleanup(threading_helper.threading_cleanup, *thread_info) - - -if __name__ == "__main__": - unittest.main() diff --git a/Lib/test/test_concurrent_futures/__init__.py b/Lib/test/test_concurrent_futures/__init__.py new file mode 100644 index 00000000000000..430fa93aa456a2 --- /dev/null +++ b/Lib/test/test_concurrent_futures/__init__.py @@ -0,0 +1,16 @@ +import os.path +import unittest +from test import support +from test.support import import_helper + +# Skip tests if _multiprocessing wasn't built. +import_helper.import_module('_multiprocessing') + +if support.check_sanitizer(address=True, memory=True): + # gh-90791: Skip the test because it is too slow when Python is built + # with ASAN/MSAN: between 5 and 20 minutes on GitHub Actions. + raise unittest.SkipTest("test too slow on ASAN/MSAN build") + + +def load_tests(*args): + return support.load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/test_concurrent_futures/executor.py b/Lib/test/test_concurrent_futures/executor.py index 1e7d4344740943..36278bdd501971 100644 --- a/Lib/test/test_concurrent_futures/executor.py +++ b/Lib/test/test_concurrent_futures/executor.py @@ -53,7 +53,6 @@ def test_map_exception(self): self.assertEqual(i.__next__(), (0, 1)) self.assertRaises(ZeroDivisionError, i.__next__) - @support.requires_resource('walltime') def test_map_timeout(self): results = [] try: diff --git a/Lib/test/test_concurrent_futures/test_as_completed.py b/Lib/test/test_concurrent_futures/test_as_completed.py new file mode 100644 index 00000000000000..de53349d4fd577 --- /dev/null +++ b/Lib/test/test_concurrent_futures/test_as_completed.py @@ -0,0 +1,111 @@ +import itertools +import time +import unittest +import weakref +from concurrent import futures +from concurrent.futures._base import ( + CANCELLED_AND_NOTIFIED, FINISHED, Future) + +from test import support + +from .util import ( + PENDING_FUTURE, RUNNING_FUTURE, + CANCELLED_AND_NOTIFIED_FUTURE, EXCEPTION_FUTURE, SUCCESSFUL_FUTURE, + create_future, create_executor_tests, setup_module) + + +def mul(x, y): + return x * y + + +class AsCompletedTests: + # TODO(brian@sweetapp.com): Should have a test with a non-zero timeout. + def test_no_timeout(self): + future1 = self.executor.submit(mul, 2, 21) + future2 = self.executor.submit(mul, 7, 6) + + completed = set(futures.as_completed( + [CANCELLED_AND_NOTIFIED_FUTURE, + EXCEPTION_FUTURE, + SUCCESSFUL_FUTURE, + future1, future2])) + self.assertEqual(set( + [CANCELLED_AND_NOTIFIED_FUTURE, + EXCEPTION_FUTURE, + SUCCESSFUL_FUTURE, + future1, future2]), + completed) + + def test_zero_timeout(self): + future1 = self.executor.submit(time.sleep, 2) + completed_futures = set() + try: + for future in futures.as_completed( + [CANCELLED_AND_NOTIFIED_FUTURE, + EXCEPTION_FUTURE, + SUCCESSFUL_FUTURE, + future1], + timeout=0): + completed_futures.add(future) + except futures.TimeoutError: + pass + + self.assertEqual(set([CANCELLED_AND_NOTIFIED_FUTURE, + EXCEPTION_FUTURE, + SUCCESSFUL_FUTURE]), + completed_futures) + + def test_duplicate_futures(self): + # Issue 20367. Duplicate futures should not raise exceptions or give + # duplicate responses. + # Issue #31641: accept arbitrary iterables. + future1 = self.executor.submit(time.sleep, 2) + completed = [ + f for f in futures.as_completed(itertools.repeat(future1, 3)) + ] + self.assertEqual(len(completed), 1) + + def test_free_reference_yielded_future(self): + # Issue #14406: Generator should not keep references + # to finished futures. + futures_list = [Future() for _ in range(8)] + futures_list.append(create_future(state=CANCELLED_AND_NOTIFIED)) + futures_list.append(create_future(state=FINISHED, result=42)) + + with self.assertRaises(futures.TimeoutError): + for future in futures.as_completed(futures_list, timeout=0): + futures_list.remove(future) + wr = weakref.ref(future) + del future + support.gc_collect() # For PyPy or other GCs. + self.assertIsNone(wr()) + + futures_list[0].set_result("test") + for future in futures.as_completed(futures_list): + futures_list.remove(future) + wr = weakref.ref(future) + del future + support.gc_collect() # For PyPy or other GCs. + self.assertIsNone(wr()) + if futures_list: + futures_list[0].set_result("test") + + def test_correct_timeout_exception_msg(self): + futures_list = [CANCELLED_AND_NOTIFIED_FUTURE, PENDING_FUTURE, + RUNNING_FUTURE, SUCCESSFUL_FUTURE] + + with self.assertRaises(futures.TimeoutError) as cm: + list(futures.as_completed(futures_list, timeout=0)) + + self.assertEqual(str(cm.exception), '2 (of 4) futures unfinished') + + +create_executor_tests(globals(), AsCompletedTests) + + +def setUpModule(): + setup_module() + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_concurrent_futures/test_deadlock.py b/Lib/test/test_concurrent_futures/test_deadlock.py new file mode 100644 index 00000000000000..6b78b360d15627 --- /dev/null +++ b/Lib/test/test_concurrent_futures/test_deadlock.py @@ -0,0 +1,253 @@ +import contextlib +import sys +import time +import unittest +from pickle import PicklingError +from concurrent import futures +from concurrent.futures.process import BrokenProcessPool + +from test import support + +from .util import ( + create_executor_tests, setup_module, + ProcessPoolForkMixin, ProcessPoolForkserverMixin, ProcessPoolSpawnMixin) + + +def _crash(delay=None): + """Induces a segfault.""" + if delay: + time.sleep(delay) + import faulthandler + faulthandler.disable() + faulthandler._sigsegv() + + +def _crash_with_data(data): + """Induces a segfault with dummy data in input.""" + _crash() + + +def _exit(): + """Induces a sys exit with exitcode 1.""" + sys.exit(1) + + +def _raise_error(Err): + """Function that raises an Exception in process.""" + raise Err() + + +def _raise_error_ignore_stderr(Err): + """Function that raises an Exception in process and ignores stderr.""" + import io + sys.stderr = io.StringIO() + raise Err() + + +def _return_instance(cls): + """Function that returns a instance of cls.""" + return cls() + + +class CrashAtPickle(object): + """Bad object that triggers a segfault at pickling time.""" + def __reduce__(self): + _crash() + + +class CrashAtUnpickle(object): + """Bad object that triggers a segfault at unpickling time.""" + def __reduce__(self): + return _crash, () + + +class ExitAtPickle(object): + """Bad object that triggers a process exit at pickling time.""" + def __reduce__(self): + _exit() + + +class ExitAtUnpickle(object): + """Bad object that triggers a process exit at unpickling time.""" + def __reduce__(self): + return _exit, () + + +class ErrorAtPickle(object): + """Bad object that triggers an error at pickling time.""" + def __reduce__(self): + from pickle import PicklingError + raise PicklingError("Error in pickle") + + +class ErrorAtUnpickle(object): + """Bad object that triggers an error at unpickling time.""" + def __reduce__(self): + from pickle import UnpicklingError + return _raise_error_ignore_stderr, (UnpicklingError, ) + + +class ExecutorDeadlockTest: + TIMEOUT = support.SHORT_TIMEOUT + + def _fail_on_deadlock(self, executor): + # If we did not recover before TIMEOUT seconds, consider that the + # executor is in a deadlock state and forcefully clean all its + # composants. + import faulthandler + from tempfile import TemporaryFile + with TemporaryFile(mode="w+") as f: + faulthandler.dump_traceback(file=f) + f.seek(0) + tb = f.read() + for p in executor._processes.values(): + p.terminate() + # This should be safe to call executor.shutdown here as all possible + # deadlocks should have been broken. + executor.shutdown(wait=True) + print(f"\nTraceback:\n {tb}", file=sys.__stderr__) + self.fail(f"Executor deadlock:\n\n{tb}") + + + def _check_crash(self, error, func, *args, ignore_stderr=False): + # test for deadlock caused by crashes in a pool + self.executor.shutdown(wait=True) + + executor = self.executor_type( + max_workers=2, mp_context=self.get_context()) + res = executor.submit(func, *args) + + if ignore_stderr: + cm = support.captured_stderr() + else: + cm = contextlib.nullcontext() + + try: + with self.assertRaises(error): + with cm: + res.result(timeout=self.TIMEOUT) + except futures.TimeoutError: + # If we did not recover before TIMEOUT seconds, + # consider that the executor is in a deadlock state + self._fail_on_deadlock(executor) + executor.shutdown(wait=True) + + def test_error_at_task_pickle(self): + # Check problem occurring while pickling a task in + # the task_handler thread + self._check_crash(PicklingError, id, ErrorAtPickle()) + + def test_exit_at_task_unpickle(self): + # Check problem occurring while unpickling a task on workers + self._check_crash(BrokenProcessPool, id, ExitAtUnpickle()) + + def test_error_at_task_unpickle(self): + # Check problem occurring while unpickling a task on workers + self._check_crash(BrokenProcessPool, id, ErrorAtUnpickle()) + + def test_crash_at_task_unpickle(self): + # Check problem occurring while unpickling a task on workers + self._check_crash(BrokenProcessPool, id, CrashAtUnpickle()) + + def test_crash_during_func_exec_on_worker(self): + # Check problem occurring during func execution on workers + self._check_crash(BrokenProcessPool, _crash) + + def test_exit_during_func_exec_on_worker(self): + # Check problem occurring during func execution on workers + self._check_crash(SystemExit, _exit) + + def test_error_during_func_exec_on_worker(self): + # Check problem occurring during func execution on workers + self._check_crash(RuntimeError, _raise_error, RuntimeError) + + def test_crash_during_result_pickle_on_worker(self): + # Check problem occurring while pickling a task result + # on workers + self._check_crash(BrokenProcessPool, _return_instance, CrashAtPickle) + + def test_exit_during_result_pickle_on_worker(self): + # Check problem occurring while pickling a task result + # on workers + self._check_crash(SystemExit, _return_instance, ExitAtPickle) + + def test_error_during_result_pickle_on_worker(self): + # Check problem occurring while pickling a task result + # on workers + self._check_crash(PicklingError, _return_instance, ErrorAtPickle) + + def test_error_during_result_unpickle_in_result_handler(self): + # Check problem occurring while unpickling a task in + # the result_handler thread + self._check_crash(BrokenProcessPool, + _return_instance, ErrorAtUnpickle, + ignore_stderr=True) + + def test_exit_during_result_unpickle_in_result_handler(self): + # Check problem occurring while unpickling a task in + # the result_handler thread + self._check_crash(BrokenProcessPool, _return_instance, ExitAtUnpickle) + + def test_shutdown_deadlock(self): + # Test that the pool calling shutdown do not cause deadlock + # if a worker fails after the shutdown call. + self.executor.shutdown(wait=True) + with self.executor_type(max_workers=2, + mp_context=self.get_context()) as executor: + self.executor = executor # Allow clean up in fail_on_deadlock + f = executor.submit(_crash, delay=.1) + executor.shutdown(wait=True) + with self.assertRaises(BrokenProcessPool): + f.result() + + def test_shutdown_deadlock_pickle(self): + # Test that the pool calling shutdown with wait=False does not cause + # a deadlock if a task fails at pickle after the shutdown call. + # Reported in bpo-39104. + self.executor.shutdown(wait=True) + with self.executor_type(max_workers=2, + mp_context=self.get_context()) as executor: + self.executor = executor # Allow clean up in fail_on_deadlock + + # Start the executor and get the executor_manager_thread to collect + # the threads and avoid dangling thread that should be cleaned up + # asynchronously. + executor.submit(id, 42).result() + executor_manager = executor._executor_manager_thread + + # Submit a task that fails at pickle and shutdown the executor + # without waiting + f = executor.submit(id, ErrorAtPickle()) + executor.shutdown(wait=False) + with self.assertRaises(PicklingError): + f.result() + + # Make sure the executor is eventually shutdown and do not leave + # dangling threads + executor_manager.join() + + def test_crash_big_data(self): + # Test that there is a clean exception instad of a deadlock when a + # child process crashes while some data is being written into the + # queue. + # https://github.com/python/cpython/issues/94777 + self.executor.shutdown(wait=True) + data = "a" * support.PIPE_MAX_SIZE + with self.executor_type(max_workers=2, + mp_context=self.get_context()) as executor: + self.executor = executor # Allow clean up in fail_on_deadlock + with self.assertRaises(BrokenProcessPool): + list(executor.map(_crash_with_data, [data] * 10)) + + +create_executor_tests(globals(), ExecutorDeadlockTest, + executor_mixins=(ProcessPoolForkMixin, + ProcessPoolForkserverMixin, + ProcessPoolSpawnMixin)) + +def setUpModule(): + setup_module() + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_concurrent_futures/test_future.py b/Lib/test/test_concurrent_futures/test_future.py new file mode 100644 index 00000000000000..4066ea1ee4b367 --- /dev/null +++ b/Lib/test/test_concurrent_futures/test_future.py @@ -0,0 +1,291 @@ +import threading +import time +import unittest +from concurrent import futures +from concurrent.futures._base import ( + PENDING, RUNNING, CANCELLED, CANCELLED_AND_NOTIFIED, FINISHED, Future) + +from test import support + +from .util import ( + PENDING_FUTURE, RUNNING_FUTURE, CANCELLED_FUTURE, + CANCELLED_AND_NOTIFIED_FUTURE, EXCEPTION_FUTURE, SUCCESSFUL_FUTURE, + BaseTestCase, create_future, setup_module) + + +class FutureTests(BaseTestCase): + def test_done_callback_with_result(self): + callback_result = None + def fn(callback_future): + nonlocal callback_result + callback_result = callback_future.result() + + f = Future() + f.add_done_callback(fn) + f.set_result(5) + self.assertEqual(5, callback_result) + + def test_done_callback_with_exception(self): + callback_exception = None + def fn(callback_future): + nonlocal callback_exception + callback_exception = callback_future.exception() + + f = Future() + f.add_done_callback(fn) + f.set_exception(Exception('test')) + self.assertEqual(('test',), callback_exception.args) + + def test_done_callback_with_cancel(self): + was_cancelled = None + def fn(callback_future): + nonlocal was_cancelled + was_cancelled = callback_future.cancelled() + + f = Future() + f.add_done_callback(fn) + self.assertTrue(f.cancel()) + self.assertTrue(was_cancelled) + + def test_done_callback_raises(self): + with support.captured_stderr() as stderr: + raising_was_called = False + fn_was_called = False + + def raising_fn(callback_future): + nonlocal raising_was_called + raising_was_called = True + raise Exception('doh!') + + def fn(callback_future): + nonlocal fn_was_called + fn_was_called = True + + f = Future() + f.add_done_callback(raising_fn) + f.add_done_callback(fn) + f.set_result(5) + self.assertTrue(raising_was_called) + self.assertTrue(fn_was_called) + self.assertIn('Exception: doh!', stderr.getvalue()) + + def test_done_callback_already_successful(self): + callback_result = None + def fn(callback_future): + nonlocal callback_result + callback_result = callback_future.result() + + f = Future() + f.set_result(5) + f.add_done_callback(fn) + self.assertEqual(5, callback_result) + + def test_done_callback_already_failed(self): + callback_exception = None + def fn(callback_future): + nonlocal callback_exception + callback_exception = callback_future.exception() + + f = Future() + f.set_exception(Exception('test')) + f.add_done_callback(fn) + self.assertEqual(('test',), callback_exception.args) + + def test_done_callback_already_cancelled(self): + was_cancelled = None + def fn(callback_future): + nonlocal was_cancelled + was_cancelled = callback_future.cancelled() + + f = Future() + self.assertTrue(f.cancel()) + f.add_done_callback(fn) + self.assertTrue(was_cancelled) + + def test_done_callback_raises_already_succeeded(self): + with support.captured_stderr() as stderr: + def raising_fn(callback_future): + raise Exception('doh!') + + f = Future() + + # Set the result first to simulate a future that runs instantly, + # effectively allowing the callback to be run immediately. + f.set_result(5) + f.add_done_callback(raising_fn) + + self.assertIn('exception calling callback for', stderr.getvalue()) + self.assertIn('doh!', stderr.getvalue()) + + + def test_repr(self): + self.assertRegex(repr(PENDING_FUTURE), + '') + self.assertRegex(repr(RUNNING_FUTURE), + '') + self.assertRegex(repr(CANCELLED_FUTURE), + '') + self.assertRegex(repr(CANCELLED_AND_NOTIFIED_FUTURE), + '') + self.assertRegex( + repr(EXCEPTION_FUTURE), + '') + self.assertRegex( + repr(SUCCESSFUL_FUTURE), + '') + + def test_cancel(self): + f1 = create_future(state=PENDING) + f2 = create_future(state=RUNNING) + f3 = create_future(state=CANCELLED) + f4 = create_future(state=CANCELLED_AND_NOTIFIED) + f5 = create_future(state=FINISHED, exception=OSError()) + f6 = create_future(state=FINISHED, result=5) + + self.assertTrue(f1.cancel()) + self.assertEqual(f1._state, CANCELLED) + + self.assertFalse(f2.cancel()) + self.assertEqual(f2._state, RUNNING) + + self.assertTrue(f3.cancel()) + self.assertEqual(f3._state, CANCELLED) + + self.assertTrue(f4.cancel()) + self.assertEqual(f4._state, CANCELLED_AND_NOTIFIED) + + self.assertFalse(f5.cancel()) + self.assertEqual(f5._state, FINISHED) + + self.assertFalse(f6.cancel()) + self.assertEqual(f6._state, FINISHED) + + def test_cancelled(self): + self.assertFalse(PENDING_FUTURE.cancelled()) + self.assertFalse(RUNNING_FUTURE.cancelled()) + self.assertTrue(CANCELLED_FUTURE.cancelled()) + self.assertTrue(CANCELLED_AND_NOTIFIED_FUTURE.cancelled()) + self.assertFalse(EXCEPTION_FUTURE.cancelled()) + self.assertFalse(SUCCESSFUL_FUTURE.cancelled()) + + def test_done(self): + self.assertFalse(PENDING_FUTURE.done()) + self.assertFalse(RUNNING_FUTURE.done()) + self.assertTrue(CANCELLED_FUTURE.done()) + self.assertTrue(CANCELLED_AND_NOTIFIED_FUTURE.done()) + self.assertTrue(EXCEPTION_FUTURE.done()) + self.assertTrue(SUCCESSFUL_FUTURE.done()) + + def test_running(self): + self.assertFalse(PENDING_FUTURE.running()) + self.assertTrue(RUNNING_FUTURE.running()) + self.assertFalse(CANCELLED_FUTURE.running()) + self.assertFalse(CANCELLED_AND_NOTIFIED_FUTURE.running()) + self.assertFalse(EXCEPTION_FUTURE.running()) + self.assertFalse(SUCCESSFUL_FUTURE.running()) + + def test_result_with_timeout(self): + self.assertRaises(futures.TimeoutError, + PENDING_FUTURE.result, timeout=0) + self.assertRaises(futures.TimeoutError, + RUNNING_FUTURE.result, timeout=0) + self.assertRaises(futures.CancelledError, + CANCELLED_FUTURE.result, timeout=0) + self.assertRaises(futures.CancelledError, + CANCELLED_AND_NOTIFIED_FUTURE.result, timeout=0) + self.assertRaises(OSError, EXCEPTION_FUTURE.result, timeout=0) + self.assertEqual(SUCCESSFUL_FUTURE.result(timeout=0), 42) + + def test_result_with_success(self): + # TODO(brian@sweetapp.com): This test is timing dependent. + def notification(): + # Wait until the main thread is waiting for the result. + time.sleep(1) + f1.set_result(42) + + f1 = create_future(state=PENDING) + t = threading.Thread(target=notification) + t.start() + + self.assertEqual(f1.result(timeout=5), 42) + t.join() + + def test_result_with_cancel(self): + # TODO(brian@sweetapp.com): This test is timing dependent. + def notification(): + # Wait until the main thread is waiting for the result. + time.sleep(1) + f1.cancel() + + f1 = create_future(state=PENDING) + t = threading.Thread(target=notification) + t.start() + + self.assertRaises(futures.CancelledError, + f1.result, timeout=support.SHORT_TIMEOUT) + t.join() + + def test_exception_with_timeout(self): + self.assertRaises(futures.TimeoutError, + PENDING_FUTURE.exception, timeout=0) + self.assertRaises(futures.TimeoutError, + RUNNING_FUTURE.exception, timeout=0) + self.assertRaises(futures.CancelledError, + CANCELLED_FUTURE.exception, timeout=0) + self.assertRaises(futures.CancelledError, + CANCELLED_AND_NOTIFIED_FUTURE.exception, timeout=0) + self.assertTrue(isinstance(EXCEPTION_FUTURE.exception(timeout=0), + OSError)) + self.assertEqual(SUCCESSFUL_FUTURE.exception(timeout=0), None) + + def test_exception_with_success(self): + def notification(): + # Wait until the main thread is waiting for the exception. + time.sleep(1) + with f1._condition: + f1._state = FINISHED + f1._exception = OSError() + f1._condition.notify_all() + + f1 = create_future(state=PENDING) + t = threading.Thread(target=notification) + t.start() + + self.assertTrue(isinstance(f1.exception(timeout=support.SHORT_TIMEOUT), OSError)) + t.join() + + def test_multiple_set_result(self): + f = create_future(state=PENDING) + f.set_result(1) + + with self.assertRaisesRegex( + futures.InvalidStateError, + 'FINISHED: ' + ): + f.set_result(2) + + self.assertTrue(f.done()) + self.assertEqual(f.result(), 1) + + def test_multiple_set_exception(self): + f = create_future(state=PENDING) + e = ValueError() + f.set_exception(e) + + with self.assertRaisesRegex( + futures.InvalidStateError, + 'FINISHED: ' + ): + f.set_exception(Exception()) + + self.assertEqual(f.exception(), e) + + +def setUpModule(): + setup_module() + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_concurrent_futures/test_init.py b/Lib/test/test_concurrent_futures/test_init.py new file mode 100644 index 00000000000000..138d6ee545fd92 --- /dev/null +++ b/Lib/test/test_concurrent_futures/test_init.py @@ -0,0 +1,117 @@ +import contextlib +import logging +import queue +import time +import unittest +from concurrent.futures._base import BrokenExecutor +from logging.handlers import QueueHandler + +from test import support + +from .util import ExecutorMixin, create_executor_tests, setup_module + + +INITIALIZER_STATUS = 'uninitialized' + +def init(x): + global INITIALIZER_STATUS + INITIALIZER_STATUS = x + +def get_init_status(): + return INITIALIZER_STATUS + +def init_fail(log_queue=None): + if log_queue is not None: + logger = logging.getLogger('concurrent.futures') + logger.addHandler(QueueHandler(log_queue)) + logger.setLevel('CRITICAL') + logger.propagate = False + time.sleep(0.1) # let some futures be scheduled + raise ValueError('error in initializer') + + +class InitializerMixin(ExecutorMixin): + worker_count = 2 + + def setUp(self): + global INITIALIZER_STATUS + INITIALIZER_STATUS = 'uninitialized' + self.executor_kwargs = dict(initializer=init, + initargs=('initialized',)) + super().setUp() + + def test_initializer(self): + futures = [self.executor.submit(get_init_status) + for _ in range(self.worker_count)] + + for f in futures: + self.assertEqual(f.result(), 'initialized') + + +class FailingInitializerMixin(ExecutorMixin): + worker_count = 2 + + def setUp(self): + if hasattr(self, "ctx"): + # Pass a queue to redirect the child's logging output + self.mp_context = self.get_context() + self.log_queue = self.mp_context.Queue() + self.executor_kwargs = dict(initializer=init_fail, + initargs=(self.log_queue,)) + else: + # In a thread pool, the child shares our logging setup + # (see _assert_logged()) + self.mp_context = None + self.log_queue = None + self.executor_kwargs = dict(initializer=init_fail) + super().setUp() + + def test_initializer(self): + with self._assert_logged('ValueError: error in initializer'): + try: + future = self.executor.submit(get_init_status) + except BrokenExecutor: + # Perhaps the executor is already broken + pass + else: + with self.assertRaises(BrokenExecutor): + future.result() + + # At some point, the executor should break + t1 = time.monotonic() + while not self.executor._broken: + if time.monotonic() - t1 > 5: + self.fail("executor not broken after 5 s.") + time.sleep(0.01) + # ... and from this point submit() is guaranteed to fail + with self.assertRaises(BrokenExecutor): + self.executor.submit(get_init_status) + + @contextlib.contextmanager + def _assert_logged(self, msg): + if self.log_queue is not None: + yield + output = [] + try: + while True: + output.append(self.log_queue.get_nowait().getMessage()) + except queue.Empty: + pass + else: + with self.assertLogs('concurrent.futures', 'CRITICAL') as cm: + yield + output = cm.output + self.assertTrue(any(msg in line for line in output), + output) + + +create_executor_tests(globals(), InitializerMixin) +create_executor_tests(globals(), FailingInitializerMixin) + + +def setUpModule(): + setup_module() + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_concurrent_futures/test_process_pool.py b/Lib/test/test_concurrent_futures/test_process_pool.py new file mode 100644 index 00000000000000..7763a4946f110c --- /dev/null +++ b/Lib/test/test_concurrent_futures/test_process_pool.py @@ -0,0 +1,202 @@ +import os +import sys +import time +import unittest +from concurrent import futures +from concurrent.futures.process import BrokenProcessPool + +from test import support +from test.support import hashlib_helper + +from .executor import ExecutorTest, mul +from .util import ( + ProcessPoolForkMixin, ProcessPoolForkserverMixin, ProcessPoolSpawnMixin, + create_executor_tests, setup_module) + + +class EventfulGCObj(): + def __init__(self, mgr): + self.event = mgr.Event() + + def __del__(self): + self.event.set() + + +class ProcessPoolExecutorTest(ExecutorTest): + + @unittest.skipUnless(sys.platform=='win32', 'Windows-only process limit') + def test_max_workers_too_large(self): + with self.assertRaisesRegex(ValueError, + "max_workers must be <= 61"): + futures.ProcessPoolExecutor(max_workers=62) + + def test_killed_child(self): + # When a child process is abruptly terminated, the whole pool gets + # "broken". + futures = [self.executor.submit(time.sleep, 3)] + # Get one of the processes, and terminate (kill) it + p = next(iter(self.executor._processes.values())) + p.terminate() + for fut in futures: + self.assertRaises(BrokenProcessPool, fut.result) + # Submitting other jobs fails as well. + self.assertRaises(BrokenProcessPool, self.executor.submit, pow, 2, 8) + + def test_map_chunksize(self): + def bad_map(): + list(self.executor.map(pow, range(40), range(40), chunksize=-1)) + + ref = list(map(pow, range(40), range(40))) + self.assertEqual( + list(self.executor.map(pow, range(40), range(40), chunksize=6)), + ref) + self.assertEqual( + list(self.executor.map(pow, range(40), range(40), chunksize=50)), + ref) + self.assertEqual( + list(self.executor.map(pow, range(40), range(40), chunksize=40)), + ref) + self.assertRaises(ValueError, bad_map) + + @classmethod + def _test_traceback(cls): + raise RuntimeError(123) # some comment + + def test_traceback(self): + # We want ensure that the traceback from the child process is + # contained in the traceback raised in the main process. + future = self.executor.submit(self._test_traceback) + with self.assertRaises(Exception) as cm: + future.result() + + exc = cm.exception + self.assertIs(type(exc), RuntimeError) + self.assertEqual(exc.args, (123,)) + cause = exc.__cause__ + self.assertIs(type(cause), futures.process._RemoteTraceback) + self.assertIn('raise RuntimeError(123) # some comment', cause.tb) + + with support.captured_stderr() as f1: + try: + raise exc + except RuntimeError: + sys.excepthook(*sys.exc_info()) + self.assertIn('raise RuntimeError(123) # some comment', + f1.getvalue()) + + @hashlib_helper.requires_hashdigest('md5') + def test_ressources_gced_in_workers(self): + # Ensure that argument for a job are correctly gc-ed after the job + # is finished + mgr = self.get_context().Manager() + obj = EventfulGCObj(mgr) + future = self.executor.submit(id, obj) + future.result() + + self.assertTrue(obj.event.wait(timeout=1)) + + # explicitly destroy the object to ensure that EventfulGCObj.__del__() + # is called while manager is still running. + obj = None + support.gc_collect() + + mgr.shutdown() + mgr.join() + + def test_saturation(self): + executor = self.executor + mp_context = self.get_context() + sem = mp_context.Semaphore(0) + job_count = 15 * executor._max_workers + for _ in range(job_count): + executor.submit(sem.acquire) + self.assertEqual(len(executor._processes), executor._max_workers) + for _ in range(job_count): + sem.release() + + def test_idle_process_reuse_one(self): + executor = self.executor + assert executor._max_workers >= 4 + if self.get_context().get_start_method(allow_none=False) == "fork": + raise unittest.SkipTest("Incompatible with the fork start method.") + executor.submit(mul, 21, 2).result() + executor.submit(mul, 6, 7).result() + executor.submit(mul, 3, 14).result() + self.assertEqual(len(executor._processes), 1) + + def test_idle_process_reuse_multiple(self): + executor = self.executor + assert executor._max_workers <= 5 + if self.get_context().get_start_method(allow_none=False) == "fork": + raise unittest.SkipTest("Incompatible with the fork start method.") + executor.submit(mul, 12, 7).result() + executor.submit(mul, 33, 25) + executor.submit(mul, 25, 26).result() + executor.submit(mul, 18, 29) + executor.submit(mul, 1, 2).result() + executor.submit(mul, 0, 9) + self.assertLessEqual(len(executor._processes), 3) + executor.shutdown() + + def test_max_tasks_per_child(self): + context = self.get_context() + if context.get_start_method(allow_none=False) == "fork": + with self.assertRaises(ValueError): + self.executor_type(1, mp_context=context, max_tasks_per_child=3) + return + # not using self.executor as we need to control construction. + # arguably this could go in another class w/o that mixin. + executor = self.executor_type( + 1, mp_context=context, max_tasks_per_child=3) + f1 = executor.submit(os.getpid) + original_pid = f1.result() + # The worker pid remains the same as the worker could be reused + f2 = executor.submit(os.getpid) + self.assertEqual(f2.result(), original_pid) + self.assertEqual(len(executor._processes), 1) + f3 = executor.submit(os.getpid) + self.assertEqual(f3.result(), original_pid) + + # A new worker is spawned, with a statistically different pid, + # while the previous was reaped. + f4 = executor.submit(os.getpid) + new_pid = f4.result() + self.assertNotEqual(original_pid, new_pid) + self.assertEqual(len(executor._processes), 1) + + executor.shutdown() + + def test_max_tasks_per_child_defaults_to_spawn_context(self): + # not using self.executor as we need to control construction. + # arguably this could go in another class w/o that mixin. + executor = self.executor_type(1, max_tasks_per_child=3) + self.assertEqual(executor._mp_context.get_start_method(), "spawn") + + def test_max_tasks_early_shutdown(self): + context = self.get_context() + if context.get_start_method(allow_none=False) == "fork": + raise unittest.SkipTest("Incompatible with the fork start method.") + # not using self.executor as we need to control construction. + # arguably this could go in another class w/o that mixin. + executor = self.executor_type( + 3, mp_context=context, max_tasks_per_child=1) + futures = [] + for i in range(6): + futures.append(executor.submit(mul, i, i)) + executor.shutdown() + for i, future in enumerate(futures): + self.assertEqual(future.result(), mul(i, i)) + + +create_executor_tests(globals(), ProcessPoolExecutorTest, + executor_mixins=(ProcessPoolForkMixin, + ProcessPoolForkserverMixin, + ProcessPoolSpawnMixin)) + + +def setUpModule(): + setup_module() + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_concurrent_futures/test_shutdown.py b/Lib/test/test_concurrent_futures/test_shutdown.py new file mode 100644 index 00000000000000..45dab7a75fdd50 --- /dev/null +++ b/Lib/test/test_concurrent_futures/test_shutdown.py @@ -0,0 +1,343 @@ +import signal +import sys +import threading +import time +import unittest +from concurrent import futures + +from test import support +from test.support.script_helper import assert_python_ok + +from .util import ( + BaseTestCase, ThreadPoolMixin, ProcessPoolForkMixin, + ProcessPoolForkserverMixin, ProcessPoolSpawnMixin, + create_executor_tests, setup_module) + + +def sleep_and_print(t, msg): + time.sleep(t) + print(msg) + sys.stdout.flush() + + +class ExecutorShutdownTest: + def test_run_after_shutdown(self): + self.executor.shutdown() + self.assertRaises(RuntimeError, + self.executor.submit, + pow, 2, 5) + + def test_interpreter_shutdown(self): + # Test the atexit hook for shutdown of worker threads and processes + rc, out, err = assert_python_ok('-c', """if 1: + from concurrent.futures import {executor_type} + from time import sleep + from test.test_concurrent_futures.test_shutdown import sleep_and_print + if __name__ == "__main__": + context = '{context}' + if context == "": + t = {executor_type}(5) + else: + from multiprocessing import get_context + context = get_context(context) + t = {executor_type}(5, mp_context=context) + t.submit(sleep_and_print, 1.0, "apple") + """.format(executor_type=self.executor_type.__name__, + context=getattr(self, "ctx", ""))) + # Errors in atexit hooks don't change the process exit code, check + # stderr manually. + self.assertFalse(err) + self.assertEqual(out.strip(), b"apple") + + def test_submit_after_interpreter_shutdown(self): + # Test the atexit hook for shutdown of worker threads and processes + rc, out, err = assert_python_ok('-c', """if 1: + import atexit + @atexit.register + def run_last(): + try: + t.submit(id, None) + except RuntimeError: + print("runtime-error") + raise + from concurrent.futures import {executor_type} + if __name__ == "__main__": + context = '{context}' + if not context: + t = {executor_type}(5) + else: + from multiprocessing import get_context + context = get_context(context) + t = {executor_type}(5, mp_context=context) + t.submit(id, 42).result() + """.format(executor_type=self.executor_type.__name__, + context=getattr(self, "ctx", ""))) + # Errors in atexit hooks don't change the process exit code, check + # stderr manually. + self.assertIn("RuntimeError: cannot schedule new futures", err.decode()) + self.assertEqual(out.strip(), b"runtime-error") + + def test_hang_issue12364(self): + fs = [self.executor.submit(time.sleep, 0.1) for _ in range(50)] + self.executor.shutdown() + for f in fs: + f.result() + + def test_cancel_futures(self): + assert self.worker_count <= 5, "test needs few workers" + fs = [self.executor.submit(time.sleep, .1) for _ in range(50)] + self.executor.shutdown(cancel_futures=True) + # We can't guarantee the exact number of cancellations, but we can + # guarantee that *some* were cancelled. With few workers, many of + # the submitted futures should have been cancelled. + cancelled = [fut for fut in fs if fut.cancelled()] + self.assertGreater(len(cancelled), 20) + + # Ensure the other futures were able to finish. + # Use "not fut.cancelled()" instead of "fut.done()" to include futures + # that may have been left in a pending state. + others = [fut for fut in fs if not fut.cancelled()] + for fut in others: + self.assertTrue(fut.done(), msg=f"{fut._state=}") + self.assertIsNone(fut.exception()) + + # Similar to the number of cancelled futures, we can't guarantee the + # exact number that completed. But, we can guarantee that at least + # one finished. + self.assertGreater(len(others), 0) + + def test_hang_gh83386(self): + """shutdown(wait=False) doesn't hang at exit with running futures. + + See https://github.com/python/cpython/issues/83386. + """ + if self.executor_type == futures.ProcessPoolExecutor: + raise unittest.SkipTest( + "Hangs, see https://github.com/python/cpython/issues/83386") + + rc, out, err = assert_python_ok('-c', """if True: + from concurrent.futures import {executor_type} + from test.test_concurrent_futures.test_shutdown import sleep_and_print + if __name__ == "__main__": + if {context!r}: multiprocessing.set_start_method({context!r}) + t = {executor_type}(max_workers=3) + t.submit(sleep_and_print, 1.0, "apple") + t.shutdown(wait=False) + """.format(executor_type=self.executor_type.__name__, + context=getattr(self, 'ctx', None))) + self.assertFalse(err) + self.assertEqual(out.strip(), b"apple") + + def test_hang_gh94440(self): + """shutdown(wait=True) doesn't hang when a future was submitted and + quickly canceled right before shutdown. + + See https://github.com/python/cpython/issues/94440. + """ + if not hasattr(signal, 'alarm'): + raise unittest.SkipTest( + "Tested platform does not support the alarm signal") + + def timeout(_signum, _frame): + raise RuntimeError("timed out waiting for shutdown") + + kwargs = {} + if getattr(self, 'ctx', None): + kwargs['mp_context'] = self.get_context() + executor = self.executor_type(max_workers=1, **kwargs) + executor.submit(int).result() + old_handler = signal.signal(signal.SIGALRM, timeout) + try: + signal.alarm(5) + executor.submit(int).cancel() + executor.shutdown(wait=True) + finally: + signal.alarm(0) + signal.signal(signal.SIGALRM, old_handler) + + +class ThreadPoolShutdownTest(ThreadPoolMixin, ExecutorShutdownTest, BaseTestCase): + def test_threads_terminate(self): + def acquire_lock(lock): + lock.acquire() + + sem = threading.Semaphore(0) + for i in range(3): + self.executor.submit(acquire_lock, sem) + self.assertEqual(len(self.executor._threads), 3) + for i in range(3): + sem.release() + self.executor.shutdown() + for t in self.executor._threads: + t.join() + + def test_context_manager_shutdown(self): + with futures.ThreadPoolExecutor(max_workers=5) as e: + executor = e + self.assertEqual(list(e.map(abs, range(-5, 5))), + [5, 4, 3, 2, 1, 0, 1, 2, 3, 4]) + + for t in executor._threads: + t.join() + + def test_del_shutdown(self): + executor = futures.ThreadPoolExecutor(max_workers=5) + res = executor.map(abs, range(-5, 5)) + threads = executor._threads + del executor + + for t in threads: + t.join() + + # Make sure the results were all computed before the + # executor got shutdown. + assert all([r == abs(v) for r, v in zip(res, range(-5, 5))]) + + def test_shutdown_no_wait(self): + # Ensure that the executor cleans up the threads when calling + # shutdown with wait=False + executor = futures.ThreadPoolExecutor(max_workers=5) + res = executor.map(abs, range(-5, 5)) + threads = executor._threads + executor.shutdown(wait=False) + for t in threads: + t.join() + + # Make sure the results were all computed before the + # executor got shutdown. + assert all([r == abs(v) for r, v in zip(res, range(-5, 5))]) + + + def test_thread_names_assigned(self): + executor = futures.ThreadPoolExecutor( + max_workers=5, thread_name_prefix='SpecialPool') + executor.map(abs, range(-5, 5)) + threads = executor._threads + del executor + support.gc_collect() # For PyPy or other GCs. + + for t in threads: + self.assertRegex(t.name, r'^SpecialPool_[0-4]$') + t.join() + + def test_thread_names_default(self): + executor = futures.ThreadPoolExecutor(max_workers=5) + executor.map(abs, range(-5, 5)) + threads = executor._threads + del executor + support.gc_collect() # For PyPy or other GCs. + + for t in threads: + # Ensure that our default name is reasonably sane and unique when + # no thread_name_prefix was supplied. + self.assertRegex(t.name, r'ThreadPoolExecutor-\d+_[0-4]$') + t.join() + + def test_cancel_futures_wait_false(self): + # Can only be reliably tested for TPE, since PPE often hangs with + # `wait=False` (even without *cancel_futures*). + rc, out, err = assert_python_ok('-c', """if True: + from concurrent.futures import ThreadPoolExecutor + from test.test_concurrent_futures.test_shutdown import sleep_and_print + if __name__ == "__main__": + t = ThreadPoolExecutor() + t.submit(sleep_and_print, .1, "apple") + t.shutdown(wait=False, cancel_futures=True) + """) + # Errors in atexit hooks don't change the process exit code, check + # stderr manually. + self.assertFalse(err) + self.assertEqual(out.strip(), b"apple") + + +class ProcessPoolShutdownTest(ExecutorShutdownTest): + def test_processes_terminate(self): + def acquire_lock(lock): + lock.acquire() + + mp_context = self.get_context() + if mp_context.get_start_method(allow_none=False) == "fork": + # fork pre-spawns, not on demand. + expected_num_processes = self.worker_count + else: + expected_num_processes = 3 + + sem = mp_context.Semaphore(0) + for _ in range(3): + self.executor.submit(acquire_lock, sem) + self.assertEqual(len(self.executor._processes), expected_num_processes) + for _ in range(3): + sem.release() + processes = self.executor._processes + self.executor.shutdown() + + for p in processes.values(): + p.join() + + def test_context_manager_shutdown(self): + with futures.ProcessPoolExecutor( + max_workers=5, mp_context=self.get_context()) as e: + processes = e._processes + self.assertEqual(list(e.map(abs, range(-5, 5))), + [5, 4, 3, 2, 1, 0, 1, 2, 3, 4]) + + for p in processes.values(): + p.join() + + def test_del_shutdown(self): + executor = futures.ProcessPoolExecutor( + max_workers=5, mp_context=self.get_context()) + res = executor.map(abs, range(-5, 5)) + executor_manager_thread = executor._executor_manager_thread + processes = executor._processes + call_queue = executor._call_queue + executor_manager_thread = executor._executor_manager_thread + del executor + support.gc_collect() # For PyPy or other GCs. + + # Make sure that all the executor resources were properly cleaned by + # the shutdown process + executor_manager_thread.join() + for p in processes.values(): + p.join() + call_queue.join_thread() + + # Make sure the results were all computed before the + # executor got shutdown. + assert all([r == abs(v) for r, v in zip(res, range(-5, 5))]) + + def test_shutdown_no_wait(self): + # Ensure that the executor cleans up the processes when calling + # shutdown with wait=False + executor = futures.ProcessPoolExecutor( + max_workers=5, mp_context=self.get_context()) + res = executor.map(abs, range(-5, 5)) + processes = executor._processes + call_queue = executor._call_queue + executor_manager_thread = executor._executor_manager_thread + executor.shutdown(wait=False) + + # Make sure that all the executor resources were properly cleaned by + # the shutdown process + executor_manager_thread.join() + for p in processes.values(): + p.join() + call_queue.join_thread() + + # Make sure the results were all computed before the executor got + # shutdown. + assert all([r == abs(v) for r, v in zip(res, range(-5, 5))]) + + +create_executor_tests(globals(), ProcessPoolShutdownTest, + executor_mixins=(ProcessPoolForkMixin, + ProcessPoolForkserverMixin, + ProcessPoolSpawnMixin)) + + +def setUpModule(): + setup_module() + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_concurrent_futures/test_thread_pool.py b/Lib/test/test_concurrent_futures/test_thread_pool.py new file mode 100644 index 00000000000000..812f989d8f3ad2 --- /dev/null +++ b/Lib/test/test_concurrent_futures/test_thread_pool.py @@ -0,0 +1,100 @@ +import contextlib +import multiprocessing as mp +import multiprocessing.process +import multiprocessing.util +import os +import threading +import unittest +from concurrent import futures +from test import support + +from .executor import ExecutorTest, mul +from .util import BaseTestCase, ThreadPoolMixin, setup_module + + +class ThreadPoolExecutorTest(ThreadPoolMixin, ExecutorTest, BaseTestCase): + def test_map_submits_without_iteration(self): + """Tests verifying issue 11777.""" + finished = [] + def record_finished(n): + finished.append(n) + + self.executor.map(record_finished, range(10)) + self.executor.shutdown(wait=True) + self.assertCountEqual(finished, range(10)) + + def test_default_workers(self): + executor = self.executor_type() + expected = min(32, (os.cpu_count() or 1) + 4) + self.assertEqual(executor._max_workers, expected) + + def test_saturation(self): + executor = self.executor_type(4) + def acquire_lock(lock): + lock.acquire() + + sem = threading.Semaphore(0) + for i in range(15 * executor._max_workers): + executor.submit(acquire_lock, sem) + self.assertEqual(len(executor._threads), executor._max_workers) + for i in range(15 * executor._max_workers): + sem.release() + executor.shutdown(wait=True) + + def test_idle_thread_reuse(self): + executor = self.executor_type() + executor.submit(mul, 21, 2).result() + executor.submit(mul, 6, 7).result() + executor.submit(mul, 3, 14).result() + self.assertEqual(len(executor._threads), 1) + executor.shutdown(wait=True) + + @unittest.skipUnless(hasattr(os, 'register_at_fork'), 'need os.register_at_fork') + @support.requires_resource('cpu') + def test_hang_global_shutdown_lock(self): + # bpo-45021: _global_shutdown_lock should be reinitialized in the child + # process, otherwise it will never exit + def submit(pool): + pool.submit(submit, pool) + + with futures.ThreadPoolExecutor(1) as pool: + pool.submit(submit, pool) + + for _ in range(50): + with futures.ProcessPoolExecutor(1, mp_context=mp.get_context('fork')) as workers: + workers.submit(tuple) + + def test_executor_map_current_future_cancel(self): + stop_event = threading.Event() + log = [] + + def log_n_wait(ident): + log.append(f"{ident=} started") + try: + stop_event.wait() + finally: + log.append(f"{ident=} stopped") + + with self.executor_type(max_workers=1) as pool: + # submit work to saturate the pool + fut = pool.submit(log_n_wait, ident="first") + try: + with contextlib.closing( + pool.map(log_n_wait, ["second", "third"], timeout=0) + ) as gen: + with self.assertRaises(TimeoutError): + next(gen) + finally: + stop_event.set() + fut.result() + # ident='second' is cancelled as a result of raising a TimeoutError + # ident='third' is cancelled because it remained in the collection of futures + self.assertListEqual(log, ["ident='first' started", "ident='first' stopped"]) + + +def setUpModule(): + setup_module() + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_concurrent_futures/test_wait.py b/Lib/test/test_concurrent_futures/test_wait.py index 3f64ca173c02f6..e4bea8b05aced6 100644 --- a/Lib/test/test_concurrent_futures/test_wait.py +++ b/Lib/test/test_concurrent_futures/test_wait.py @@ -3,7 +3,6 @@ import time import unittest from concurrent import futures -from test import support from .util import ( CANCELLED_FUTURE, CANCELLED_AND_NOTIFIED_FUTURE, EXCEPTION_FUTURE, @@ -54,7 +53,6 @@ def test_first_completed_some_already_completed(self): finished) self.assertEqual(set([future1]), pending) - @support.requires_resource('walltime') def test_first_exception(self): future1 = self.executor.submit(mul, 2, 21) future2 = self.executor.submit(sleep_and_raise, 1.5) @@ -112,7 +110,6 @@ def test_all_completed(self): future2]), finished) self.assertEqual(set(), pending) - @support.requires_resource('walltime') def test_timeout(self): future1 = self.executor.submit(mul, 6, 7) future2 = self.executor.submit(time.sleep, 6) diff --git a/Lib/test/test_concurrent_futures/util.py b/Lib/test/test_concurrent_futures/util.py new file mode 100644 index 00000000000000..dc48bec796b87f --- /dev/null +++ b/Lib/test/test_concurrent_futures/util.py @@ -0,0 +1,141 @@ +import multiprocessing +import sys +import time +import unittest +from concurrent import futures +from concurrent.futures._base import ( + PENDING, RUNNING, CANCELLED, CANCELLED_AND_NOTIFIED, FINISHED, Future, + ) +from concurrent.futures.process import _check_system_limits + +from test import support +from test.support import threading_helper + + +def create_future(state=PENDING, exception=None, result=None): + f = Future() + f._state = state + f._exception = exception + f._result = result + return f + + +PENDING_FUTURE = create_future(state=PENDING) +RUNNING_FUTURE = create_future(state=RUNNING) +CANCELLED_FUTURE = create_future(state=CANCELLED) +CANCELLED_AND_NOTIFIED_FUTURE = create_future(state=CANCELLED_AND_NOTIFIED) +EXCEPTION_FUTURE = create_future(state=FINISHED, exception=OSError()) +SUCCESSFUL_FUTURE = create_future(state=FINISHED, result=42) + + +class BaseTestCase(unittest.TestCase): + def setUp(self): + self._thread_key = threading_helper.threading_setup() + + def tearDown(self): + support.reap_children() + threading_helper.threading_cleanup(*self._thread_key) + + +class ExecutorMixin: + worker_count = 5 + executor_kwargs = {} + + def setUp(self): + super().setUp() + + self.t1 = time.monotonic() + if hasattr(self, "ctx"): + self.executor = self.executor_type( + max_workers=self.worker_count, + mp_context=self.get_context(), + **self.executor_kwargs) + else: + self.executor = self.executor_type( + max_workers=self.worker_count, + **self.executor_kwargs) + + def tearDown(self): + self.executor.shutdown(wait=True) + self.executor = None + + dt = time.monotonic() - self.t1 + if support.verbose: + print("%.2fs" % dt, end=' ') + self.assertLess(dt, 300, "synchronization issue: test lasted too long") + + super().tearDown() + + def get_context(self): + return multiprocessing.get_context(self.ctx) + + +class ThreadPoolMixin(ExecutorMixin): + executor_type = futures.ThreadPoolExecutor + + +class ProcessPoolForkMixin(ExecutorMixin): + executor_type = futures.ProcessPoolExecutor + ctx = "fork" + + def get_context(self): + try: + _check_system_limits() + except NotImplementedError: + self.skipTest("ProcessPoolExecutor unavailable on this system") + if sys.platform == "win32": + self.skipTest("require unix system") + return super().get_context() + + +class ProcessPoolSpawnMixin(ExecutorMixin): + executor_type = futures.ProcessPoolExecutor + ctx = "spawn" + + def get_context(self): + try: + _check_system_limits() + except NotImplementedError: + self.skipTest("ProcessPoolExecutor unavailable on this system") + return super().get_context() + + +class ProcessPoolForkserverMixin(ExecutorMixin): + executor_type = futures.ProcessPoolExecutor + ctx = "forkserver" + + def get_context(self): + try: + _check_system_limits() + except NotImplementedError: + self.skipTest("ProcessPoolExecutor unavailable on this system") + if sys.platform == "win32": + self.skipTest("require unix system") + return super().get_context() + + +def create_executor_tests(remote_globals, mixin, bases=(BaseTestCase,), + executor_mixins=(ThreadPoolMixin, + ProcessPoolForkMixin, + ProcessPoolForkserverMixin, + ProcessPoolSpawnMixin)): + def strip_mixin(name): + if name.endswith(('Mixin', 'Tests')): + return name[:-5] + elif name.endswith('Test'): + return name[:-4] + else: + return name + + module = remote_globals['__name__'] + for exe in executor_mixins: + name = ("%s%sTest" + % (strip_mixin(exe.__name__), strip_mixin(mixin.__name__))) + cls = type(name, (mixin,) + (exe,) + bases, {'__module__': module}) + remote_globals[name] = cls + + +def setup_module(): + unittest.addModuleCleanup(multiprocessing.util._cleanup_tests) + thread_info = threading_helper.threading_setup() + unittest.addModuleCleanup(threading_helper.threading_cleanup, *thread_info) diff --git a/Misc/NEWS.d/next/Tests/2023-08-24-06-10-36.gh-issue-108388.YCVB0D.rst b/Misc/NEWS.d/next/Tests/2023-08-24-06-10-36.gh-issue-108388.YCVB0D.rst new file mode 100644 index 00000000000000..ddff07be024d47 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-08-24-06-10-36.gh-issue-108388.YCVB0D.rst @@ -0,0 +1,2 @@ +Convert test_concurrent_futures to a package of 7 sub-tests. Patch by Victor +Stinner. From 43ff8fcce21377cdce3a594ea10e1a045bb70752 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 21 Sep 2023 18:45:11 -0700 Subject: [PATCH 337/632] [3.11] gh-109375: Fix bug where pdb registers an alias without an associated command (GH-109376) (#109430) gh-109375: Fix bug where pdb registers an alias without an associated command (GH-109376) (cherry picked from commit 68a6f21f47e779ddd70e33cf04d170a63f077fcd) Co-authored-by: buermarc <44375277+buermarc@users.noreply.github.com> --- Lib/pdb.py | 7 +++++-- Lib/test/test_pdb.py | 6 ++++++ Misc/ACKS | 1 + .../Library/2023-09-13-17-22-44.gh-issue-109375.ijJHZ9.rst | 1 + 4 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-09-13-17-22-44.gh-issue-109375.ijJHZ9.rst diff --git a/Lib/pdb.py b/Lib/pdb.py index d3824e19fa82d1..fe9eab9b5e1ff0 100755 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -1505,8 +1505,11 @@ def do_alias(self, arg): for alias in keys: self.message("%s = %s" % (alias, self.aliases[alias])) return - if args[0] in self.aliases and len(args) == 1: - self.message("%s = %s" % (args[0], self.aliases[args[0]])) + if len(args) == 1: + if args[0] in self.aliases: + self.message("%s = %s" % (args[0], self.aliases[args[0]])) + else: + self.error(f"Unknown alias '{args[0]}'") else: self.aliases[args[0]] = ' '.join(args[1:]) diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 4f4a56922803cf..22fc2b35819466 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -640,8 +640,10 @@ def test_pdb_alias_command(): ... o.method() >>> with PdbTestInput([ # doctest: +ELLIPSIS + ... 'alias pi', ... 'alias pi for k in %1.__dict__.keys(): print(f"%1.{k} = {%1.__dict__[k]}")', ... 'alias ps pi self', + ... 'alias ps', ... 'pi o', ... 's', ... 'ps', @@ -650,8 +652,12 @@ def test_pdb_alias_command(): ... test_function() > (4)test_function() -> o.method() + (Pdb) alias pi + *** Unknown alias 'pi' (Pdb) alias pi for k in %1.__dict__.keys(): print(f"%1.{k} = {%1.__dict__[k]}") (Pdb) alias ps pi self + (Pdb) alias ps + ps = pi self (Pdb) pi o o.attr1 = 10 o.attr2 = str diff --git a/Misc/ACKS b/Misc/ACKS index 5fe93d638082dc..988c3675cf2eee 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -252,6 +252,7 @@ Curtis Bucher Colm Buckley Erik de Bueger Jan-Hein Bührman +Marc Bürg Lars Buitinck Artem Bulgakov Dick Bulterman diff --git a/Misc/NEWS.d/next/Library/2023-09-13-17-22-44.gh-issue-109375.ijJHZ9.rst b/Misc/NEWS.d/next/Library/2023-09-13-17-22-44.gh-issue-109375.ijJHZ9.rst new file mode 100644 index 00000000000000..9b7a85d05f66ca --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-09-13-17-22-44.gh-issue-109375.ijJHZ9.rst @@ -0,0 +1 @@ +The :mod:`pdb` ``alias`` command now prevents registering aliases without arguments. From 74978ae6c6797e501af6822b3b829fd06a78eadb Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 21 Sep 2023 19:02:57 -0700 Subject: [PATCH 338/632] [3.11] gh-109702: Increase concurrent_futures deadlock timeout (GH-109703) (#109708) gh-109702: Increase concurrent_futures deadlock timeout (GH-109703) Replace SHORT_TIMEOUT with LONG_TIMEOUT in test_deadlock of test_concurrent_futures. (cherry picked from commit 1eb1b45183c3b8aeefe3d5d27694155741e82bbc) Co-authored-by: Victor Stinner --- Lib/test/test_concurrent_futures/test_deadlock.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_concurrent_futures/test_deadlock.py b/Lib/test/test_concurrent_futures/test_deadlock.py index 6b78b360d15627..d128cf2374fc21 100644 --- a/Lib/test/test_concurrent_futures/test_deadlock.py +++ b/Lib/test/test_concurrent_futures/test_deadlock.py @@ -88,7 +88,7 @@ def __reduce__(self): class ExecutorDeadlockTest: - TIMEOUT = support.SHORT_TIMEOUT + TIMEOUT = support.LONG_TIMEOUT def _fail_on_deadlock(self, executor): # If we did not recover before TIMEOUT seconds, consider that the From f45ef5edabb1cc0748f3326e7114b8aaa0424392 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Fri, 22 Sep 2023 13:34:46 +0300 Subject: [PATCH 339/632] [3.11] gh-109625: Move _ready_to_import() from test_import to support.import_helper (GH-109626) (#109718) [3.11] gh-109625: Move _ready_to_import() from test_import to support.import_helper (GH-109626). (cherry picked from commit 115c49ad5a5ccfb628fef3ae06a566f7a0197f97) --- Lib/test/support/import_helper.py | 25 +++++++++++++++++++- Lib/test/test_import/__init__.py | 38 ++++++++----------------------- Lib/test/test_inspect.py | 6 ++--- 3 files changed, 35 insertions(+), 34 deletions(-) diff --git a/Lib/test/support/import_helper.py b/Lib/test/support/import_helper.py index 5201dc84cf6df4..a803d9f1b4039a 100644 --- a/Lib/test/support/import_helper.py +++ b/Lib/test/support/import_helper.py @@ -8,7 +8,7 @@ import unittest import warnings -from .os_helper import unlink +from .os_helper import unlink, temp_dir @contextlib.contextmanager @@ -246,3 +246,26 @@ def modules_cleanup(oldmodules): # do currently). Implicitly imported *real* modules should be left alone # (see issue 10556). sys.modules.update(oldmodules) + + +@contextlib.contextmanager +def ready_to_import(name=None, source=""): + from test.support import script_helper + + # 1. Sets up a temporary directory and removes it afterwards + # 2. Creates the module file + # 3. Temporarily clears the module from sys.modules (if any) + # 4. Reverts or removes the module when cleaning up + name = name or "spam" + with temp_dir() as tempdir: + path = script_helper.make_script(tempdir, name, source) + old_module = sys.modules.pop(name, None) + try: + sys.path.insert(0, tempdir) + yield name, path + sys.path.remove(tempdir) + finally: + if old_module is not None: + sys.modules[name] = old_module + else: + sys.modules.pop(name, None) diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index 6e2606cdb05bc6..131aebbd4e1f56 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -24,9 +24,10 @@ STDLIB_DIR, is_jython, swap_attr, swap_item, cpython_only, is_emscripten, is_wasi) from test.support.import_helper import ( - forget, make_legacy_pyc, unlink, unload, DirsOnSysPath, CleanImport) + forget, make_legacy_pyc, unlink, unload, ready_to_import, + DirsOnSysPath, CleanImport) from test.support.os_helper import ( - TESTFN, rmtree, temp_umask, TESTFN_UNENCODABLE, temp_dir) + TESTFN, rmtree, temp_umask, TESTFN_UNENCODABLE) from test.support import script_helper from test.support import threading_helper from test.test_importlib.util import uncache @@ -46,27 +47,6 @@ def remove_files(name): rmtree('__pycache__') -@contextlib.contextmanager -def _ready_to_import(name=None, source=""): - # sets up a temporary directory and removes it - # creates the module file - # temporarily clears the module from sys.modules (if any) - # reverts or removes the module when cleaning up - name = name or "spam" - with temp_dir() as tempdir: - path = script_helper.make_script(tempdir, name, source) - old_module = sys.modules.pop(name, None) - try: - sys.path.insert(0, tempdir) - yield name, path - sys.path.remove(tempdir) - finally: - if old_module is not None: - sys.modules[name] = old_module - elif name in sys.modules: - del sys.modules[name] - - class ImportTests(unittest.TestCase): def setUp(self): @@ -130,7 +110,7 @@ def test_from_import_missing_attr_path_is_canonical(self): def test_from_import_star_invalid_type(self): import re - with _ready_to_import() as (name, path): + with ready_to_import() as (name, path): with open(path, 'w', encoding='utf-8') as f: f.write("__all__ = [b'invalid_type']") globals = {} @@ -139,7 +119,7 @@ def test_from_import_star_invalid_type(self): ): exec(f"from {name} import *", globals) self.assertNotIn(b"invalid_type", globals) - with _ready_to_import() as (name, path): + with ready_to_import() as (name, path): with open(path, 'w', encoding='utf-8') as f: f.write("globals()[b'invalid_type'] = object()") globals = {} @@ -550,7 +530,7 @@ class FilePermissionTests(unittest.TestCase): ) def test_creation_mode(self): mask = 0o022 - with temp_umask(mask), _ready_to_import() as (name, path): + with temp_umask(mask), ready_to_import() as (name, path): cached_path = importlib.util.cache_from_source(path) module = __import__(name) if not os.path.exists(cached_path): @@ -569,7 +549,7 @@ def test_creation_mode(self): def test_cached_mode_issue_2051(self): # permissions of .pyc should match those of .py, regardless of mask mode = 0o600 - with temp_umask(0o022), _ready_to_import() as (name, path): + with temp_umask(0o022), ready_to_import() as (name, path): cached_path = importlib.util.cache_from_source(path) os.chmod(path, mode) __import__(name) @@ -585,7 +565,7 @@ def test_cached_mode_issue_2051(self): @os_helper.skip_unless_working_chmod def test_cached_readonly(self): mode = 0o400 - with temp_umask(0o022), _ready_to_import() as (name, path): + with temp_umask(0o022), ready_to_import() as (name, path): cached_path = importlib.util.cache_from_source(path) os.chmod(path, mode) __import__(name) @@ -600,7 +580,7 @@ def test_cached_readonly(self): def test_pyc_always_writable(self): # Initially read-only .pyc files on Windows used to cause problems # with later updates, see issue #6074 for details - with _ready_to_import() as (name, path): + with ready_to_import() as (name, path): # Write a Python file, make it read-only and import it with open(path, 'w', encoding='utf-8') as f: f.write("x = 'original'\n") diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index f5824778d1e5d6..650d56e39c415c 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -27,7 +27,7 @@ from test.support import cpython_only from test.support import MISSING_C_DOCSTRINGS, ALWAYS_EQ -from test.support.import_helper import DirsOnSysPath +from test.support.import_helper import DirsOnSysPath, ready_to_import from test.support.os_helper import TESTFN from test.support.script_helper import assert_python_ok, assert_python_failure from test import inspect_fodder as mod @@ -37,8 +37,6 @@ from test import inspect_stringized_annotations from test import inspect_stringized_annotations_2 -from test.test_import import _ready_to_import - # Functions tested in this suite: # ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode, @@ -4505,7 +4503,7 @@ def assertInspectEqual(self, path, source): def test_getsource_reload(self): # see issue 1218234 - with _ready_to_import('reload_bug', self.src_before) as (name, path): + with ready_to_import('reload_bug', self.src_before) as (name, path): module = importlib.import_module(name) self.assertInspectEqual(path, module) with open(path, 'w', encoding='utf-8') as src: From 1629b1d6f96b377d3ae8e6b6e35e6ffd304f0cd7 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 22 Sep 2023 16:34:53 +0200 Subject: [PATCH 340/632] [3.11] gh-109709: Fix asyncio test_stdin_broken_pipe() (#109710) (#109735) gh-109709: Fix asyncio test_stdin_broken_pipe() (#109710) Replace harcoded sleep of 500 ms with synchronization using a pipe. Fix also Process._feed_stdin(): catch also BrokenPipeError on stdin.write(input), not only on stdin.drain(). (cherry picked from commit cbbdf2c1440c804adcfc32ea0470865b3b3b8eb2) --- Lib/asyncio/subprocess.py | 12 +++--- Lib/test/test_asyncio/test_subprocess.py | 52 +++++++++++++++++++----- 2 files changed, 49 insertions(+), 15 deletions(-) diff --git a/Lib/asyncio/subprocess.py b/Lib/asyncio/subprocess.py index c380bbb0ee93ec..da4f00a4a07fc8 100644 --- a/Lib/asyncio/subprocess.py +++ b/Lib/asyncio/subprocess.py @@ -147,14 +147,16 @@ def kill(self): async def _feed_stdin(self, input): debug = self._loop.get_debug() - self.stdin.write(input) - if debug: - logger.debug( - '%r communicate: feed stdin (%s bytes)', self, len(input)) try: + self.stdin.write(input) + if debug: + logger.debug( + '%r communicate: feed stdin (%s bytes)', self, len(input)) + await self.stdin.drain() except (BrokenPipeError, ConnectionResetError) as exc: - # communicate() ignores BrokenPipeError and ConnectionResetError + # communicate() ignores BrokenPipeError and ConnectionResetError. + # write() and drain() can raise these exceptions. if debug: logger.debug('%r communicate: stdin got %r', self, exc) diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py index bea2314a528663..8b4f14eb48de65 100644 --- a/Lib/test/test_asyncio/test_subprocess.py +++ b/Lib/test/test_asyncio/test_subprocess.py @@ -2,6 +2,7 @@ import shutil import signal import sys +import textwrap import unittest import warnings from unittest import mock @@ -13,9 +14,14 @@ from test import support from test.support import os_helper -if sys.platform != 'win32': + +MS_WINDOWS = (sys.platform == 'win32') +if MS_WINDOWS: + import msvcrt +else: from asyncio import unix_events + if support.check_sanitizer(address=True): raise unittest.SkipTest("Exposes ASAN flakiness in GitHub CI") @@ -253,26 +259,43 @@ async def send_signal(proc): finally: signal.signal(signal.SIGHUP, old_handler) - def prepare_broken_pipe_test(self): + def test_stdin_broken_pipe(self): # buffer large enough to feed the whole pipe buffer large_data = b'x' * support.PIPE_MAX_SIZE + rfd, wfd = os.pipe() + self.addCleanup(os.close, rfd) + self.addCleanup(os.close, wfd) + if MS_WINDOWS: + handle = msvcrt.get_osfhandle(rfd) + os.set_handle_inheritable(handle, True) + code = textwrap.dedent(f''' + import os, msvcrt + handle = {handle} + fd = msvcrt.open_osfhandle(handle, os.O_RDONLY) + os.read(fd, 1) + ''') + from subprocess import STARTUPINFO + startupinfo = STARTUPINFO() + startupinfo.lpAttributeList = {"handle_list": [handle]} + kwargs = dict(startupinfo=startupinfo) + else: + code = f'import os; fd = {rfd}; os.read(fd, 1)' + kwargs = dict(pass_fds=(rfd,)) + # the program ends before the stdin can be fed proc = self.loop.run_until_complete( asyncio.create_subprocess_exec( - sys.executable, '-c', 'pass', + sys.executable, '-c', code, stdin=subprocess.PIPE, + **kwargs ) ) - return (proc, large_data) - - def test_stdin_broken_pipe(self): - proc, large_data = self.prepare_broken_pipe_test() - async def write_stdin(proc, data): - await asyncio.sleep(0.5) proc.stdin.write(data) + # Only exit the child process once the write buffer is filled + os.write(wfd, b'go') await proc.stdin.drain() coro = write_stdin(proc, large_data) @@ -283,7 +306,16 @@ async def write_stdin(proc, data): self.loop.run_until_complete(proc.wait()) def test_communicate_ignore_broken_pipe(self): - proc, large_data = self.prepare_broken_pipe_test() + # buffer large enough to feed the whole pipe buffer + large_data = b'x' * support.PIPE_MAX_SIZE + + # the program ends before the stdin can be fed + proc = self.loop.run_until_complete( + asyncio.create_subprocess_exec( + sys.executable, '-c', 'pass', + stdin=subprocess.PIPE, + ) + ) # communicate() must ignore BrokenPipeError when feeding stdin self.loop.set_exception_handler(lambda loop, msg: None) From 567c3e846ea06beedcca21e5056dcc10870f1c48 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 22 Sep 2023 08:05:04 -0700 Subject: [PATCH 341/632] [3.11] ACKS: Fix ordering; Correct Itamar Oren's surname; Add Adam Turner (GH-109737) (#109742) ACKS: Fix ordering; Correct Itamar Oren's surname; Add Adam Turner (GH-109737) (cherry picked from commit 3e8fcb7df74248530c4280915c77e69811f69c3f) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Misc/ACKS | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Misc/ACKS b/Misc/ACKS index 988c3675cf2eee..4d2e8211bb023e 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -496,6 +496,7 @@ Daniel Ellis Phil Elson David Ely Victor van den Elzen +Vlad Emelianov Jeff Epler Tom Epperly Gökcen Eraslan @@ -1312,6 +1313,7 @@ Ethan Onstott Ken Jin Ooi Piet van Oostrum Tomas Oppelstrup +Itamar Oren Jason Orendorff Yan "yyyyyyyan" Orestes Bastien Orivel @@ -1839,6 +1841,7 @@ Jason Trowbridge Brent Tubbs Anthony Tuininga Erno Tukia +Adam Turner David Turner Stephen Turner Itamar Turner-Trauring @@ -2045,7 +2048,5 @@ Jelle Zijlstra Gennadiy Zlobin Doug Zongker Peter Åstrand -Vlad Emelianov -Andrey Doroschenko (Entries should be added in rough alphabetical order by last names) From 6bb18bb0b457b0d4f75e388f9f83772e4d90e312 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Fri, 22 Sep 2023 18:50:32 +0300 Subject: [PATCH 342/632] [3.11] gh-108303: Fix and move `badsyntax_pep3120.py` (GH-109513) (#109724) * [3.11] gh-108303: Fix and move `badsyntax_pep3120.py` (GH-109513) Co-authored-by: Alex Waygood . (cherry picked from commit 4dd47c63a97b3c39cd964ad12431fcdaf76dc823) Backport to 3.11: update also test_imp. Co-authored-by: Nikita Sobolev --- Lib/test/.ruff.toml | 4 ++-- Lib/test/test_imp.py | 8 +++++--- Lib/test/test_utf8source.py | 4 +--- Lib/test/{ => tokenizedata}/badsyntax_pep3120.py | 0 4 files changed, 8 insertions(+), 8 deletions(-) rename Lib/test/{ => tokenizedata}/badsyntax_pep3120.py (100%) diff --git a/Lib/test/.ruff.toml b/Lib/test/.ruff.toml index 08bd5c8a87ee21..0afa0fe36b0ce4 100644 --- a/Lib/test/.ruff.toml +++ b/Lib/test/.ruff.toml @@ -3,13 +3,13 @@ select = [ "F811", # Redefinition of unused variable (useful for finding test methods with the same name) ] extend-exclude = [ + # Excluded (these aren't actually executed, they're just "data files") + "tokenizedata/*.py", # Failed to lint - "badsyntax_pep3120.py", "encoded_modules/module_iso_8859_1.py", "encoded_modules/module_koi8_r.py", "test_source_encoding.py", # Failed to parse - "badsyntax_3131.py", "test_fstring.py", # TODO Fix: F811 Redefinition of unused name "test_buffer.py", diff --git a/Lib/test/test_imp.py b/Lib/test/test_imp.py index 4bb03908fc2bb2..4062afd7254870 100644 --- a/Lib/test/test_imp.py +++ b/Lib/test/test_imp.py @@ -78,7 +78,7 @@ def test_find_module_encoding(self): with imp.find_module('module_' + mod, self.test_path)[0] as fd: self.assertEqual(fd.encoding, encoding) - path = [os.path.dirname(__file__)] + path = [os.path.join(os.path.dirname(__file__), 'tokenizedata')] with self.assertRaises(SyntaxError): imp.find_module('badsyntax_pep3120', path) @@ -203,9 +203,11 @@ def test_issue5604(self): os_helper.rmtree('__pycache__') def test_issue9319(self): - path = os.path.dirname(__file__) + path = os.path.join(os.path.dirname(__file__), "tokenizedata") self.assertRaises(SyntaxError, - imp.find_module, "badsyntax_pep3120", [path]) + imp.find_module, + "badsyntax_pep3120", + [path]) def test_load_from_source(self): # Verify that the imp module can correctly load and find .py files diff --git a/Lib/test/test_utf8source.py b/Lib/test/test_utf8source.py index 97dced8a622889..c42b6aaaab579d 100644 --- a/Lib/test/test_utf8source.py +++ b/Lib/test/test_utf8source.py @@ -1,5 +1,3 @@ -# This file is marked as binary in the CVS, to prevent MacCVS from recoding it. - import unittest class PEP3120Test(unittest.TestCase): @@ -16,7 +14,7 @@ def test_pep3120(self): def test_badsyntax(self): try: - import test.badsyntax_pep3120 + import test.tokenizedata.badsyntax_pep3120 except SyntaxError as msg: msg = str(msg).lower() self.assertTrue('utf-8' in msg) diff --git a/Lib/test/badsyntax_pep3120.py b/Lib/test/tokenizedata/badsyntax_pep3120.py similarity index 100% rename from Lib/test/badsyntax_pep3120.py rename to Lib/test/tokenizedata/badsyntax_pep3120.py From 3db2ec26bc68847d79a889a7c740b2fe30be10f0 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 22 Sep 2023 15:09:46 -0700 Subject: [PATCH 343/632] [3.11] gh-109706: Fix multiprocessing test_nested_startmethod() (GH-109707) (#109763) gh-109706: Fix multiprocessing test_nested_startmethod() (GH-109707) Don't check order, queue items can be written in any order. (cherry picked from commit b03a791497ff4b3c42805e06c73d08ac34087402) Co-authored-by: Victor Stinner --- Lib/test/_test_multiprocessing.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index ac0ed397684f42..e83c4416cb05a3 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -5396,7 +5396,9 @@ def test_nested_startmethod(self): while not queue.empty(): results.append(queue.get()) - self.assertEqual(results, [2, 1]) + # gh-109706: queue.put(1) can write into the queue before queue.put(2), + # there is no synchronization in the test. + self.assertSetEqual(set(results), set([2, 1])) @unittest.skipIf(sys.platform == "win32", From cbe153edd687a49e21bea5d12ae3c5a2600b5e11 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 22 Sep 2023 21:53:47 -0600 Subject: [PATCH 344/632] [3.11] Docs: Update Donghee Na's name (GH-109743) (#109759) * Update Donghee Na's name in Docs/ * Update Donghee Na's name in Misc/ACKS * Update Donghee Na's name in Misc/NEWS.d/ --- Doc/whatsnew/3.10.rst | 8 ++++---- Doc/whatsnew/3.11.rst | 18 +++++++++--------- Doc/whatsnew/3.8.rst | 4 ++-- Doc/whatsnew/3.9.rst | 32 ++++++++++++++++---------------- Misc/ACKS | 2 +- Misc/NEWS.d/3.10.0a1.rst | 12 ++++++------ Misc/NEWS.d/3.10.0a2.rst | 4 ++-- Misc/NEWS.d/3.10.0a3.rst | 2 +- Misc/NEWS.d/3.10.0a4.rst | 4 ++-- Misc/NEWS.d/3.10.0a6.rst | 2 +- Misc/NEWS.d/3.10.0a7.rst | 4 ++-- Misc/NEWS.d/3.10.0b1.rst | 2 +- Misc/NEWS.d/3.11.0a1.rst | 24 ++++++++++++------------ Misc/NEWS.d/3.11.0a2.rst | 10 +++++----- Misc/NEWS.d/3.11.0a3.rst | 2 +- Misc/NEWS.d/3.11.0a5.rst | 2 +- Misc/NEWS.d/3.11.0a6.rst | 6 +++--- Misc/NEWS.d/3.11.0a7.rst | 4 ++-- Misc/NEWS.d/3.11.0b1.rst | 2 +- Misc/NEWS.d/3.11.0b4.rst | 2 +- Misc/NEWS.d/3.11.1.rst | 8 ++++---- Misc/NEWS.d/3.11.2.rst | 6 +++--- Misc/NEWS.d/3.11.4.rst | 4 ++-- Misc/NEWS.d/3.11.5.rst | 2 +- Misc/NEWS.d/3.5.4.rst | 2 +- Misc/NEWS.d/3.5.4rc1.rst | 2 +- Misc/NEWS.d/3.6.2rc1.rst | 6 +++--- Misc/NEWS.d/3.6.3rc1.rst | 2 +- Misc/NEWS.d/3.6.6rc1.rst | 2 +- Misc/NEWS.d/3.7.0a1.rst | 8 ++++---- Misc/NEWS.d/3.7.0a3.rst | 4 ++-- Misc/NEWS.d/3.7.0b5.rst | 2 +- Misc/NEWS.d/3.8.0a1.rst | 4 ++-- Misc/NEWS.d/3.9.0a1.rst | 20 ++++++++++---------- Misc/NEWS.d/3.9.0a3.rst | 16 ++++++++-------- Misc/NEWS.d/3.9.0a4.rst | 6 +++--- Misc/NEWS.d/3.9.0a5.rst | 14 +++++++------- Misc/NEWS.d/3.9.0b1.rst | 6 +++--- 38 files changed, 130 insertions(+), 130 deletions(-) diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 9213c9c3e45e1d..572bb4b07e5030 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -877,7 +877,7 @@ Other Language Changes (Contributed by Raymond Hettinger in :issue:`43475`.) * A :exc:`SyntaxError` (instead of a :exc:`NameError`) will be raised when deleting - the :const:`__debug__` constant. (Contributed by Dong-hee Na in :issue:`45000`.) + the :const:`__debug__` constant. (Contributed by Donghee Na in :issue:`45000`.) * :exc:`SyntaxError` exceptions now have ``end_lineno`` and ``end_offset`` attributes. They will be ``None`` if not determined. @@ -1254,7 +1254,7 @@ pipe. (Contributed by Pablo Galindo in :issue:`41625`.) Add :const:`~os.O_EVTONLY`, :const:`~os.O_FSYNC`, :const:`~os.O_SYMLINK` and :const:`~os.O_NOFOLLOW_ANY` for macOS. -(Contributed by Dong-hee Na in :issue:`43106`.) +(Contributed by Donghee Na in :issue:`43106`.) os.path ------- @@ -1581,7 +1581,7 @@ Optimizations * The following built-in functions now support the faster :pep:`590` vectorcall calling convention: :func:`map`, :func:`filter`, :func:`reversed`, :func:`bool` and :func:`float`. - (Contributed by Dong-hee Na and Jeroen Demeyer in :issue:`43575`, :issue:`43287`, :issue:`41922`, :issue:`41873` and :issue:`41870`.) + (Contributed by Donghee Na and Jeroen Demeyer in :issue:`43575`, :issue:`43287`, :issue:`41922`, :issue:`41873` and :issue:`41870`.) * :class:`BZ2File` performance is improved by removing internal ``RLock``. This makes :class:`BZ2File` thread unsafe in the face of multiple simultaneous @@ -1816,7 +1816,7 @@ Removed scheduled to be removed in Python 3.6, but such removals were delayed until after Python 2.7 EOL. Existing users should copy whatever classes they use into their code. - (Contributed by Dong-hee Na and Terry J. Reedy in :issue:`42299`.) + (Contributed by Donghee Na and Terry J. Reedy in :issue:`42299`.) * Removed the :c:func:`!PyModule_GetWarningsModule` function that was useless now due to the :mod:`!_warnings` module was converted to a builtin module in 2.6. diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index af7162128ebbc2..edaa9e34c481d5 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -499,7 +499,7 @@ Other CPython Implementation Changes * The special methods :meth:`~object.__complex__` for :class:`complex` and :meth:`~object.__bytes__` for :class:`bytes` are implemented to support the :class:`typing.SupportsComplex` and :class:`typing.SupportsBytes` protocols. - (Contributed by Mark Dickinson and Dong-hee Na in :issue:`24234`.) + (Contributed by Mark Dickinson and Donghee Na in :issue:`24234`.) * ``siphash13`` is added as a new internal hashing algorithm. It has similar security properties as ``siphash24``, @@ -897,7 +897,7 @@ os * On Windows, :func:`os.urandom` now uses ``BCryptGenRandom()``, instead of ``CryptGenRandom()`` which is deprecated. - (Contributed by Dong-hee Na in :issue:`44611`.) + (Contributed by Donghee Na in :issue:`44611`.) .. _whatsnew311-pathlib: @@ -1089,7 +1089,7 @@ time `_ which has a resolution of 100 nanoseconds (10\ :sup:`-7` seconds). Previously, it had a resolution of 1 millisecond (10\ :sup:`-3` seconds). - (Contributed by Benjamin Szőke, Dong-hee Na, Eryk Sun and Victor Stinner in :issue:`21302` and :issue:`45429`.) + (Contributed by Benjamin Szőke, Donghee Na, Eryk Sun and Victor Stinner in :issue:`21302` and :issue:`45429`.) .. _whatsnew311-tkinter: @@ -1305,7 +1305,7 @@ This section covers specific optimizations independent of the * :func:`unicodedata.normalize` now normalizes pure-ASCII strings in constant time. - (Contributed by Dong-hee Na in :issue:`44987`.) + (Contributed by Donghee Na in :issue:`44987`.) .. _whatsnew311-faster-cpython: @@ -1452,7 +1452,7 @@ Bucher, with additional help from Irit Katriel and Dennis Sweeney.) | | | | (up to) | | +===============+====================+=======================================================+===================+===================+ | Binary | ``x + x`` | Binary add, multiply and subtract for common types | 10% | Mark Shannon, | -| operations | | such as :class:`int`, :class:`float` and :class:`str` | | Dong-hee Na, | +| operations | | such as :class:`int`, :class:`float` and :class:`str` | | Donghee Na, | | | ``x - x`` | take custom fast paths for their underlying types. | | Brandt Bucher, | | | | | | Dennis Sweeney | | | ``x * x`` | | | | @@ -1839,7 +1839,7 @@ Standard Library * :class:`!webbrowser.MacOSX` is deprecated and will be removed in Python 3.13. It is untested, undocumented, and not used by :mod:`webbrowser` itself. - (Contributed by Dong-hee Na in :issue:`42255`.) + (Contributed by Donghee Na in :issue:`42255`.) * The behavior of returning a value from a :class:`~unittest.TestCase` and :class:`~unittest.IsolatedAsyncioTestCase` test methods (other than the @@ -1984,7 +1984,7 @@ Removed C APIs are :ref:`listed separately `. :meth:`!NullTranslations.set_output_charset` methods, and the *codeset* parameter of :func:`!translation` and :func:`!install`, since they are only used for the :func:`!l*gettext` functions. - (Contributed by Dong-hee Na and Serhiy Storchaka in :issue:`44235`.) + (Contributed by Donghee Na and Serhiy Storchaka in :issue:`44235`.) * Removed from the :mod:`inspect` module: @@ -2009,7 +2009,7 @@ Removed C APIs are :ref:`listed separately `. * Removed the :class:`!MailmanProxy` class in the :mod:`smtpd` module, as it is unusable without the external :mod:`!mailman` package. - (Contributed by Dong-hee Na in :issue:`35800`.) + (Contributed by Donghee Na in :issue:`35800`.) * Removed the deprecated :meth:`!split` method of :class:`!_tkinter.TkappType`. (Contributed by Erlend E. Aasland in :issue:`38371`.) @@ -2151,7 +2151,7 @@ Build Changes * CPython can now be built with the `ThinLTO `_ option via passing ``thin`` to :option:`--with-lto`, i.e. ``--with-lto=thin``. - (Contributed by Dong-hee Na and Brett Holman in :issue:`44340`.) + (Contributed by Donghee Na and Brett Holman in :issue:`44340`.) * Freelists for object structs can now be disabled. A new :program:`configure` option :option:`!--without-freelists` can be used to disable all freelists diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index 1d2f0a6ff3ca5e..71b135de2d693c 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -947,7 +947,7 @@ This made it difficult to update, experiment with, or teach the various logging configuration options using the interactive prompt or a Jupyter notebook. -(Suggested by Raymond Hettinger, implemented by Dong-hee Na, and +(Suggested by Raymond Hettinger, implemented by Donghee Na, and reviewed by Vinay Sajip in :issue:`33897`.) @@ -1711,7 +1711,7 @@ Deprecated * The :meth:`~threading.Thread.isAlive()` method of :class:`threading.Thread` has been deprecated. - (Contributed by Dong-hee Na in :issue:`35283`.) + (Contributed by Donghee Na in :issue:`35283`.) * Many builtin and extension functions that take integer arguments will now emit a deprecation warning for :class:`~decimal.Decimal`\ s, diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index 6829970900930a..fb73eaf59d5dac 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -415,7 +415,7 @@ datetime The :meth:`~datetime.date.isocalendar()` of :class:`datetime.date` and :meth:`~datetime.datetime.isocalendar()` of :class:`datetime.datetime` methods now returns a :func:`~collections.namedtuple` instead of a :class:`tuple`. -(Contributed by Dong-hee Na in :issue:`24416`.) +(Contributed by Donghee Na in :issue:`24416`.) distutils --------- @@ -429,14 +429,14 @@ fcntl Added constants :const:`~fcntl.F_OFD_GETLK`, :const:`~fcntl.F_OFD_SETLK` and :const:`~fcntl.F_OFD_SETLKW`. -(Contributed by Dong-hee Na in :issue:`38602`.) +(Contributed by Donghee Na in :issue:`38602`.) ftplib ------- :class:`~ftplib.FTP` and :class:`~ftplib.FTP_TLS` now raise a :class:`ValueError` if the given timeout for their constructor is zero to prevent the creation of -a non-blocking socket. (Contributed by Dong-hee Na in :issue:`39259`.) +a non-blocking socket. (Contributed by Donghee Na in :issue:`39259`.) gc -- @@ -468,7 +468,7 @@ http ---- HTTP status codes ``103 EARLY_HINTS``, ``418 IM_A_TEAPOT`` and ``425 TOO_EARLY`` are added to -:class:`http.HTTPStatus`. (Contributed by Dong-hee Na in :issue:`39509` and Ross Rhodes in :issue:`39507`.) +:class:`http.HTTPStatus`. (Contributed by Donghee Na in :issue:`39509` and Ross Rhodes in :issue:`39507`.) IDLE and idlelib ---------------- @@ -509,14 +509,14 @@ an optional *timeout* parameter for their constructors. Also, the :meth:`~imaplib.IMAP4.open` method now has an optional *timeout* parameter with this change. The overridden methods of :class:`~imaplib.IMAP4_SSL` and :class:`~imaplib.IMAP4_stream` were applied to this change. -(Contributed by Dong-hee Na in :issue:`38615`.) +(Contributed by Donghee Na in :issue:`38615`.) :meth:`imaplib.IMAP4.unselect` is added. :meth:`imaplib.IMAP4.unselect` frees server's resources associated with the selected mailbox and returns the server to the authenticated state. This command performs the same actions as :meth:`imaplib.IMAP4.close`, except that no messages are permanently removed from the currently -selected mailbox. (Contributed by Dong-hee Na in :issue:`40375`.) +selected mailbox. (Contributed by Donghee Na in :issue:`40375`.) importlib --------- @@ -588,13 +588,13 @@ nntplib :class:`~nntplib.NNTP` and :class:`~nntplib.NNTP_SSL` now raise a :class:`ValueError` if the given timeout for their constructor is zero to prevent the creation of -a non-blocking socket. (Contributed by Dong-hee Na in :issue:`39259`.) +a non-blocking socket. (Contributed by Donghee Na in :issue:`39259`.) os -- Added :const:`~os.CLD_KILLED` and :const:`~os.CLD_STOPPED` for :attr:`si_code`. -(Contributed by Dong-hee Na in :issue:`38493`.) +(Contributed by Donghee Na in :issue:`38493`.) Exposed the Linux-specific :func:`os.pidfd_open` (:issue:`38692`) and :const:`os.P_PIDFD` (:issue:`38713`) for process management with file @@ -629,7 +629,7 @@ poplib :class:`~poplib.POP3` and :class:`~poplib.POP3_SSL` now raise a :class:`ValueError` if the given timeout for their constructor is zero to prevent the creation of -a non-blocking socket. (Contributed by Dong-hee Na in :issue:`39259`.) +a non-blocking socket. (Contributed by Donghee Na in :issue:`39259`.) pprint ------ @@ -661,10 +661,10 @@ smtplib :class:`~smtplib.SMTP` and :class:`~smtplib.SMTP_SSL` now raise a :class:`ValueError` if the given timeout for their constructor is zero to prevent the creation of -a non-blocking socket. (Contributed by Dong-hee Na in :issue:`39259`.) +a non-blocking socket. (Contributed by Donghee Na in :issue:`39259`.) :class:`~smtplib.LMTP` constructor now has an optional *timeout* parameter. -(Contributed by Dong-hee Na in :issue:`39329`.) +(Contributed by Donghee Na in :issue:`39329`.) socket ------ @@ -777,7 +777,7 @@ Optimizations * A number of Python builtins (:class:`range`, :class:`tuple`, :class:`set`, :class:`frozenset`, :class:`list`, :class:`dict`) are now sped up by using :pep:`590` vectorcall protocol. - (Contributed by Dong-hee Na, Mark Shannon, Jeroen Demeyer and Petr Viktorin in :issue:`37207`.) + (Contributed by Donghee Na, Mark Shannon, Jeroen Demeyer and Petr Viktorin in :issue:`37207`.) * Optimized :func:`~set.difference_update` for the case when the other set is much larger than the base set. @@ -791,7 +791,7 @@ Optimizations * :term:`floor division` of float operation now has a better performance. Also the message of :exc:`ZeroDivisionError` for this operation is updated. - (Contributed by Dong-hee Na in :issue:`39434`.) + (Contributed by Donghee Na in :issue:`39434`.) * Decoding short ASCII strings with UTF-8 and ascii codecs is now about 15% faster. (Contributed by Inada Naoki in :issue:`37348`.) @@ -961,7 +961,7 @@ Removed are not supported or not enabled by NNTP server administrators. For ``xgtitle()``, please use :meth:`nntplib.NNTP.descriptions` or :meth:`nntplib.NNTP.description` instead. - (Contributed by Dong-hee Na in :issue:`39366`.) + (Contributed by Donghee Na in :issue:`39366`.) * :class:`array.array`: ``tostring()`` and ``fromstring()`` methods have been removed. They were aliases to ``tobytes()`` and ``frombytes()``, deprecated @@ -994,7 +994,7 @@ Removed * The :meth:`~threading.Thread.isAlive()` method of :class:`threading.Thread` has been removed. It was deprecated since Python 3.8. Use :meth:`~threading.Thread.is_alive()` instead. - (Contributed by Dong-hee Na in :issue:`37804`.) + (Contributed by Donghee Na in :issue:`37804`.) * Methods ``getchildren()`` and ``getiterator()`` of classes :class:`~xml.etree.ElementTree.ElementTree` and @@ -1315,7 +1315,7 @@ New Features * The :c:func:`PyModule_AddType` function is added to help adding a type to a module. - (Contributed by Dong-hee Na in :issue:`40024`.) + (Contributed by Donghee Na in :issue:`40024`.) * Added the functions :c:func:`PyObject_GC_IsTracked` and :c:func:`PyObject_GC_IsFinalized` to the public API to allow to query if diff --git a/Misc/ACKS b/Misc/ACKS index 4d2e8211bb023e..6eff2cc1f19e28 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1251,7 +1251,7 @@ R. David Murray Matti Mäki Jörg Müller Kaushik N -Dong-hee Na +Donghee Na Dale Nagata John Nagle Takahiro Nakayama diff --git a/Misc/NEWS.d/3.10.0a1.rst b/Misc/NEWS.d/3.10.0a1.rst index 2ef511c6e227e6..16f33637c88f73 100644 --- a/Misc/NEWS.d/3.10.0a1.rst +++ b/Misc/NEWS.d/3.10.0a1.rst @@ -68,7 +68,7 @@ getting the ``__bases__`` attribute leads to infinite recursion. .. section: Core and Builtins Speed up calls to ``reversed()`` by using the :pep:`590` ``vectorcall`` -calling convention. Patch by Dong-hee Na. +calling convention. Patch by Donghee Na. .. @@ -88,7 +88,7 @@ convention. Patch by Dennis Sweeney. .. section: Core and Builtins Speed up calls to ``bool()`` by using the :pep:`590` ``vectorcall`` calling -convention. Patch by Dong-hee Na. +convention. Patch by Donghee Na. .. @@ -715,7 +715,7 @@ Fix refleak in _Py_fopen_obj() when PySys_Audit() fails .. section: Core and Builtins Add a state to the :mod:`nis` module (:pep:`3121`) and apply the multiphase -initialization. Patch by Dong-hee Na. +initialization. Patch by Donghee Na. .. @@ -936,7 +936,7 @@ class. Patch by Pablo Galindo. .. section: Core and Builtins :c:func:`Py_TYPE()` is changed to the inline static function. Patch by -Dong-hee Na. +Donghee Na. .. @@ -2596,7 +2596,7 @@ remove multiple items from a list". .. section: Documentation Fix RemovedInSphinx40Warning when building the documentation. Patch by -Dong-hee Na. +Donghee Na. .. @@ -2862,7 +2862,7 @@ Make test_gdb properly run on HP-UX. Patch by Michael Osipov. .. section: Build Update :c:macro:`Py_UNREACHABLE` to use __builtin_unreachable() if only the -compiler is able to use it. Patch by Dong-hee Na. +compiler is able to use it. Patch by Donghee Na. .. diff --git a/Misc/NEWS.d/3.10.0a2.rst b/Misc/NEWS.d/3.10.0a2.rst index 9f85616c9f28dc..5930191635d131 100644 --- a/Misc/NEWS.d/3.10.0a2.rst +++ b/Misc/NEWS.d/3.10.0a2.rst @@ -185,7 +185,7 @@ Removed special methods ``__int__``, ``__float__``, ``__floordiv__``, Micro optimization when compute :c:member:`~PySequenceMethods.sq_item` and :c:member:`~PyMappingMethods.mp_subscript` of :class:`range`. Patch by -Dong-hee Na. +Donghee Na. .. @@ -205,7 +205,7 @@ error message using the current locale's encoding. .. nonce: iLoMVF .. section: Core and Builtins -Micro optimization for range.index if step is 1. Patch by Dong-hee Na. +Micro optimization for range.index if step is 1. Patch by Donghee Na. .. diff --git a/Misc/NEWS.d/3.10.0a3.rst b/Misc/NEWS.d/3.10.0a3.rst index 70b79f5f250a18..0e89984d4bc821 100644 --- a/Misc/NEWS.d/3.10.0a3.rst +++ b/Misc/NEWS.d/3.10.0a3.rst @@ -394,7 +394,7 @@ Removed the ``formatter`` module, which was deprecated in Python 3.4. It is somewhat obsolete, little used, and not tested. It was originally scheduled to be removed in Python 3.6, but such removals were delayed until after Python 2.7 EOL. Existing users should copy whatever classes they use into -their code. Patch by Dong-hee Na and and Terry J. Reedy. +their code. Patch by Donghee Na and and Terry J. Reedy. .. diff --git a/Misc/NEWS.d/3.10.0a4.rst b/Misc/NEWS.d/3.10.0a4.rst index 95f9319668db45..414823f162d85c 100644 --- a/Misc/NEWS.d/3.10.0a4.rst +++ b/Misc/NEWS.d/3.10.0a4.rst @@ -105,7 +105,7 @@ blocks .. section: Core and Builtins Make the :mod:`atexit` module state per-interpreter. It is now safe have -more than one :mod:`atexit` module instance. Patch by Dong-hee Na and Victor +more than one :mod:`atexit` module instance. Patch by Donghee Na and Victor Stinner. .. @@ -768,7 +768,7 @@ results. Patch by Ammar Askar. .. section: Tests Update test_nntplib to use official group name of news.aioe.org for testing. -Patch by Dong-hee Na. +Patch by Donghee Na. .. diff --git a/Misc/NEWS.d/3.10.0a6.rst b/Misc/NEWS.d/3.10.0a6.rst index 313aa689254040..c379b968c9885b 100644 --- a/Misc/NEWS.d/3.10.0a6.rst +++ b/Misc/NEWS.d/3.10.0a6.rst @@ -295,7 +295,7 @@ actual dictionary. This created problems for introspection tools. .. section: Library Added :const:`~os.O_EVTONLY`, :const:`~os.O_FSYNC`, :const:`~os.O_SYMLINK` and -:const:`~os.O_NOFOLLOW_ANY` for macOS. Patch by Dong-hee Na. +:const:`~os.O_NOFOLLOW_ANY` for macOS. Patch by Donghee Na. .. diff --git a/Misc/NEWS.d/3.10.0a7.rst b/Misc/NEWS.d/3.10.0a7.rst index 7933f71b01c14d..3a1694f444616a 100644 --- a/Misc/NEWS.d/3.10.0a7.rst +++ b/Misc/NEWS.d/3.10.0a7.rst @@ -113,7 +113,7 @@ in f-strings. Patch by Pablo Galindo. .. section: Core and Builtins Speed up calls to ``map()`` by using the :pep:`590` ``vectorcall`` calling -convention. Patch by Dong-hee Na. +convention. Patch by Donghee Na. .. @@ -240,7 +240,7 @@ of processes that don't use sigaltstack. .. section: Core and Builtins Speed up calls to ``filter()`` by using the :pep:`590` ``vectorcall`` -calling convention. Patch by Dong-hee Na. +calling convention. Patch by Donghee Na. .. diff --git a/Misc/NEWS.d/3.10.0b1.rst b/Misc/NEWS.d/3.10.0b1.rst index 4f33420c144c93..7b4680778ef6dc 100644 --- a/Misc/NEWS.d/3.10.0b1.rst +++ b/Misc/NEWS.d/3.10.0b1.rst @@ -516,7 +516,7 @@ encoding. .. section: Library Removed an unnecessary list comprehension before looping from -:func:`urllib.parse.parse_qsl`. Patch by Christoph Zwerschke and Dong-hee +:func:`urllib.parse.parse_qsl`. Patch by Christoph Zwerschke and Donghee Na. .. diff --git a/Misc/NEWS.d/3.11.0a1.rst b/Misc/NEWS.d/3.11.0a1.rst index 1fea35c42f26c6..fe21d9b5e66093 100644 --- a/Misc/NEWS.d/3.11.0a1.rst +++ b/Misc/NEWS.d/3.11.0a1.rst @@ -292,7 +292,7 @@ Fixed pickling of range iterators that iterated for over ``2**32`` times. .. section: Core and Builtins A :exc:`SyntaxError` is now raised when trying to delete :const:`__debug__`. -Patch by Dong-hee Na. +Patch by Donghee Na. .. @@ -415,7 +415,7 @@ type :class:`float` or :class:`complex`. .. section: Core and Builtins A debug variable :envvar:`PYTHONDUMPREFSFILE` is added for creating a dump -file which is generated by :option:`--with-trace-refs`. Patch by Dong-hee +file which is generated by :option:`--with-trace-refs`. Patch by Donghee Na. .. @@ -670,7 +670,7 @@ Parameter substitution of the union type with wrong types now raises .. section: Core and Builtins Update ``property_descr_set`` to use vectorcall if possible. Patch by -Dong-hee Na. +Donghee Na. .. @@ -732,7 +732,7 @@ Collapse union of equal types. E.g. the result of ``int | int`` is now On Windows, :func:`os.urandom`: uses BCryptGenRandom API instead of CryptGenRandom API which is deprecated from Microsoft Windows API. Patch by -Dong-hee Na. +Donghee Na. .. @@ -1657,7 +1657,7 @@ Patch by Hugo van Kemenade. .. section: Library Pure ASCII strings are now normalized in constant time by -:func:`unicodedata.normalize`. Patch by Dong-hee Na. +:func:`unicodedata.normalize`. Patch by Donghee Na. .. @@ -1968,7 +1968,7 @@ A new function ``operator.call`` has been added, such that :class:`webbrowser.MacOSX` is deprecated and will be removed in Python 3.13. It is untested and undocumented and also not used by webbrowser itself. -Patch by Dong-hee Na. +Patch by Donghee Na. .. @@ -2465,7 +2465,7 @@ generator .. section: Library Make the implementation consistency of :func:`~operator.indexOf` between C -and Python versions. Patch by Dong-hee Na. +and Python versions. Patch by Donghee Na. .. @@ -2752,7 +2752,7 @@ of reserved filenames, including those with trailing spaces or colons. .. section: Library Fix :meth:`~email.message.MIMEPart.as_string` to pass unixfrom properly. -Patch by Dong-hee Na. +Patch by Donghee Na. .. @@ -2809,7 +2809,7 @@ behaves differently than the similar implementation in :mod:`sysconfig`. .. section: Library :class:`smtpd.MailmanProxy` is now removed as it is unusable without an -external module, ``mailman``. Patch by Dong-hee Na. +external module, ``mailman``. Patch by Donghee Na. .. @@ -2916,7 +2916,7 @@ Support PEP 515 for Fraction's initialization from string. .. nonce: qFBYpp .. section: Library -Remove deprecated functions in the :mod:`gettext`. Patch by Dong-hee Na. +Remove deprecated functions in the :mod:`gettext`. Patch by Donghee Na. .. @@ -4471,7 +4471,7 @@ and modify the frozen modules. .. section: Build Add support for building with clang thin lto via --with-lto=thin/full. Patch -by Dong-hee Na and Brett Holman. +by Donghee Na and Brett Holman. .. @@ -4798,7 +4798,7 @@ Allow the Argument Clinic tool to handle ``__complex__`` special methods. Removed the 'test2to3' demo project that demonstrated using lib2to3 to support Python 2.x and Python 3.x from a single source in a distutils -package. Patch by Dong-hee Na +package. Patch by Donghee Na .. diff --git a/Misc/NEWS.d/3.11.0a2.rst b/Misc/NEWS.d/3.11.0a2.rst index 8ae8847d846b12..d4ae7dc30b04b1 100644 --- a/Misc/NEWS.d/3.11.0a2.rst +++ b/Misc/NEWS.d/3.11.0a2.rst @@ -142,7 +142,7 @@ Add SipHash13 for string hash algorithm and use it by default. .. nonce: CTUT8s .. section: Core and Builtins -Fix reference leak from descr_check. Patch by Dong-hee Na. +Fix reference leak from descr_check. Patch by Donghee Na. .. @@ -263,7 +263,7 @@ Improve the generated bytecode for class and mapping patterns. .. section: Core and Builtins Speed up calls to ``enumerate()`` by using the :pep:`590` ``vectorcall`` -calling convention. Patch by Dong-hee Na. +calling convention. Patch by Donghee Na. .. @@ -396,7 +396,7 @@ Patch by Inada Naoki. .. section: Library Update :class:`~typing.ForwardRef` to support ``|`` operator. Patch by -Dong-hee Na. +Donghee Na. .. @@ -486,7 +486,7 @@ Patch by Joongi Kim. .. section: Library Empty escapechar/quotechar is not allowed when initializing -:class:`csv.Dialect`. Patch by Vajrasky Kok and Dong-hee Na. +:class:`csv.Dialect`. Patch by Vajrasky Kok and Donghee Na. .. @@ -569,7 +569,7 @@ formatting options. .. section: Library Improve error message of :class:`csv.Dialect` when initializing. Patch by -Vajrasky Kok and Dong-hee Na. +Vajrasky Kok and Donghee Na. .. diff --git a/Misc/NEWS.d/3.11.0a3.rst b/Misc/NEWS.d/3.11.0a3.rst index 426531904993a9..41a3a4b94c4f3d 100644 --- a/Misc/NEWS.d/3.11.0a3.rst +++ b/Misc/NEWS.d/3.11.0a3.rst @@ -615,7 +615,7 @@ Launch GNOME web browsers via gio tool instead of obsolete gvfs-open .. section: Library On Windows, :func:`time.sleep` now uses a waitable timer which supports -high-resolution timers. Patch by Dong-hee Na and Eryk Sun. +high-resolution timers. Patch by Donghee Na and Eryk Sun. .. diff --git a/Misc/NEWS.d/3.11.0a5.rst b/Misc/NEWS.d/3.11.0a5.rst index c28078da8d8339..08d94e82ed8ccf 100644 --- a/Misc/NEWS.d/3.11.0a5.rst +++ b/Misc/NEWS.d/3.11.0a5.rst @@ -127,7 +127,7 @@ Aditya. .. section: Core and Builtins Speed up calls to :meth:`weakref.ref.__call__` by using the :pep:`590` -``vectorcall`` calling convention. Patch by Dong-hee Na. +``vectorcall`` calling convention. Patch by Donghee Na. .. diff --git a/Misc/NEWS.d/3.11.0a6.rst b/Misc/NEWS.d/3.11.0a6.rst index fcec71c6f59da2..52055b3fafd485 100644 --- a/Misc/NEWS.d/3.11.0a6.rst +++ b/Misc/NEWS.d/3.11.0a6.rst @@ -382,7 +382,7 @@ involving lots of brackets. Patch by Pablo Galindo. .. section: Core and Builtins :mod:`ctypes` now allocates memory on the stack instead of on the heap to -pass arguments while calling a Python callback function. Patch by Dong-hee +pass arguments while calling a Python callback function. Patch by Donghee Na. .. @@ -441,7 +441,7 @@ Add a missing call to ``va_end()`` in ``Modules/_hashopenssl.c``. .. section: Core and Builtins Use :c:func:`PyObject_Vectorcall` while calling ctypes callback function. -Patch by Dong-hee Na. +Patch by Donghee Na. .. @@ -514,7 +514,7 @@ For performance, use the optimized string-searching implementations from .. section: Library :class:`~http.server.SimpleHTTPRequestHandler` now uses HTML5 grammar. Patch -by Dong-hee Na. +by Donghee Na. .. diff --git a/Misc/NEWS.d/3.11.0a7.rst b/Misc/NEWS.d/3.11.0a7.rst index 2172c02e2fc847..20f2aa3c56ae2e 100644 --- a/Misc/NEWS.d/3.11.0a7.rst +++ b/Misc/NEWS.d/3.11.0a7.rst @@ -89,7 +89,7 @@ problem. Define :c:macro:`PY_CALL_TRAMPOLINE` to enable call trampolines. .. section: Core and Builtins Some Windows system error codes(>= 10000) are now mapped into the correct -errno and may now raise a subclass of :exc:`OSError`. Patch by Dong-hee Na. +errno and may now raise a subclass of :exc:`OSError`. Patch by Donghee Na. .. @@ -1599,7 +1599,7 @@ Call the public :func:`sys.get_asyncgen_hooks` and .. section: C API Remove private functions ``_PySys_GetObjectId()`` and -``_PySys_SetObjectId()``. Patch by Dong-hee Na. +``_PySys_SetObjectId()``. Patch by Donghee Na. .. diff --git a/Misc/NEWS.d/3.11.0b1.rst b/Misc/NEWS.d/3.11.0b1.rst index 1da722b21680ee..c663ca8de98ca2 100644 --- a/Misc/NEWS.d/3.11.0b1.rst +++ b/Misc/NEWS.d/3.11.0b1.rst @@ -185,7 +185,7 @@ functions leave the current exception unchanged. Patch by Victor Stinner. .. section: Core and Builtins Fix a minor memory leak at exit: release the memory of the -:class:`generic_alias_iterator` type. Patch by Dong-hee Na. +:class:`generic_alias_iterator` type. Patch by Donghee Na. .. diff --git a/Misc/NEWS.d/3.11.0b4.rst b/Misc/NEWS.d/3.11.0b4.rst index 8340b7686ff2bb..8155d91a283fc2 100644 --- a/Misc/NEWS.d/3.11.0b4.rst +++ b/Misc/NEWS.d/3.11.0b4.rst @@ -699,7 +699,7 @@ Now such ranges are interpreted as empty ranges. Now :func:`~dis.dis` and :func:`~dis.get_instructions` handle operand values for instructions prefixed by ``EXTENDED_ARG_QUICK``. Patch by Sam Gross and -Dong-hee Na. +Donghee Na. .. diff --git a/Misc/NEWS.d/3.11.1.rst b/Misc/NEWS.d/3.11.1.rst index 62cf53afe507c9..5c4934f4c29dd0 100644 --- a/Misc/NEWS.d/3.11.1.rst +++ b/Misc/NEWS.d/3.11.1.rst @@ -179,7 +179,7 @@ back to alternative names ("python", "python."). .. section: Core and Builtins Update :mod:`faulthandler` to emit an error message with the proper -unexpected signal number. Patch by Dong-hee Na. +unexpected signal number. Patch by Donghee Na. .. @@ -250,7 +250,7 @@ raising a :exc:`TypeError` when accessed via an instance of an invalid type. .. nonce: eOBh8M .. section: Core and Builtins -Suppress ImportError for invalid query for help() command. Patch by Dong-hee +Suppress ImportError for invalid query for help() command. Patch by Donghee Na. .. @@ -281,7 +281,7 @@ errors from :mod:`ctypes` calls. .. section: Core and Builtins :func:`os.sched_yield` now release the GIL while calling sched_yield(2). -Patch by Dong-hee Na. +Patch by Donghee Na. .. @@ -1289,7 +1289,7 @@ if :option:`--with-system-expat` is passed to :program:`!configure`. .. section: Build Fix the build process of clang compiler for :program:`_bootstrap_python` if -LTO optimization is applied. Patch by Matthias Görgens and Dong-hee Na. +LTO optimization is applied. Patch by Matthias Görgens and Donghee Na. .. diff --git a/Misc/NEWS.d/3.11.2.rst b/Misc/NEWS.d/3.11.2.rst index 3f4ff595501caa..bdc1d2f6bdc357 100644 --- a/Misc/NEWS.d/3.11.2.rst +++ b/Misc/NEWS.d/3.11.2.rst @@ -15,7 +15,7 @@ a reference leak in that function. .. section: Core and Builtins Fix wrong lineno in exception message on :keyword:`continue` or -:keyword:`break` which are not in a loop. Patch by Dong-hee Na. +:keyword:`break` which are not in a loop. Patch by Donghee Na. .. @@ -25,7 +25,7 @@ Fix wrong lineno in exception message on :keyword:`continue` or .. section: Core and Builtins Fix :func:`~unicodedata.is_normalized` to properly handle the UCD 3.2.0 -cases. Patch by Dong-hee Na. +cases. Patch by Donghee Na. .. @@ -362,7 +362,7 @@ Fix ``tuple`` subclasses being cast to ``tuple`` when used as enum values. .. section: Library Update :exc:`~urllib.error.HTTPError` to be initialized properly, even if -the ``fp`` is ``None``. Patch by Dong-hee Na. +the ``fp`` is ``None``. Patch by Donghee Na. .. diff --git a/Misc/NEWS.d/3.11.4.rst b/Misc/NEWS.d/3.11.4.rst index 71ed43d544f552..cfd57607170ddd 100644 --- a/Misc/NEWS.d/3.11.4.rst +++ b/Misc/NEWS.d/3.11.4.rst @@ -147,7 +147,7 @@ Fix bug in line numbers of instructions emitted for :keyword:`except* .. section: Core and Builtins Migrate :meth:`~ssl.SSLContext.set_ecdh_curve` method not to use deprecated -OpenSSL APIs. Patch by Dong-hee Na. +OpenSSL APIs. Patch by Donghee Na. .. @@ -669,7 +669,7 @@ linking with ``libbsd``. .. nonce: -W9BJS .. section: Build -Add gcc fallback of mkfifoat/mknodat for macOS. Patch by Dong-hee Na. +Add gcc fallback of mkfifoat/mknodat for macOS. Patch by Donghee Na. .. diff --git a/Misc/NEWS.d/3.11.5.rst b/Misc/NEWS.d/3.11.5.rst index 575e34e58a00f5..502e0ccfd68c46 100644 --- a/Misc/NEWS.d/3.11.5.rst +++ b/Misc/NEWS.d/3.11.5.rst @@ -161,7 +161,7 @@ pure Python version did the set up in ``__init__``. .. section: Library Fix :func:`multiprocessing.set_forkserver_preload` to check the given list -of modules names. Patch by Dong-hee Na. +of modules names. Patch by Donghee Na. .. diff --git a/Misc/NEWS.d/3.5.4.rst b/Misc/NEWS.d/3.5.4.rst index cd0ca4872f1ab0..7839fa2709ecf2 100644 --- a/Misc/NEWS.d/3.5.4.rst +++ b/Misc/NEWS.d/3.5.4.rst @@ -5,4 +5,4 @@ .. section: Library ftplib.FTP.putline() now throws ValueError on commands that contains CR or -LF. Patch by Dong-hee Na. +LF. Patch by Donghee Na. diff --git a/Misc/NEWS.d/3.5.4rc1.rst b/Misc/NEWS.d/3.5.4rc1.rst index 04a035a41e7461..d65d5d14ee78bb 100644 --- a/Misc/NEWS.d/3.5.4rc1.rst +++ b/Misc/NEWS.d/3.5.4rc1.rst @@ -340,7 +340,7 @@ not keep objects alive longer than expected. .. section: Library inspect.signature() now supports callables with variable-argument parameters -wrapped with partialmethod. Patch by Dong-hee Na. +wrapped with partialmethod. Patch by Donghee Na. .. diff --git a/Misc/NEWS.d/3.6.2rc1.rst b/Misc/NEWS.d/3.6.2rc1.rst index cdf4c3d541c4ca..28eb88f79130c5 100644 --- a/Misc/NEWS.d/3.6.2rc1.rst +++ b/Misc/NEWS.d/3.6.2rc1.rst @@ -77,7 +77,7 @@ delivered to the innermost frame. .. section: Core and Builtins sys.getsizeof() on a code object now returns the sizes which includes the -code struct and sizes of objects which it references. Patch by Dong-hee Na. +code struct and sizes of objects which it references. Patch by Donghee Na. .. @@ -163,7 +163,7 @@ no longer ignored. Patch by Mircea Cosbuc. .. nonce: I2mDTz .. section: Library -Functional API of enum allows to create empty enums. Patched by Dong-hee Na +Functional API of enum allows to create empty enums. Patched by Donghee Na .. @@ -202,7 +202,7 @@ not keep objects alive longer than expected. .. section: Library inspect.signature() now supports callables with variable-argument parameters -wrapped with partialmethod. Patch by Dong-hee Na. +wrapped with partialmethod. Patch by Donghee Na. .. diff --git a/Misc/NEWS.d/3.6.3rc1.rst b/Misc/NEWS.d/3.6.3rc1.rst index 4dc2eef5d3b61b..4b2aae9dc88441 100644 --- a/Misc/NEWS.d/3.6.3rc1.rst +++ b/Misc/NEWS.d/3.6.3rc1.rst @@ -506,7 +506,7 @@ Fix handling of long oids in ssl. Based on patch by Christian Heimes. .. section: Library ftplib.FTP.putline() now throws ValueError on commands that contains CR or -LF. Patch by Dong-hee Na. +LF. Patch by Donghee Na. .. diff --git a/Misc/NEWS.d/3.6.6rc1.rst b/Misc/NEWS.d/3.6.6rc1.rst index 71a5c3ec595ba2..9624195c79043b 100644 --- a/Misc/NEWS.d/3.6.6rc1.rst +++ b/Misc/NEWS.d/3.6.6rc1.rst @@ -289,7 +289,7 @@ literals on pydoc. Patch by Andrés Delfino. .. section: Library Update error message when constructing invalid inspect.Parameters Patch by -Dong-hee Na. +Donghee Na. .. diff --git a/Misc/NEWS.d/3.7.0a1.rst b/Misc/NEWS.d/3.7.0a1.rst index 2a851fad5fb2e9..d2f0a93ccf15c1 100644 --- a/Misc/NEWS.d/3.7.0a1.rst +++ b/Misc/NEWS.d/3.7.0a1.rst @@ -529,7 +529,7 @@ name are now supported. .. section: Core and Builtins sys.getsizeof() on a code object now returns the sizes which includes the -code struct and sizes of objects which it references. Patch by Dong-hee Na. +code struct and sizes of objects which it references. Patch by Donghee Na. .. @@ -2260,7 +2260,7 @@ Update zlib to 1.2.11. .. section: Library ftplib.FTP.putline() now throws ValueError on commands that contains CR or -LF. Patch by Dong-hee Na. +LF. Patch by Donghee Na. .. @@ -2329,7 +2329,7 @@ always return bytes. .. nonce: I2mDTz .. section: Library -Functional API of enum allows to create empty enums. Patched by Dong-hee Na +Functional API of enum allows to create empty enums. Patched by Donghee Na .. @@ -2612,7 +2612,7 @@ Fix handling escape characters in HZ codec. Based on patch by Ma Lin. .. section: Library inspect.signature() now supports callables with variable-argument parameters -wrapped with partialmethod. Patch by Dong-hee Na. +wrapped with partialmethod. Patch by Donghee Na. .. diff --git a/Misc/NEWS.d/3.7.0a3.rst b/Misc/NEWS.d/3.7.0a3.rst index 92b0f328851208..69db9fcd1c8228 100644 --- a/Misc/NEWS.d/3.7.0a3.rst +++ b/Misc/NEWS.d/3.7.0a3.rst @@ -539,7 +539,7 @@ optional .. section: Library Updates 2to3 to convert from operator.isCallable(obj) to callable(obj). -Patch by Dong-hee Na. +Patch by Donghee Na. .. @@ -549,7 +549,7 @@ Patch by Dong-hee Na. .. section: Library inspect.signature should follow :pep:`8`, if the parameter has an annotation -and a default value. Patch by Dong-hee Na. +and a default value. Patch by Donghee Na. .. diff --git a/Misc/NEWS.d/3.7.0b5.rst b/Misc/NEWS.d/3.7.0b5.rst index 20476993b9652a..fb29109869188b 100644 --- a/Misc/NEWS.d/3.7.0b5.rst +++ b/Misc/NEWS.d/3.7.0b5.rst @@ -418,7 +418,7 @@ trigger a ``DeprecationWarning`` and have been marked for removal in Python .. section: Library Update error message when constructing invalid inspect.Parameters Patch by -Dong-hee Na. +Donghee Na. .. diff --git a/Misc/NEWS.d/3.8.0a1.rst b/Misc/NEWS.d/3.8.0a1.rst index 85a5447ceb5f28..f0518bde996fd2 100644 --- a/Misc/NEWS.d/3.8.0a1.rst +++ b/Misc/NEWS.d/3.8.0a1.rst @@ -1965,7 +1965,7 @@ result of an internal future if it's already done. .. section: Library Add a deprecated warning for the :meth:`threading.Thread.isAlive` method. -Patch by Dong-hee Na. +Patch by Donghee Na. .. @@ -4974,7 +4974,7 @@ Enum members. .. section: Library Update error message when constructing invalid inspect.Parameters Patch by -Dong-hee Na. +Donghee Na. .. diff --git a/Misc/NEWS.d/3.9.0a1.rst b/Misc/NEWS.d/3.9.0a1.rst index 41ab33fac3322d..d18ed70841e0c5 100644 --- a/Misc/NEWS.d/3.9.0a1.rst +++ b/Misc/NEWS.d/3.9.0a1.rst @@ -33,7 +33,7 @@ Fixes audit event for :func:`os.system` to be named ``os.system``. .. section: Security Escape the server title of :class:`xmlrpc.server.DocXMLRPCServer` when -rendering the document page as HTML. (Contributed by Dong-hee Na in +rendering the document page as HTML. (Contributed by Donghee Na in :issue:`38243`.) .. @@ -203,7 +203,7 @@ arguments in decorators. .. section: Core and Builtins Fix a segmentation fault when using reverse iterators of empty ``dict`` -objects. Patch by Dong-hee Na and Inada Naoki. +objects. Patch by Donghee Na and Inada Naoki. .. @@ -280,7 +280,7 @@ visited by ``tp_traverse()`` are valid. .. section: Core and Builtins Remove unnecessary intersection and update set operation in dictview with -empty set. (Contributed by Dong-hee Na in :issue:`38210`.) +empty set. (Contributed by Donghee Na in :issue:`38210`.) .. @@ -1194,7 +1194,7 @@ Expose the Linux ``pidfd_open`` syscall as :func:`os.pidfd_open`. .. section: Library Added constants :const:`~fcntl.F_OFD_GETLK`, :const:`~fcntl.F_OFD_SETLK` and -:const:`~fcntl.F_OFD_SETLKW` to the :mod:`fcntl` module. Patch by Dong-hee +:const:`~fcntl.F_OFD_SETLKW` to the :mod:`fcntl` module. Patch by Donghee Na. .. @@ -1284,7 +1284,7 @@ Fixed erroneous equality comparison in statistics.NormalDist(). .. section: Library Added :const:`~os.CLD_KILLED` and :const:`~os.CLD_STOPPED` for -:attr:`si_code`. Patch by Dong-hee Na. +:attr:`si_code`. Patch by Donghee Na. .. @@ -1882,7 +1882,7 @@ avoid dynamic lookup. .. section: Library Update :class:`importlib.machinery.BuiltinImporter` to use -``loader._ORIGIN`` instead of a hardcoded value. Patch by Dong-hee Na. +``loader._ORIGIN`` instead of a hardcoded value. Patch by Donghee Na. .. @@ -2080,7 +2080,7 @@ method which emits a deprecation warning and calls corresponding methody .. section: Library Update test_statistics.py to verify that the statistics module works well -for both C and Python implementations. Patch by Dong-hee Na +for both C and Python implementations. Patch by Donghee Na .. @@ -2201,7 +2201,7 @@ uses more than ``SIGSTKSZ`` bytes of stack memory on some platforms. .. nonce: AmXrik .. section: Library -Add C fastpath for statistics.NormalDist.inv_cdf() Patch by Dong-hee Na +Add C fastpath for statistics.NormalDist.inv_cdf() Patch by Donghee Na .. @@ -2210,7 +2210,7 @@ Add C fastpath for statistics.NormalDist.inv_cdf() Patch by Dong-hee Na .. nonce: Ene6L- .. section: Library -Remove the deprecated method `threading.Thread.isAlive()`. Patch by Dong-hee +Remove the deprecated method `threading.Thread.isAlive()`. Patch by Donghee Na. .. @@ -4089,7 +4089,7 @@ Increase code coverage for multiprocessing.shared_memory. .. nonce: Kl1sti .. section: Tests -Add tests for json.dump(..., skipkeys=True). Patch by Dong-hee Na. +Add tests for json.dump(..., skipkeys=True). Patch by Donghee Na. .. diff --git a/Misc/NEWS.d/3.9.0a3.rst b/Misc/NEWS.d/3.9.0a3.rst index 54b61ca3b7785f..a56573dd2c3d95 100644 --- a/Misc/NEWS.d/3.9.0a3.rst +++ b/Misc/NEWS.d/3.9.0a3.rst @@ -149,7 +149,7 @@ argument - by Anthony Sottile. .. section: Core and Builtins Correct the error message when calling the :func:`min` or :func:`max` with -no arguments. Patch by Dong-hee Na. +no arguments. Patch by Donghee Na. .. @@ -392,7 +392,7 @@ Remove ``fractions.gcd()`` function, deprecated since Python 3.5 .. section: Library :class:`~smtplib.LMTP` constructor now has an optional *timeout* parameter. -Patch by Dong-hee Na. +Patch by Donghee Na. .. @@ -414,7 +414,7 @@ Taskaya. :class:`~ftplib.FTP_TLS` and :class:`~ftplib.FTP_TLS` now raise a :class:`ValueError` if the given timeout for their constructor is zero to -prevent the creation of a non-blocking socket. Patch by Dong-hee Na. +prevent the creation of a non-blocking socket. Patch by Donghee Na. .. @@ -425,7 +425,7 @@ prevent the creation of a non-blocking socket. Patch by Dong-hee Na. :class:`~smtplib.SMTP` and :class:`~smtplib.SMTP_SSL` now raise a :class:`ValueError` if the given timeout for their constructor is zero to -prevent the creation of a non-blocking socket. Patch by Dong-hee Na. +prevent the creation of a non-blocking socket. Patch by Donghee Na. .. @@ -456,7 +456,7 @@ resilients to inaccessible sys.path entries (importlib_metadata v1.4.0). :class:`~nntplib.NNTP` and :class:`~nntplib.NNTP_SSL` now raise a :class:`ValueError` if the given timeout for their constructor is zero to -prevent the creation of a non-blocking socket. Patch by Dong-hee Na. +prevent the creation of a non-blocking socket. Patch by Donghee Na. .. @@ -488,7 +488,7 @@ towards *y*. :class:`~poplib.POP3` and :class:`~poplib.POP3_SSL` now raise a :class:`ValueError` if the given timeout for their constructor is zero to -prevent the creation of a non-blocking socket. Patch by Dong-hee Na. +prevent the creation of a non-blocking socket. Patch by Donghee Na. .. @@ -571,7 +571,7 @@ new task spawning before exception raising. .. section: Library Correctly parenthesize filter-based statements that contain lambda -expressions in mod:`lib2to3`. Patch by Dong-hee Na. +expressions in mod:`lib2to3`. Patch by Donghee Na. .. @@ -699,7 +699,7 @@ upon inheritance. Patch by Bar Harel. :meth:`~imaplib.IMAP4.open` method now has an optional *timeout* parameter with this change. The overridden methods of :class:`~imaplib.IMAP4_SSL` and :class:`~imaplib.IMAP4_stream` were applied to this change. Patch by -Dong-hee Na. +Donghee Na. .. diff --git a/Misc/NEWS.d/3.9.0a4.rst b/Misc/NEWS.d/3.9.0a4.rst index 019b34c4082d10..e59435b5509acf 100644 --- a/Misc/NEWS.d/3.9.0a4.rst +++ b/Misc/NEWS.d/3.9.0a4.rst @@ -43,7 +43,7 @@ first item. Patch by Yonatan Goldschmidt. .. nonce: BIIX2M .. section: Core and Builtins -Update clinic tool to use :c:func:`Py_IS_TYPE`. Patch by Dong-hee Na. +Update clinic tool to use :c:func:`Py_IS_TYPE`. Patch by Donghee Na. .. @@ -141,7 +141,7 @@ collection of deleted, pickled objects. .. section: Core and Builtins Fixed a possible crash in :meth:`list.__contains__` when a list is changed -during comparing items. Patch by Dong-hee Na. +during comparing items. Patch by Donghee Na. .. @@ -152,7 +152,7 @@ during comparing items. Patch by Dong-hee Na. :term:`floor division` of float operation now has a better performance. Also the message of :exc:`ZeroDivisionError` for this operation is updated. Patch -by Dong-hee Na. +by Donghee Na. .. diff --git a/Misc/NEWS.d/3.9.0a5.rst b/Misc/NEWS.d/3.9.0a5.rst index 8c6057aa3c8a4b..e9d3358f0bfae3 100644 --- a/Misc/NEWS.d/3.9.0a5.rst +++ b/Misc/NEWS.d/3.9.0a5.rst @@ -96,7 +96,7 @@ Port itertools module to multiphase initialization (:pep:`489`). .. section: Core and Builtins Speed up calls to ``frozenset()`` by using the :pep:`590` ``vectorcall`` -calling convention. Patch by Dong-hee Na. +calling convention. Patch by Donghee Na. .. @@ -117,7 +117,7 @@ own variable. .. section: Core and Builtins Speed up calls to ``set()`` by using the :pep:`590` ``vectorcall`` calling -convention. Patch by Dong-hee Na. +convention. Patch by Donghee Na. .. @@ -166,7 +166,7 @@ Allow executing asynchronous comprehensions on the top level when the .. section: Core and Builtins Speed up calls to ``tuple()`` by using the :pep:`590` ``vectorcall`` calling -convention. Patch by Dong-hee Na. +convention. Patch by Donghee Na. .. @@ -571,7 +571,7 @@ Fixed :func:`ast.unparse` for extended slices containing a single element .. nonce: yWq9NJ .. section: Library -Fix :mod:`json.tool` to catch :exc:`BrokenPipeError`. Patch by Dong-hee Na. +Fix :mod:`json.tool` to catch :exc:`BrokenPipeError`. Patch by Donghee Na. .. @@ -783,7 +783,7 @@ when the optional ``qop`` parameter is not present. .. section: Library HTTP status codes ``103 EARLY_HINTS`` and ``425 TOO_EARLY`` are added to -:class:`http.HTTPStatus`. Patch by Dong-hee Na. +:class:`http.HTTPStatus`. Patch by Donghee Na. .. @@ -1133,7 +1133,7 @@ module. Patch by José Roberto Meza Cabrera. .. section: C API Add :c:func:`PyModule_AddType` helper function: add a type to a module. -Patch by Dong-hee Na. +Patch by Donghee Na. .. @@ -1163,7 +1163,7 @@ Python thread state. .. nonce: R3jaTy .. section: C API -Add _PyArg_NoKwnames helper function. Patch by Dong-hee Na. +Add _PyArg_NoKwnames helper function. Patch by Donghee Na. .. diff --git a/Misc/NEWS.d/3.9.0b1.rst b/Misc/NEWS.d/3.9.0b1.rst index a7f52f81a5cd3a..49418640093a0d 100644 --- a/Misc/NEWS.d/3.9.0b1.rst +++ b/Misc/NEWS.d/3.9.0b1.rst @@ -490,7 +490,7 @@ The first argument of :func:`pickle.loads` is now positional-only. .. section: Library Update :mod:`nntplib` to merge :class:`nntplib.NNTP` and -:class:`nntplib._NNTPBase`. Patch by Dong-hee Na. +:class:`nntplib._NNTPBase`. Patch by Donghee Na. .. @@ -500,7 +500,7 @@ Update :mod:`nntplib` to merge :class:`nntplib.NNTP` and .. section: Library Update :mod:`dbm.gnu` to use gdbm_count if possible when calling -:func:`len`. Patch by Dong-hee Na. +:func:`len`. Patch by Donghee Na. .. @@ -592,7 +592,7 @@ subdirectories in package data, matching backport in importlib_resources .. nonce: 5GuK2A .. section: Library -:meth:`imaplib.IMAP4.unselect` is added. Patch by Dong-hee Na. +:meth:`imaplib.IMAP4.unselect` is added. Patch by Donghee Na. .. From 4ec7c7e8452f693584530918a6e60a9038f123ad Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 22 Sep 2023 21:19:22 -0700 Subject: [PATCH 345/632] [3.11] GH-95913: Add the release date for Python 3.11 (GH-109750) (#109772) GH-95913: Add the release date for Python 3.11 (GH-109750) (cherry picked from commit b10de68c6ceae1076cdc98c890b9802dc81a7f44) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/whatsnew/3.11.rst | 2 +- Doc/whatsnew/3.9.rst | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index edaa9e34c481d5..175b9e5b2ab5f7 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -45,7 +45,7 @@ when researching a change. This article explains the new features in Python 3.11, compared to 3.10. - +Python 3.11 was released on October 24, 2022. For full details, see the :ref:`changelog `. diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index fb73eaf59d5dac..6e6961e48c4ee8 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -44,7 +44,6 @@ This article explains the new features in Python 3.9, compared to 3.8. Python 3.9 was released on October 5, 2020. - For full details, see the :ref:`changelog `. .. seealso:: From e6a9cbd1147a67d9128f5fa3e82f8f0ee77dee93 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 24 Sep 2023 03:02:07 -0700 Subject: [PATCH 346/632] [3.11] gh-101100: Fix sphinx warnings in `Doc/library/xml.etree.elementtree.rst` (GH-109799) (#109801) gh-101100: Fix sphinx warnings in `Doc/library/xml.etree.elementtree.rst` (GH-109799) (cherry picked from commit 649768fb6781ba810df44017fee1975a11d65e2f) gh-101100: Fix shpinx warnings in `Doc/library/xml.etree.elementtree.rst` Co-authored-by: Nikita Sobolev --- Doc/library/xml.etree.elementtree.rst | 7 ++++++- Doc/tools/.nitignore | 1 - 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Doc/library/xml.etree.elementtree.rst b/Doc/library/xml.etree.elementtree.rst index 2fe0d2e082fb3a..76c5b89ac66316 100644 --- a/Doc/library/xml.etree.elementtree.rst +++ b/Doc/library/xml.etree.elementtree.rst @@ -17,7 +17,7 @@ for parsing and creating XML data. This module will use a fast implementation whenever available. .. deprecated:: 3.3 - The :mod:`xml.etree.cElementTree` module is deprecated. + The :mod:`!xml.etree.cElementTree` module is deprecated. .. warning:: @@ -825,6 +825,8 @@ Reference Functions ^^^^^^^^^ +.. module:: xml.etree.ElementInclude + .. function:: xml.etree.ElementInclude.default_loader( href, parse, encoding=None) :module: @@ -862,6 +864,9 @@ Functions Element Objects ^^^^^^^^^^^^^^^ +.. module:: xml.etree.ElementTree + :noindex: + .. class:: Element(tag, attrib={}, **extra) Element class. This class defines the Element interface, and provides a diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index f9494ff51165b1..149e19202a5f86 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -141,7 +141,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.etree.elementtree.rst Doc/library/xml.rst Doc/library/xml.sax.handler.rst Doc/library/xml.sax.reader.rst From 97ea90194c478ac9214ce827868ea0e517f700e0 Mon Sep 17 00:00:00 2001 From: elfstrom Date: Sun, 24 Sep 2023 21:28:03 +0200 Subject: [PATCH 347/632] [3.11] gh-105829: Fix concurrent.futures.ProcessPoolExecutor deadlock (GH-108513) (#109783) This fixes issue GH-105829, https://github.com/python/cpython/issues/105829 (cherry picked from commit 405b06375a8a4cdb08ff53afade09a8b66ec23d5) --- Lib/concurrent/futures/process.py | 18 ++++- .../test_concurrent_futures/test_deadlock.py | 72 ++++++++++++++++++- ...-08-26-12-35-39.gh-issue-105829.kyYhWI.rst | 1 + 3 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-08-26-12-35-39.gh-issue-105829.kyYhWI.rst diff --git a/Lib/concurrent/futures/process.py b/Lib/concurrent/futures/process.py index 7a069895d479ee..30a7ac8e6fe886 100644 --- a/Lib/concurrent/futures/process.py +++ b/Lib/concurrent/futures/process.py @@ -69,6 +69,11 @@ def __init__(self): self._reader, self._writer = mp.Pipe(duplex=False) def close(self): + # Please note that we do not take the shutdown lock when + # calling clear() (to avoid deadlocking) so this method can + # only be called safely from the same thread as all calls to + # clear() even if you hold the shutdown lock. Otherwise we + # might try to read from the closed pipe. if not self._closed: self._closed = True self._writer.close() @@ -424,8 +429,12 @@ def wait_result_broken_or_wakeup(self): elif wakeup_reader in ready: is_broken = False - with self.shutdown_lock: - self.thread_wakeup.clear() + # No need to hold the _shutdown_lock here because: + # 1. we're the only thread to use the wakeup reader + # 2. we're also the only thread to call thread_wakeup.close() + # 3. we want to avoid a possible deadlock when both reader and writer + # would block (gh-105829) + self.thread_wakeup.clear() return result_item, is_broken, cause @@ -708,7 +717,10 @@ def __init__(self, max_workers=None, mp_context=None, # as it could result in a deadlock if a worker process dies with the # _result_queue write lock still acquired. # - # _shutdown_lock must be locked to access _ThreadWakeup. + # _shutdown_lock must be locked to access _ThreadWakeup.close() and + # .wakeup(). Care must also be taken to not call clear or close from + # more than one thread since _ThreadWakeup.clear() is not protected by + # the _shutdown_lock self._executor_manager_thread_wakeup = _ThreadWakeup() # Create communication channels for the executor diff --git a/Lib/test/test_concurrent_futures/test_deadlock.py b/Lib/test/test_concurrent_futures/test_deadlock.py index d128cf2374fc21..2f08bf84698885 100644 --- a/Lib/test/test_concurrent_futures/test_deadlock.py +++ b/Lib/test/test_concurrent_futures/test_deadlock.py @@ -1,10 +1,13 @@ import contextlib +import queue +import signal import sys import time import unittest +import unittest.mock from pickle import PicklingError from concurrent import futures -from concurrent.futures.process import BrokenProcessPool +from concurrent.futures.process import BrokenProcessPool, _ThreadWakeup from test import support @@ -239,6 +242,73 @@ def test_crash_big_data(self): with self.assertRaises(BrokenProcessPool): list(executor.map(_crash_with_data, [data] * 10)) + def test_gh105829_should_not_deadlock_if_wakeup_pipe_full(self): + # Issue #105829: The _ExecutorManagerThread wakeup pipe could + # fill up and block. See: https://github.com/python/cpython/issues/105829 + + # Lots of cargo culting while writing this test, apologies if + # something is really stupid... + + self.executor.shutdown(wait=True) + + if not hasattr(signal, 'alarm'): + raise unittest.SkipTest( + "Tested platform does not support the alarm signal") + + def timeout(_signum, _frame): + import faulthandler + faulthandler.dump_traceback() + + raise RuntimeError("timed out while submitting jobs?") + + thread_run = futures.process._ExecutorManagerThread.run + def mock_run(self): + # Delay thread startup so the wakeup pipe can fill up and block + time.sleep(3) + thread_run(self) + + class MockWakeup(_ThreadWakeup): + """Mock wakeup object to force the wakeup to block""" + def __init__(self): + super().__init__() + self._dummy_queue = queue.Queue(maxsize=1) + + def wakeup(self): + self._dummy_queue.put(None, block=True) + super().wakeup() + + def clear(self): + try: + while True: + self._dummy_queue.get_nowait() + except queue.Empty: + super().clear() + + with (unittest.mock.patch.object(futures.process._ExecutorManagerThread, + 'run', mock_run), + unittest.mock.patch('concurrent.futures.process._ThreadWakeup', + MockWakeup)): + with self.executor_type(max_workers=2, + mp_context=self.get_context()) as executor: + self.executor = executor # Allow clean up in fail_on_deadlock + + job_num = 100 + job_data = range(job_num) + + # Need to use sigalarm for timeout detection because + # Executor.submit is not guarded by any timeout (both + # self._work_ids.put(self._queue_count) and + # self._executor_manager_thread_wakeup.wakeup() might + # timeout, maybe more?). In this specific case it was + # the wakeup call that deadlocked on a blocking pipe. + old_handler = signal.signal(signal.SIGALRM, timeout) + try: + signal.alarm(int(self.TIMEOUT)) + self.assertEqual(job_num, len(list(executor.map(int, job_data)))) + finally: + signal.alarm(0) + signal.signal(signal.SIGALRM, old_handler) + create_executor_tests(globals(), ExecutorDeadlockTest, executor_mixins=(ProcessPoolForkMixin, diff --git a/Misc/NEWS.d/next/Library/2023-08-26-12-35-39.gh-issue-105829.kyYhWI.rst b/Misc/NEWS.d/next/Library/2023-08-26-12-35-39.gh-issue-105829.kyYhWI.rst new file mode 100644 index 00000000000000..eaa2a5a4330e28 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-08-26-12-35-39.gh-issue-105829.kyYhWI.rst @@ -0,0 +1 @@ +Fix concurrent.futures.ProcessPoolExecutor deadlock From 9238c6880e8a1baecaeb44f4db8c15e0131f32a9 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 25 Sep 2023 07:22:41 -0700 Subject: [PATCH 348/632] [3.11] gh-109833: Fix asyncio test_wait_for() (GH-109834) (#109838) gh-109833: Fix asyncio test_wait_for() (GH-109834) Expect the test to be "short" but don't measure the exact performance of the CI. SHORT_TIMEOUT is about 30 seconds whereas the cancelled coroutine takes around 1 hour. (cherry picked from commit f29bc9c9a0a6794c6b8a9e84a7ba9237b427a10a) Co-authored-by: Victor Stinner --- Lib/test/test_asyncio/test_waitfor.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_asyncio/test_waitfor.py b/Lib/test/test_asyncio/test_waitfor.py index 45498fa097f6bc..07dbab3b39ca2b 100644 --- a/Lib/test/test_asyncio/test_waitfor.py +++ b/Lib/test/test_asyncio/test_waitfor.py @@ -1,6 +1,7 @@ import asyncio import unittest import time +from test import support def tearDownModule(): @@ -130,7 +131,7 @@ async def foo(): nonlocal foo_running foo_running = True try: - await asyncio.sleep(10) + await asyncio.sleep(support.LONG_TIMEOUT) finally: foo_running = False return 'done' @@ -144,7 +145,7 @@ async def foo(): self.assertTrue(fut.done()) # it should have been cancelled due to the timeout self.assertTrue(fut.cancelled()) - self.assertLess(t1 - t0, 0.5) + self.assertLess(t1 - t0, support.SHORT_TIMEOUT) self.assertEqual(foo_running, False) async def test_wait_for_blocking(self): From 0afae8928bbe723e9603366745d8ed16f05bb0b8 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 25 Sep 2023 18:12:43 +0200 Subject: [PATCH 349/632] =?UTF-8?q?[3.11]=20gh-109795:=20`=5Fthread.start?= =?UTF-8?q?=5Fnew=5Fthread`:=20allocate=20thread=20bootstate=20usin?= =?UTF-8?q?=E2=80=A6=20(#109852)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gh-109795: `_thread.start_new_thread`: allocate thread bootstate using raw memory allocator (#109808) (cherry picked from commit 1b8f2366b38c87b0450d9c15bdfdd4c4a2fc3a01) Co-authored-by: Radislav Chugunov <52372310+chgnrdv@users.noreply.github.com> --- Modules/_threadmodule.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index ac49ee5c35d4b3..72eda4917ab860 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -1068,7 +1068,7 @@ thread_bootstate_free(struct bootstate *boot, int decref) Py_DECREF(boot->args); Py_XDECREF(boot->kwargs); } - PyMem_Free(boot); + PyMem_RawFree(boot); } @@ -1164,13 +1164,16 @@ thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs) return NULL; } - struct bootstate *boot = PyMem_NEW(struct bootstate, 1); + // gh-109795: Use PyMem_RawMalloc() instead of PyMem_Malloc(), + // because it should be possible to call thread_bootstate_free() + // without holding the GIL. + struct bootstate *boot = PyMem_RawMalloc(sizeof(struct bootstate)); if (boot == NULL) { return PyErr_NoMemory(); } boot->tstate = _PyThreadState_Prealloc(interp); if (boot->tstate == NULL) { - PyMem_Free(boot); + PyMem_RawFree(boot); return PyErr_NoMemory(); } boot->func = Py_NewRef(func); From 22a8efa7408e00552c9845420caf925685dde92f Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 25 Sep 2023 09:29:56 -0700 Subject: [PATCH 350/632] [3.11] gh-89363: Skip threading test_is_alive_after_fork() if ASAN (GH-109835) (#109856) gh-89363: Skip threading test_is_alive_after_fork() if ASAN (GH-109835) Skip test_is_alive_after_fork() of test_threading if Python is built with Address Sanitizer (ASAN). (cherry picked from commit bc06743533b5fea2d5ecdad6dd3caa372c67439f) Co-authored-by: Victor Stinner --- Lib/test/_test_multiprocessing.py | 2 +- Lib/test/test_threading.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index e83c4416cb05a3..c3ba1f686b0021 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -78,7 +78,7 @@ if support.check_sanitizer(address=True): - # bpo-45200: Skip multiprocessing tests if Python is built with ASAN to + # gh-89363: Skip multiprocessing tests if Python is built with ASAN to # work around a libasan race condition: dead lock in pthread_create(). raise unittest.SkipTest("libasan has a pthread_create() dead lock") diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 9c6561c099f570..4688648aa35ea8 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -589,6 +589,10 @@ def background_thread(evt): self.assertEqual(err, b'') @support.requires_fork() + # gh-89363: Skip multiprocessing tests if Python is built with ASAN to + # work around a libasan race condition: dead lock in pthread_create(). + @support.skip_if_sanitizer("libasan has a pthread_create() dead lock", + address=True) def test_is_alive_after_fork(self): # Try hard to trigger #18418: is_alive() could sometimes be True on # threads that vanished after a fork. From 358282e9655a4baf09aa876531fa56f3b5810ddf Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 25 Sep 2023 16:41:32 -0700 Subject: [PATCH 351/632] [3.11] gh-109748: Fix venv test_zippath_from_non_installed_posix() (GH-109872) (#109874) gh-109748: Fix venv test_zippath_from_non_installed_posix() (GH-109872) Fix test_zippath_from_non_installed_posix() of test_venv: don't copy __pycache__/ sub-directories, because they can be modified by other Python tests running in parallel. (cherry picked from commit 25bb266fc876b344e31e0b5634a4db94912c1aba) Co-authored-by: Victor Stinner --- Lib/test/test_venv.py | 10 +++++++++- .../2023-09-26-00-49-18.gh-issue-109748.nxlT1i.rst | 3 +++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Tests/2023-09-26-00-49-18.gh-issue-109748.nxlT1i.rst diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index c7efff549b8631..a37c6b7163f2d2 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -562,6 +562,13 @@ def test_zippath_from_non_installed_posix(self): platlibdir, stdlib_zip) additional_pythonpath_for_non_installed = [] + + # gh-109748: Don't copy __pycache__/ sub-directories, because they can + # be modified by other Python tests running in parallel. + ignored_names = {'__pycache__'} + def ignore_pycache(src, names): + return ignored_names + # Copy stdlib files to the non-installed python so venv can # correctly calculate the prefix. for eachpath in sys.path: @@ -578,7 +585,8 @@ def test_zippath_from_non_installed_posix(self): if os.path.isfile(fn): shutil.copy(fn, libdir) elif os.path.isdir(fn): - shutil.copytree(fn, os.path.join(libdir, name)) + shutil.copytree(fn, os.path.join(libdir, name), + ignore=ignore_pycache) else: additional_pythonpath_for_non_installed.append( eachpath) diff --git a/Misc/NEWS.d/next/Tests/2023-09-26-00-49-18.gh-issue-109748.nxlT1i.rst b/Misc/NEWS.d/next/Tests/2023-09-26-00-49-18.gh-issue-109748.nxlT1i.rst new file mode 100644 index 00000000000000..840366ba8d1611 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-09-26-00-49-18.gh-issue-109748.nxlT1i.rst @@ -0,0 +1,3 @@ +Fix ``test_zippath_from_non_installed_posix()`` of test_venv: don't copy +``__pycache__/`` sub-directories, because they can be modified by other Python +tests running in parallel. Patch by Victor Stinner. From 95e616aee099949a3707ec487dfc87b1eae65876 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 25 Sep 2023 17:34:00 -0700 Subject: [PATCH 352/632] [3.11] gh-109401: Fix threading barrier test_default_timeout() (GH-109875) (#109877) gh-109401: Fix threading barrier test_default_timeout() (GH-109875) Increase timeouts. Barrier default timeout should be long enough to spawn 4 threads on a slow CI. (cherry picked from commit e5186c3de4194de3ea8c80edb182d786f5e20944) Co-authored-by: Victor Stinner --- Lib/test/lock_tests.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Lib/test/lock_tests.py b/Lib/test/lock_tests.py index f16c7ed952cf55..bd6e95c2ed5ffd 100644 --- a/Lib/test/lock_tests.py +++ b/Lib/test/lock_tests.py @@ -1015,13 +1015,15 @@ def test_default_timeout(self): """ Test the barrier's default timeout """ - # create a barrier with a low default timeout - barrier = self.barriertype(self.N, timeout=0.3) + # gh-109401: Barrier timeout should be long enough + # to create 4 threads on a slow CI. + timeout = 1.0 + barrier = self.barriertype(self.N, timeout=timeout) def f(): i = barrier.wait() if i == self.N // 2: - # One thread is later than the default timeout of 0.3s. - time.sleep(1.0) + # One thread is later than the default timeout. + time.sleep(timeout * 2) self.assertRaises(threading.BrokenBarrierError, barrier.wait) self.run_threads(f) From 3ab0621db72124f78799f0d72699cadb17d9ab2b Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 26 Sep 2023 00:58:59 -0700 Subject: [PATCH 353/632] [3.11] gh-101100: Fix Sphinx warnings in `Doc/library/weakref.rst` (GH-109881) (#109883) Co-authored-by: Nikita Sobolev --- Doc/library/weakref.rst | 19 +++++++++---------- Doc/tools/.nitignore | 1 - 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Doc/library/weakref.rst b/Doc/library/weakref.rst index 1406b663c6a8e2..d6e062df945c64 100644 --- a/Doc/library/weakref.rst +++ b/Doc/library/weakref.rst @@ -111,7 +111,7 @@ See :ref:`__slots__ documentation ` for details. Exceptions raised by the callback will be noted on the standard error output, but cannot be propagated; they are handled in exactly the same way as exceptions - raised from an object's :meth:`__del__` method. + raised from an object's :meth:`~object.__del__` method. Weak references are :term:`hashable` if the *object* is hashable. They will maintain their hash value even after the *object* was deleted. If @@ -221,8 +221,7 @@ than needed. Added support for ``|`` and ``|=`` operators, as specified in :pep:`584`. :class:`WeakValueDictionary` objects have an additional method that has the -same issues as the :meth:`keyrefs` method of :class:`WeakKeyDictionary` -objects. +same issues as the :meth:`WeakKeyDictionary.keyrefs` method. .. method:: WeakValueDictionary.valuerefs() @@ -281,7 +280,7 @@ objects. Exceptions raised by finalizer callbacks during garbage collection will be shown on the standard error output, but cannot be propagated. They are handled in the same way as exceptions raised - from an object's :meth:`__del__` method or a weak reference's + from an object's :meth:`~object.__del__` method or a weak reference's callback. When the program exits, each remaining live finalizer is called @@ -523,18 +522,18 @@ is still alive. For instance obj dead or exiting -Comparing finalizers with :meth:`__del__` methods -------------------------------------------------- +Comparing finalizers with :meth:`~object.__del__` methods +--------------------------------------------------------- Suppose we want to create a class whose instances represent temporary directories. The directories should be deleted with their contents when the first of the following events occurs: * the object is garbage collected, -* the object's :meth:`remove` method is called, or +* the object's :meth:`!remove` method is called, or * the program exits. -We might try to implement the class using a :meth:`__del__` method as +We might try to implement the class using a :meth:`~object.__del__` method as follows:: class TempDir: @@ -553,12 +552,12 @@ follows:: def __del__(self): self.remove() -Starting with Python 3.4, :meth:`__del__` methods no longer prevent +Starting with Python 3.4, :meth:`~object.__del__` methods no longer prevent reference cycles from being garbage collected, and module globals are no longer forced to :const:`None` during :term:`interpreter shutdown`. So this code should work without any issues on CPython. -However, handling of :meth:`__del__` methods is notoriously implementation +However, handling of :meth:`~object.__del__` methods is notoriously implementation specific, since it depends on internal details of the interpreter's garbage collector implementation. diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 149e19202a5f86..1c17a3f2bd2c84 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -136,7 +136,6 @@ Doc/library/unittest.mock.rst Doc/library/unittest.rst Doc/library/urllib.parse.rst Doc/library/urllib.request.rst -Doc/library/weakref.rst Doc/library/wsgiref.rst Doc/library/xml.dom.minidom.rst Doc/library/xml.dom.pulldom.rst From 97f7e9d5d6c46106e82ca374f98239c549ef0ed8 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 26 Sep 2023 01:19:29 -0700 Subject: [PATCH 354/632] [3.11] gh-109631: Allow interruption of short repeated regex matches (GH-109867) (GH-109885) Counting for signal checking now continues in new match from the point where it ended in the previous match instead of starting from 0. (cherry picked from commit 8ac2085b80eca4d9b2a1093d0a7da020fd12e11a) Co-authored-by: Serhiy Storchaka --- .../Library/2023-09-25-23-00-37.gh-issue-109631.eWSqpO.rst | 3 +++ Modules/_sre/sre.h | 1 + Modules/_sre/sre_lib.h | 6 ++++-- 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-09-25-23-00-37.gh-issue-109631.eWSqpO.rst diff --git a/Misc/NEWS.d/next/Library/2023-09-25-23-00-37.gh-issue-109631.eWSqpO.rst b/Misc/NEWS.d/next/Library/2023-09-25-23-00-37.gh-issue-109631.eWSqpO.rst new file mode 100644 index 00000000000000..58af2e57068267 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-09-25-23-00-37.gh-issue-109631.eWSqpO.rst @@ -0,0 +1,3 @@ +:mod:`re` functions such as :func:`re.findall`, :func:`re.split`, +:func:`re.search` and :func:`re.sub` which perform short repeated matches +can now be interrupted by user. diff --git a/Modules/_sre/sre.h b/Modules/_sre/sre.h index 52ae3e11b5f750..8d09b110ede11c 100644 --- a/Modules/_sre/sre.h +++ b/Modules/_sre/sre.h @@ -83,6 +83,7 @@ typedef struct { size_t data_stack_base; /* current repeat context */ SRE_REPEAT *repeat; + unsigned int sigcount; } SRE_STATE; typedef struct { diff --git a/Modules/_sre/sre_lib.h b/Modules/_sre/sre_lib.h index e83149825e2cdb..f8d556b2db85c0 100644 --- a/Modules/_sre/sre_lib.h +++ b/Modules/_sre/sre_lib.h @@ -563,7 +563,7 @@ SRE(match)(SRE_STATE* state, const SRE_CODE* pattern, int toplevel) Py_ssize_t alloc_pos, ctx_pos = -1; Py_ssize_t ret = 0; int jump; - unsigned int sigcount=0; + unsigned int sigcount = state->sigcount; SRE(match_context)* ctx; SRE(match_context)* nextctx; @@ -1565,8 +1565,10 @@ SRE(match)(SRE_STATE* state, const SRE_CODE* pattern, int toplevel) ctx_pos = ctx->last_ctx_pos; jump = ctx->jump; DATA_POP_DISCARD(ctx); - if (ctx_pos == -1) + if (ctx_pos == -1) { + state->sigcount = sigcount; return ret; + } DATA_LOOKUP_AT(SRE(match_context), ctx, ctx_pos); switch (jump) { From 8fef834f055d6c6311d022eba2a8a22b4348fa95 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 26 Sep 2023 01:21:09 -0700 Subject: [PATCH 355/632] [3.11] gh-109370: Fix unexpected traceback output in test_concurrent_futures (GH-109780) (GH-109882) Follow-up of gh-107219. * Only close the connection writer on Windows. * Also use existing constant _winapi.ERROR_OPERATION_ABORTED instead of WSA_OPERATION_ABORTED. (cherry picked from commit 0b4e090422db5f959184353d53552d1675f74212) Co-authored-by: Serhiy Storchaka --- Lib/concurrent/futures/process.py | 3 ++- Lib/multiprocessing/connection.py | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/concurrent/futures/process.py b/Lib/concurrent/futures/process.py index 30a7ac8e6fe886..952fa9034574bc 100644 --- a/Lib/concurrent/futures/process.py +++ b/Lib/concurrent/futures/process.py @@ -512,7 +512,8 @@ def terminate_broken(self, cause): # gh-107219: Close the connection writer which can unblock # Queue._feed() if it was stuck in send_bytes(). - self.call_queue._writer.close() + if sys.platform == 'win32': + self.call_queue._writer.close() # clean up resources self.join_executor_internals() diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py index 63c6596b77cd14..8b81f9954e4793 100644 --- a/Lib/multiprocessing/connection.py +++ b/Lib/multiprocessing/connection.py @@ -42,7 +42,6 @@ BUFSIZE = 8192 # A very generous timeout when it comes to local connections... CONNECTION_TIMEOUT = 20. -WSA_OPERATION_ABORTED = 995 _mmap_counter = itertools.count() @@ -300,7 +299,7 @@ def _send_bytes(self, buf): finally: self._send_ov = None nwritten, err = ov.GetOverlappedResult(True) - if err == WSA_OPERATION_ABORTED: + if err == _winapi.ERROR_OPERATION_ABORTED: # close() was called by another thread while # WaitForMultipleObjects() was waiting for the overlapped # operation. From bda6949e860227873432dd1e3732127f5835baed Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 26 Sep 2023 04:08:27 -0700 Subject: [PATCH 356/632] [3.11] gh-109832: concurrent.futures test_deadlock restores sys.stderr (GH-109887) (#109893) gh-109832: concurrent.futures test_deadlock restores sys.stderr (GH-109887) test_error_at_task_unpickle() and test_error_during_result_unpickle_in_result_handler() now restore sys.stderr which is overriden by _raise_error_ignore_stderr(). (cherry picked from commit 2897142d2ec0930a8991af964c798b68fb6dcadd) Co-authored-by: Victor Stinner --- Lib/test/test_concurrent_futures/test_deadlock.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Lib/test/test_concurrent_futures/test_deadlock.py b/Lib/test/test_concurrent_futures/test_deadlock.py index 2f08bf84698885..7ede95d5ed3422 100644 --- a/Lib/test/test_concurrent_futures/test_deadlock.py +++ b/Lib/test/test_concurrent_futures/test_deadlock.py @@ -145,6 +145,9 @@ def test_exit_at_task_unpickle(self): self._check_crash(BrokenProcessPool, id, ExitAtUnpickle()) def test_error_at_task_unpickle(self): + # gh-109832: Restore stderr overriden by _raise_error_ignore_stderr() + self.addCleanup(setattr, sys, 'stderr', sys.stderr) + # Check problem occurring while unpickling a task on workers self._check_crash(BrokenProcessPool, id, ErrorAtUnpickle()) @@ -180,6 +183,9 @@ def test_error_during_result_pickle_on_worker(self): self._check_crash(PicklingError, _return_instance, ErrorAtPickle) def test_error_during_result_unpickle_in_result_handler(self): + # gh-109832: Restore stderr overriden by _raise_error_ignore_stderr() + self.addCleanup(setattr, sys, 'stderr', sys.stderr) + # Check problem occurring while unpickling a task in # the result_handler thread self._check_crash(BrokenProcessPool, From f764abb3752574f8f4c2d6fd2ed3aa1346fd2aee Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 26 Sep 2023 05:21:52 -0700 Subject: [PATCH 357/632] [3.11] gh-109593: Fix reentrancy issue in multiprocessing resource_tracker (GH-109629) (#109897) gh-109593: Fix reentrancy issue in multiprocessing resource_tracker (GH-109629) --------- (cherry picked from commit 0eb98837b60bc58e57ad3e2b35c6b0e9ab634678) Co-authored-by: Antoine Pitrou Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> --- Lib/multiprocessing/resource_tracker.py | 34 ++++++++++++++++-- Lib/test/lock_tests.py | 36 +++++++++++++++++++ Lib/test/test_importlib/test_locks.py | 2 ++ Lib/test/test_threading.py | 3 ++ Lib/threading.py | 7 ++++ ...-09-22-20-16-44.gh-issue-109593.LboaNM.rst | 1 + Modules/_threadmodule.c | 14 ++++++++ 7 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-09-22-20-16-44.gh-issue-109593.LboaNM.rst diff --git a/Lib/multiprocessing/resource_tracker.py b/Lib/multiprocessing/resource_tracker.py index ea369507297f86..79e96ecf3245f4 100644 --- a/Lib/multiprocessing/resource_tracker.py +++ b/Lib/multiprocessing/resource_tracker.py @@ -51,15 +51,31 @@ }) +class ReentrantCallError(RuntimeError): + pass + + class ResourceTracker(object): def __init__(self): - self._lock = threading.Lock() + self._lock = threading.RLock() self._fd = None self._pid = None + def _reentrant_call_error(self): + # gh-109629: this happens if an explicit call to the ResourceTracker + # gets interrupted by a garbage collection, invoking a finalizer (*) + # that itself calls back into ResourceTracker. + # (*) for example the SemLock finalizer + raise ReentrantCallError( + "Reentrant call into the multiprocessing resource tracker") + def _stop(self): with self._lock: + # This should not happen (_stop() isn't called by a finalizer) + # but we check for it anyway. + if self._lock._recursion_count() > 1: + return self._reentrant_call_error() if self._fd is None: # not running return @@ -81,6 +97,9 @@ def ensure_running(self): This can be run from any process. Usually a child process will use the resource created by its parent.''' with self._lock: + if self._lock._recursion_count() > 1: + # The code below is certainly not reentrant-safe, so bail out + return self._reentrant_call_error() if self._fd is not None: # resource tracker was launched before, is it still running? if self._check_alive(): @@ -159,7 +178,17 @@ def unregister(self, name, rtype): self._send('UNREGISTER', name, rtype) def _send(self, cmd, name, rtype): - self.ensure_running() + try: + self.ensure_running() + except ReentrantCallError: + # The code below might or might not work, depending on whether + # the resource tracker was already running and still alive. + # Better warn the user. + # (XXX is warnings.warn itself reentrant-safe? :-) + warnings.warn( + f"ResourceTracker called reentrantly for resource cleanup, " + f"which is unsupported. " + f"The {rtype} object {name!r} might leak.") msg = '{0}:{1}:{2}\n'.format(cmd, name, rtype).encode('ascii') if len(msg) > 512: # posix guarantees that writes to a pipe of less than PIPE_BUF @@ -176,6 +205,7 @@ def _send(self, cmd, name, rtype): unregister = _resource_tracker.unregister getfd = _resource_tracker.getfd + def main(fd): '''Run resource tracker.''' # protect the process from ^C and "killall python" etc diff --git a/Lib/test/lock_tests.py b/Lib/test/lock_tests.py index bd6e95c2ed5ffd..d5ac1319064721 100644 --- a/Lib/test/lock_tests.py +++ b/Lib/test/lock_tests.py @@ -331,6 +331,42 @@ def test_release_save_unacquired(self): lock.release() self.assertRaises(RuntimeError, lock._release_save) + def test_recursion_count(self): + lock = self.locktype() + self.assertEqual(0, lock._recursion_count()) + lock.acquire() + self.assertEqual(1, lock._recursion_count()) + lock.acquire() + lock.acquire() + self.assertEqual(3, lock._recursion_count()) + lock.release() + self.assertEqual(2, lock._recursion_count()) + lock.release() + lock.release() + self.assertEqual(0, lock._recursion_count()) + + phase = [] + + def f(): + lock.acquire() + phase.append(None) + while len(phase) == 1: + _wait() + lock.release() + phase.append(None) + + with threading_helper.wait_threads_exit(): + start_new_thread(f, ()) + while len(phase) == 0: + _wait() + self.assertEqual(len(phase), 1) + self.assertEqual(0, lock._recursion_count()) + phase.append(None) + while len(phase) == 2: + _wait() + self.assertEqual(len(phase), 3) + self.assertEqual(0, lock._recursion_count()) + def test_different_thread(self): # Cannot release from a different thread lock = self.locktype() diff --git a/Lib/test/test_importlib/test_locks.py b/Lib/test/test_importlib/test_locks.py index 56d73c496e6bbb..5549d5448bac1f 100644 --- a/Lib/test/test_importlib/test_locks.py +++ b/Lib/test/test_importlib/test_locks.py @@ -29,6 +29,8 @@ class ModuleLockAsRLockTests: test_timeout = None # _release_save() unsupported test_release_save_unacquired = None + # _recursion_count() unsupported + test_recursion_count = None # lock status in repr unsupported test_repr = None test_locked_repr = None diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 4688648aa35ea8..b352c44a4da4db 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -1607,6 +1607,9 @@ class ConditionAsRLockTests(lock_tests.RLockTests): # Condition uses an RLock by default and exports its API. locktype = staticmethod(threading.Condition) + def test_recursion_count(self): + self.skipTest("Condition does not expose _recursion_count()") + class ConditionTests(lock_tests.ConditionTests): condtype = staticmethod(threading.Condition) diff --git a/Lib/threading.py b/Lib/threading.py index 4f72938551d4ad..70601fc0d5fd44 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -218,6 +218,13 @@ def _release_save(self): def _is_owned(self): return self._owner == get_ident() + # Internal method used for reentrancy checks + + def _recursion_count(self): + if self._owner != get_ident(): + return 0 + return self._count + _PyRLock = _RLock diff --git a/Misc/NEWS.d/next/Library/2023-09-22-20-16-44.gh-issue-109593.LboaNM.rst b/Misc/NEWS.d/next/Library/2023-09-22-20-16-44.gh-issue-109593.LboaNM.rst new file mode 100644 index 00000000000000..292aea0be24dfb --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-09-22-20-16-44.gh-issue-109593.LboaNM.rst @@ -0,0 +1 @@ +Avoid deadlocking on a reentrant call to the multiprocessing resource tracker. Such a reentrant call, though unlikely, can happen if a GC pass invokes the finalizer for a multiprocessing object such as SemLock. diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index 72eda4917ab860..625e4e6e775db1 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -485,6 +485,18 @@ PyDoc_STRVAR(rlock_release_save_doc, \n\ For internal use by `threading.Condition`."); +static PyObject * +rlock_recursion_count(rlockobject *self, PyObject *Py_UNUSED(ignored)) +{ + unsigned long tid = PyThread_get_thread_ident(); + return PyLong_FromUnsignedLong( + self->rlock_owner == tid ? self->rlock_count : 0UL); +} + +PyDoc_STRVAR(rlock_recursion_count_doc, +"_recursion_count() -> int\n\ +\n\ +For internal use by reentrancy checks."); static PyObject * rlock_is_owned(rlockobject *self, PyObject *Py_UNUSED(ignored)) @@ -560,6 +572,8 @@ static PyMethodDef rlock_methods[] = { METH_VARARGS, rlock_acquire_restore_doc}, {"_release_save", (PyCFunction)rlock_release_save, METH_NOARGS, rlock_release_save_doc}, + {"_recursion_count", (PyCFunction)rlock_recursion_count, + METH_NOARGS, rlock_recursion_count_doc}, {"__enter__", _PyCFunction_CAST(rlock_acquire), METH_VARARGS | METH_KEYWORDS, rlock_acquire_doc}, {"__exit__", (PyCFunction)rlock_release, From 4e66eca489a33dc81d676037a456e85c50c81123 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 26 Sep 2023 13:23:20 -0700 Subject: [PATCH 358/632] [3.11] gh-109845: Make test_ftplib more stable under load (GH-109912) (GH-109920) recv() can return partial data cut in the middle of a multibyte character. Test raw binary data instead of data incorrectly decoded by parts. (cherry picked from commit 2ef2fffe3be953b91852585c75188d5475b09474) Co-authored-by: Serhiy Storchaka --- Lib/test/test_ftplib.py | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/Lib/test/test_ftplib.py b/Lib/test/test_ftplib.py index 082a90d46baedc..7d6b12ffadcef3 100644 --- a/Lib/test/test_ftplib.py +++ b/Lib/test/test_ftplib.py @@ -34,7 +34,7 @@ DEFAULT_ENCODING = 'utf-8' # the dummy data returned by server over the data channel when # RETR, LIST, NLST, MLSD commands are issued -RETR_DATA = 'abcde12345\r\n' * 1000 + 'non-ascii char \xAE\r\n' +RETR_DATA = 'abcde\xB9\xB2\xB3\xA4\xA6\r\n' * 1000 LIST_DATA = 'foo\r\nbar\r\n non-ascii char \xAE\r\n' NLST_DATA = 'foo\r\nbar\r\n non-ascii char \xAE\r\n' MLSD_DATA = ("type=cdir;perm=el;unique==keVO1+ZF4; test\r\n" @@ -69,11 +69,11 @@ class DummyDTPHandler(asynchat.async_chat): def __init__(self, conn, baseclass): asynchat.async_chat.__init__(self, conn) self.baseclass = baseclass - self.baseclass.last_received_data = '' + self.baseclass.last_received_data = bytearray() self.encoding = baseclass.encoding def handle_read(self): - new_data = self.recv(1024).decode(self.encoding, 'replace') + new_data = self.recv(1024) self.baseclass.last_received_data += new_data def handle_close(self): @@ -109,7 +109,7 @@ def __init__(self, conn, encoding=DEFAULT_ENCODING): self.in_buffer = [] self.dtp = None self.last_received_cmd = None - self.last_received_data = '' + self.last_received_data = bytearray() self.next_response = '' self.next_data = None self.rest = None @@ -592,19 +592,17 @@ def test_abort(self): self.client.abort() def test_retrbinary(self): - def callback(data): - received.append(data.decode(self.client.encoding)) received = [] - self.client.retrbinary('retr', callback) - self.check_data(''.join(received), RETR_DATA) + self.client.retrbinary('retr', received.append) + self.check_data(b''.join(received), + RETR_DATA.encode(self.client.encoding)) def test_retrbinary_rest(self): - def callback(data): - received.append(data.decode(self.client.encoding)) for rest in (0, 10, 20): received = [] - self.client.retrbinary('retr', callback, rest=rest) - self.check_data(''.join(received), RETR_DATA[rest:]) + self.client.retrbinary('retr', received.append, rest=rest) + self.check_data(b''.join(received), + RETR_DATA[rest:].encode(self.client.encoding)) def test_retrlines(self): received = [] @@ -614,7 +612,8 @@ def test_retrlines(self): def test_storbinary(self): f = io.BytesIO(RETR_DATA.encode(self.client.encoding)) self.client.storbinary('stor', f) - self.check_data(self.server.handler_instance.last_received_data, RETR_DATA) + self.check_data(self.server.handler_instance.last_received_data, + RETR_DATA.encode(self.server.encoding)) # test new callback arg flag = [] f.seek(0) @@ -633,7 +632,8 @@ def test_storlines(self): data = RETR_DATA.replace('\r\n', '\n').encode(self.client.encoding) f = io.BytesIO(data) self.client.storlines('stor', f) - self.check_data(self.server.handler_instance.last_received_data, RETR_DATA) + self.check_data(self.server.handler_instance.last_received_data, + RETR_DATA.encode(self.server.encoding)) # test new callback arg flag = [] f.seek(0) @@ -651,7 +651,7 @@ def test_nlst(self): def test_dir(self): l = [] - self.client.dir(lambda x: l.append(x)) + self.client.dir(l.append) self.assertEqual(''.join(l), LIST_DATA.replace('\r\n', '')) def test_mlsd(self): @@ -891,12 +891,10 @@ def test_makepasv(self): def test_transfer(self): def retr(): - def callback(data): - received.append(data.decode(self.client.encoding)) received = [] - self.client.retrbinary('retr', callback) - self.assertEqual(len(''.join(received)), len(RETR_DATA)) - self.assertEqual(''.join(received), RETR_DATA) + self.client.retrbinary('retr', received.append) + self.assertEqual(b''.join(received), + RETR_DATA.encode(self.client.encoding)) self.client.set_pasv(True) retr() self.client.set_pasv(False) From 88917ddf448d6a2f5cb635b8a0b224b476757aaf Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 26 Sep 2023 15:51:34 -0700 Subject: [PATCH 359/632] [3.11] gh-107888: Fix test_mmap.test_access_parameter() on macOS 14 (GH-109928) (#109930) gh-107888: Fix test_mmap.test_access_parameter() on macOS 14 (GH-109928) (cherry picked from commit 9dbfe2dc8e7bba25e52f9470ae6969821a365297) Co-authored-by: Victor Stinner --- Lib/test/test_mmap.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_mmap.py b/Lib/test/test_mmap.py index bab868600895c1..92c99d645b25cc 100644 --- a/Lib/test/test_mmap.py +++ b/Lib/test/test_mmap.py @@ -255,10 +255,15 @@ def test_access_parameter(self): # Try writing with PROT_EXEC and without PROT_WRITE prot = mmap.PROT_READ | getattr(mmap, 'PROT_EXEC', 0) with open(TESTFN, "r+b") as f: - m = mmap.mmap(f.fileno(), mapsize, prot=prot) - self.assertRaises(TypeError, m.write, b"abcdef") - self.assertRaises(TypeError, m.write_byte, 0) - m.close() + try: + m = mmap.mmap(f.fileno(), mapsize, prot=prot) + except PermissionError: + # on macOS 14, PROT_READ | PROT_WRITE is not allowed + pass + else: + self.assertRaises(TypeError, m.write, b"abcdef") + self.assertRaises(TypeError, m.write_byte, 0) + m.close() def test_bad_file_desc(self): # Try opening a bad file descriptor... From c485715ba884ebfbcbad7a0ab01a8dc933f74d2b Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 26 Sep 2023 17:35:26 -0700 Subject: [PATCH 360/632] [3.11] gh-101100: Fix Sphinx warnings in Doc/using/configure.rst (GH-109931) (#109938) gh-101100: Fix Sphinx warnings in Doc/using/configure.rst (GH-109931) (cherry picked from commit 3538930d87e6bdd2bfffa3f674a62cc91d359d31) Co-authored-by: Victor Stinner --- Doc/tools/.nitignore | 1 - Doc/using/configure.rst | 16 ++++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 1c17a3f2bd2c84..49566e61cdc35c 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -156,7 +156,6 @@ Doc/tutorial/controlflow.rst Doc/tutorial/datastructures.rst Doc/tutorial/introduction.rst Doc/using/cmdline.rst -Doc/using/configure.rst Doc/using/windows.rst Doc/whatsnew/2.0.rst Doc/whatsnew/2.1.rst diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst index c2b2dee5615ba5..6e4a43f4aadcbf 100644 --- a/Doc/using/configure.rst +++ b/Doc/using/configure.rst @@ -18,8 +18,8 @@ General Options .. cmdoption:: --enable-loadable-sqlite-extensions - Support loadable extensions in the :mod:`_sqlite` extension module (default - is no). + Support loadable extensions in the :mod:`!_sqlite` extension module (default + is no) of the :mod:`sqlite3` module. See the :meth:`sqlite3.Connection.enable_load_extension` method of the :mod:`sqlite3` module. @@ -117,7 +117,7 @@ General Options Some Linux distribution packaging policies recommend against bundling dependencies. For example, Fedora installs wheel packages in the ``/usr/share/python-wheels/`` directory and don't install the - :mod:`ensurepip._bundled` package. + :mod:`!ensurepip._bundled` package. .. versionadded:: 3.10 @@ -193,7 +193,7 @@ Install Options .. cmdoption:: --disable-test-modules Don't build nor install test modules, like the :mod:`test` package or the - :mod:`_testcapi` extension module (built and installed by default). + :mod:`!_testcapi` extension module (built and installed by default). .. versionadded:: 3.10 @@ -292,7 +292,7 @@ Effects of a debug build: * Display all warnings by default: the list of default warning filters is empty in the :mod:`warnings` module. * Add ``d`` to :data:`sys.abiflags`. -* Add :func:`sys.gettotalrefcount` function. +* Add :func:`!sys.gettotalrefcount` function. * Add :option:`-X showrefcount <-X>` command line option. * Add :envvar:`PYTHONTHREADDEBUG` environment variable. * Add support for the ``__lltrace__`` variable: enable low-level tracing in the @@ -313,7 +313,7 @@ Effects of a debug build: * Check that deallocator functions don't change the current exception. * The garbage collector (:func:`gc.collect` function) runs some basic checks on objects consistency. - * The :c:macro:`Py_SAFE_DOWNCAST()` macro checks for integer underflow and + * The :c:macro:`!Py_SAFE_DOWNCAST()` macro checks for integer underflow and overflow when downcasting from wide types to narrow types. See also the :ref:`Python Development Mode ` and the @@ -341,7 +341,7 @@ Debug options Effects: * Define the ``Py_TRACE_REFS`` macro. - * Add :func:`sys.getobjects` function. + * Add :func:`!sys.getobjects` function. * Add :envvar:`PYTHONDUMPREFS` environment variable. This build is not ABI compatible with release build (default build) or debug @@ -419,7 +419,7 @@ Libraries options .. cmdoption:: --with-system-expat - Build the :mod:`pyexpat` module using an installed ``expat`` library + Build the :mod:`!pyexpat` module using an installed ``expat`` library (default is no). .. cmdoption:: --with-system-ffi From 8ed9bda5199eccc482b76a69644c9e60504b822a Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 26 Sep 2023 23:15:40 -0700 Subject: [PATCH 361/632] [3.11] gh-109098: Fuzz re module instead of internal sre (GH-109911) (GH-109933) * Fix c-analyzer globals test failure * Put globals exception in ignored.tsv (cherry picked from commit a829356f86d597e4dfe92e236a6d711c8a464f16) Co-authored-by: Ammar Askar --- Modules/_xxtestfuzz/fuzzer.c | 36 +++++++++++++--------------- Tools/c-analyzer/cpython/ignored.tsv | 6 ++--- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/Modules/_xxtestfuzz/fuzzer.c b/Modules/_xxtestfuzz/fuzzer.c index 366e81a54519a7..0c9f8427254e55 100644 --- a/Modules/_xxtestfuzz/fuzzer.c +++ b/Modules/_xxtestfuzz/fuzzer.c @@ -188,37 +188,33 @@ static int fuzz_json_loads(const char* data, size_t size) { #define MAX_RE_TEST_SIZE 0x10000 -PyObject* sre_compile_method = NULL; -PyObject* sre_error_exception = NULL; -int SRE_FLAG_DEBUG = 0; +PyObject* re_compile_method = NULL; +PyObject* re_error_exception = NULL; +int RE_FLAG_DEBUG = 0; /* Called by LLVMFuzzerTestOneInput for initialization */ static int init_sre_compile(void) { /* Import sre_compile.compile and sre.error */ - PyObject* sre_compile_module = PyImport_ImportModule("sre_compile"); - if (sre_compile_module == NULL) { + PyObject* re_module = PyImport_ImportModule("re"); + if (re_module == NULL) { return 0; } - sre_compile_method = PyObject_GetAttrString(sre_compile_module, "compile"); - if (sre_compile_method == NULL) { + re_compile_method = PyObject_GetAttrString(re_module, "compile"); + if (re_compile_method == NULL) { return 0; } - PyObject* sre_constants = PyImport_ImportModule("sre_constants"); - if (sre_constants == NULL) { - return 0; - } - sre_error_exception = PyObject_GetAttrString(sre_constants, "error"); - if (sre_error_exception == NULL) { + re_error_exception = PyObject_GetAttrString(re_module, "error"); + if (re_error_exception == NULL) { return 0; } - PyObject* debug_flag = PyObject_GetAttrString(sre_constants, "SRE_FLAG_DEBUG"); + PyObject* debug_flag = PyObject_GetAttrString(re_module, "DEBUG"); if (debug_flag == NULL) { return 0; } - SRE_FLAG_DEBUG = PyLong_AsLong(debug_flag); + RE_FLAG_DEBUG = PyLong_AsLong(debug_flag); return 1; } -/* Fuzz _sre.compile(x) */ +/* Fuzz re.compile(x) */ static int fuzz_sre_compile(const char* data, size_t size) { /* Ignore really long regex patterns that will timeout the fuzzer */ if (size > MAX_RE_TEST_SIZE) { @@ -231,7 +227,7 @@ static int fuzz_sre_compile(const char* data, size_t size) { uint16_t flags = ((uint16_t*) data)[0]; /* We remove the SRE_FLAG_DEBUG if present. This is because it prints to stdout which greatly decreases fuzzing speed */ - flags &= ~SRE_FLAG_DEBUG; + flags &= ~RE_FLAG_DEBUG; /* Pull the pattern from the remaining bytes */ PyObject* pattern_bytes = PyBytes_FromStringAndSize(data + 2, size - 2); @@ -244,9 +240,9 @@ static int fuzz_sre_compile(const char* data, size_t size) { return 0; } - /* compiled = _sre.compile(data[2:], data[0:2] */ + /* compiled = re.compile(data[2:], data[0:2] */ PyObject* compiled = PyObject_CallFunctionObjArgs( - sre_compile_method, pattern_bytes, flags_obj, NULL); + re_compile_method, pattern_bytes, flags_obj, NULL); /* Ignore ValueError as the fuzzer will more than likely generate some invalid combination of flags */ if (compiled == NULL && PyErr_ExceptionMatches(PyExc_ValueError)) { @@ -262,7 +258,7 @@ static int fuzz_sre_compile(const char* data, size_t size) { PyErr_Clear(); } /* Ignore re.error */ - if (compiled == NULL && PyErr_ExceptionMatches(sre_error_exception)) { + if (compiled == NULL && PyErr_ExceptionMatches(re_error_exception)) { PyErr_Clear(); } diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv index 6e4f1c4046398c..b2af126025563b 100644 --- a/Tools/c-analyzer/cpython/ignored.tsv +++ b/Tools/c-analyzer/cpython/ignored.tsv @@ -243,15 +243,15 @@ Modules/_testmultiphase.c - testexport_methods - Modules/_testmultiphase.c - uninitialized_def - Modules/_xxtestfuzz/_xxtestfuzz.c - _fuzzmodule - Modules/_xxtestfuzz/_xxtestfuzz.c - module_methods - -Modules/_xxtestfuzz/fuzzer.c - SRE_FLAG_DEBUG - +Modules/_xxtestfuzz/fuzzer.c - RE_FLAG_DEBUG - Modules/_xxtestfuzz/fuzzer.c - ast_literal_eval_method - Modules/_xxtestfuzz/fuzzer.c - compiled_patterns - Modules/_xxtestfuzz/fuzzer.c - csv_error - Modules/_xxtestfuzz/fuzzer.c - csv_module - Modules/_xxtestfuzz/fuzzer.c - json_loads_method - Modules/_xxtestfuzz/fuzzer.c - regex_patterns - -Modules/_xxtestfuzz/fuzzer.c - sre_compile_method - -Modules/_xxtestfuzz/fuzzer.c - sre_error_exception - +Modules/_xxtestfuzz/fuzzer.c - re_compile_method - +Modules/_xxtestfuzz/fuzzer.c - re_error_exception - Modules/_xxtestfuzz/fuzzer.c - struct_error - Modules/_xxtestfuzz/fuzzer.c - struct_unpack_method - Modules/_xxtestfuzz/fuzzer.c LLVMFuzzerTestOneInput CSV_READER_INITIALIZED - From 0a69de768724639b5f91fcd1434acda7baaf6afd Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 26 Sep 2023 23:18:25 -0700 Subject: [PATCH 362/632] [3.11] Remove loop from docstring for asyncio.streams.open_connection (GH-108528) (#109942) Remove loop from docstring for asyncio.streams.open_connection (GH-108528) (cherry picked from commit e721f7a95186452339dc9e57630d639d549b2521) Co-authored-by: Tom Gillespie --- Lib/asyncio/streams.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Lib/asyncio/streams.py b/Lib/asyncio/streams.py index 3d577f127041aa..19d9154b66d027 100644 --- a/Lib/asyncio/streams.py +++ b/Lib/asyncio/streams.py @@ -67,9 +67,8 @@ async def start_server(client_connected_cb, host=None, port=None, *, positional host and port, with various optional keyword arguments following. The return value is the same as loop.create_server(). - Additional optional keyword arguments are loop (to set the event loop - instance to use) and limit (to set the buffer limit passed to the - StreamReader). + Additional optional keyword argument is limit (to set the buffer + limit passed to the StreamReader). The return value is the same as loop.create_server(), i.e. a Server object which can be used to stop the service. From d951aaea9762d86b560aaeb335e6ffac68238868 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 27 Sep 2023 10:40:24 +0200 Subject: [PATCH 363/632] [3.11] gh-107298: Document PyMODINIT_FUNC macro (#109236) (#109948) gh-107298: Document PyMODINIT_FUNC macro (#109236) Document PyMODINIT_FUNC macro. Remove links to PyAPI_FUNC() and PyAPI_DATA() macros since they are not documented. These macros should only be used to define the Python C API. They should not be used outside Python code base. (cherry picked from commit d7a27e527d7e669d2e45cff80ad725978226477c) --- Doc/c-api/intro.rst | 24 ++++++++++++++++++++++++ Doc/using/configure.rst | 2 +- Doc/whatsnew/2.3.rst | 2 +- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/Doc/c-api/intro.rst b/Doc/c-api/intro.rst index ec2d8bc0ef02ef..39df9ec390d4a9 100644 --- a/Doc/c-api/intro.rst +++ b/Doc/c-api/intro.rst @@ -105,6 +105,30 @@ defined closer to where they are useful (e.g. :c:macro:`Py_RETURN_NONE`). Others of a more general utility are defined here. This is not necessarily a complete listing. +.. c:macro:: PyMODINIT_FUNC + + Declare an extension module ``PyInit`` initialization function. The function + return type is :c:expr:`PyObject*`. The macro declares any special linkage + declarations required by the platform, and for C++ declares the function as + ``extern "C"``. + + The initialization function must be named :samp:`PyInit_{name}`, where + *name* is the name of the module, and should be the only non-\ ``static`` + item defined in the module file. Example:: + + static struct PyModuleDef spam_module = { + PyModuleDef_HEAD_INIT, + .m_name = "spam", + ... + }; + + PyMODINIT_FUNC + PyInit_spam(void) + { + return PyModule_Create(&spam_module); + } + + .. c:macro:: Py_ABS(x) Return the absolute value of ``x``. diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst index 6e4a43f4aadcbf..ca3b88e079609a 100644 --- a/Doc/using/configure.rst +++ b/Doc/using/configure.rst @@ -679,7 +679,7 @@ Extensions defined after the ``*shared*`` marker are built as dynamic libraries. The :file:`setup.py` script only builds C extensions as shared libraries using the :mod:`distutils` module. -The :c:macro:`PyAPI_FUNC()`, :c:macro:`PyAPI_DATA()` and +The :c:macro:`!PyAPI_FUNC()`, :c:macro:`!PyAPI_DATA()` and :c:macro:`PyMODINIT_FUNC` macros of :file:`Include/pyport.h` are defined differently depending if the ``Py_BUILD_CORE_MODULE`` macro is defined: diff --git a/Doc/whatsnew/2.3.rst b/Doc/whatsnew/2.3.rst index 72aef3d269f8d3..3460421fe45cce 100644 --- a/Doc/whatsnew/2.3.rst +++ b/Doc/whatsnew/2.3.rst @@ -1889,7 +1889,7 @@ Changes to Python's build process and to the C API include: * The :c:macro:`!DL_EXPORT` and :c:macro:`!DL_IMPORT` macros are now deprecated. Initialization functions for Python extension modules should now be declared using the new macro :c:macro:`PyMODINIT_FUNC`, while the Python core will - generally use the :c:macro:`PyAPI_FUNC` and :c:macro:`PyAPI_DATA` macros. + generally use the :c:macro:`!PyAPI_FUNC` and :c:macro:`!PyAPI_DATA` macros. * The interpreter can be compiled without any docstrings for the built-in functions and modules by supplying :option:`!--without-doc-strings` to the From 242316860480fd33eb2a0afeb1154b64c46aa155 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 27 Sep 2023 10:59:02 +0200 Subject: [PATCH 364/632] [3.11] gh-109615: Fix test_tools.test_freeze SRCDIR (#109935) (#109951) gh-109615: Fix test_tools.test_freeze SRCDIR (#109935) Fix copy_source_tree() function of test_tools.test_freeze: * Don't copy SRC_DIR/build/ anymore. This directory is modified by other tests running in parallel. * Add test.support.copy_python_src_ignore(). * Use sysconfig to get the source directory. * Use sysconfig.get_config_var() to get CONFIG_ARGS variable. (cherry picked from commit 1512d6c6ee2a770afb339bbb74c1b990116f7f89) --- Lib/test/libregrtest/main.py | 2 +- Lib/test/support/__init__.py | 26 +++++++++++++++++ Lib/test/test_support.py | 22 +++++++++++++++ Lib/test/test_venv.py | 10 ++----- Tools/freeze/test/freeze.py | 54 +++++------------------------------- 5 files changed, 58 insertions(+), 56 deletions(-) diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index 6e6423e156781b..e4e65e943d67bc 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -731,7 +731,7 @@ def set_temp_dir(self): if sysconfig.is_python_build(): self.tmp_dir = sysconfig.get_config_var('abs_builddir') if self.tmp_dir is None: - # bpo-30284: On Windows, only srcdir is available. Using + # gh-74470: On Windows, only srcdir is available. Using # abs_builddir mostly matters on UNIX when building Python # out of the source tree, especially when the source tree # is read only. diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index b4fa727aae3413..2c6b22fdee5a21 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -2324,3 +2324,29 @@ def adjust_int_max_str_digits(max_digits): yield finally: sys.set_int_max_str_digits(current) + +_BASE_COPY_SRC_DIR_IGNORED_NAMES = frozenset({ + # SRC_DIR/.git + '.git', + # ignore all __pycache__/ sub-directories + '__pycache__', +}) + +# Ignore function for shutil.copytree() to copy the Python source code. +def copy_python_src_ignore(path, names): + ignored = _BASE_COPY_SRC_DIR_IGNORED_NAMES + if os.path.basename(path) == 'Doc': + ignored |= { + # SRC_DIR/Doc/build/ + 'build', + # SRC_DIR/Doc/venv/ + 'venv', + } + + # check if we are at the root of the source code + elif 'Modules' in names: + ignored |= { + # SRC_DIR/build/ + 'build', + } + return ignored diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index 54e532064740f3..a6b241e4a82b76 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -7,6 +7,7 @@ import stat import subprocess import sys +import sysconfig import tempfile import textwrap import time @@ -777,6 +778,27 @@ def recursive_function(depth): #self.assertEqual(available, 2) + def test_copy_python_src_ignore(self): + src_dir = sysconfig.get_config_var('srcdir') + src_dir = os.path.abspath(src_dir) + + ignored = {'.git', '__pycache__'} + + # Source code directory + names = os.listdir(src_dir) + self.assertEqual(support.copy_python_src_ignore(src_dir, names), + ignored | {'build'}) + + # Doc/ directory + path = os.path.join(src_dir, 'Doc') + self.assertEqual(support.copy_python_src_ignore(path, os.listdir(path)), + ignored | {'build', 'venv'}) + + # An other directory + path = os.path.join(src_dir, 'Objects') + self.assertEqual(support.copy_python_src_ignore(path, os.listdir(path)), + ignored) + # XXX -follows a list of untested API # make_legacy_pyc # is_resource_enabled diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index a37c6b7163f2d2..884ac3ac567c07 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -21,7 +21,7 @@ skip_if_broken_multiprocessing_synchronize, verbose, requires_subprocess, is_emscripten, is_wasi, requires_venv_with_pip, TEST_HOME_DIR, - requires_resource) + requires_resource, copy_python_src_ignore) from test.support.os_helper import (can_symlink, EnvironmentVarGuard, rmtree) import unittest import venv @@ -563,12 +563,6 @@ def test_zippath_from_non_installed_posix(self): stdlib_zip) additional_pythonpath_for_non_installed = [] - # gh-109748: Don't copy __pycache__/ sub-directories, because they can - # be modified by other Python tests running in parallel. - ignored_names = {'__pycache__'} - def ignore_pycache(src, names): - return ignored_names - # Copy stdlib files to the non-installed python so venv can # correctly calculate the prefix. for eachpath in sys.path: @@ -586,7 +580,7 @@ def ignore_pycache(src, names): shutil.copy(fn, libdir) elif os.path.isdir(fn): shutil.copytree(fn, os.path.join(libdir, name), - ignore=ignore_pycache) + ignore=copy_python_src_ignore) else: additional_pythonpath_for_non_installed.append( eachpath) diff --git a/Tools/freeze/test/freeze.py b/Tools/freeze/test/freeze.py index f6a5adb4519fdb..bb15941464e3d1 100644 --- a/Tools/freeze/test/freeze.py +++ b/Tools/freeze/test/freeze.py @@ -1,14 +1,15 @@ import os import os.path -import re import shlex import shutil import subprocess +import sysconfig +from test import support TESTS_DIR = os.path.dirname(__file__) TOOL_ROOT = os.path.dirname(TESTS_DIR) -SRCDIR = os.path.dirname(os.path.dirname(TOOL_ROOT)) +SRCDIR = os.path.abspath(sysconfig.get_config_var('srcdir')) MAKE = shutil.which('make') FREEZE = os.path.join(TOOL_ROOT, 'freeze.py') @@ -75,56 +76,17 @@ def ensure_opt(args, name, value): def copy_source_tree(newroot, oldroot): - print(f'copying the source tree into {newroot}...') + print(f'copying the source tree from {oldroot} to {newroot}...') if os.path.exists(newroot): if newroot == SRCDIR: raise Exception('this probably isn\'t what you wanted') shutil.rmtree(newroot) - def ignore_non_src(src, names): - """Turns what could be a 1000M copy into a 100M copy.""" - # Don't copy the ~600M+ of needless git repo metadata. - # source only, ignore cached .pyc files. - subdirs_to_skip = {'.git', '__pycache__'} - if os.path.basename(src) == 'Doc': - # Another potential ~250M+ of non test related data. - subdirs_to_skip.add('build') - subdirs_to_skip.add('venv') - return subdirs_to_skip - - shutil.copytree(oldroot, newroot, ignore=ignore_non_src) + shutil.copytree(oldroot, newroot, ignore=support.copy_python_src_ignore) if os.path.exists(os.path.join(newroot, 'Makefile')): _run_quiet([MAKE, 'clean'], newroot) -def get_makefile_var(builddir, name): - regex = re.compile(rf'^{name} *=\s*(.*?)\s*$') - filename = os.path.join(builddir, 'Makefile') - try: - infile = open(filename, encoding='utf-8') - except FileNotFoundError: - return None - with infile: - for line in infile: - m = regex.match(line) - if m: - value, = m.groups() - return value or '' - return None - - -def get_config_var(builddir, name): - python = os.path.join(builddir, 'python') - if os.path.isfile(python): - cmd = [python, '-c', - f'import sysconfig; print(sysconfig.get_config_var("{name}"))'] - try: - return _run_stdout(cmd) - except subprocess.CalledProcessError: - pass - return get_makefile_var(builddir, name) - - ################################## # freezing @@ -151,10 +113,8 @@ def prepare(script=None, outdir=None): # Run configure. print(f'configuring python in {builddir}...') - cmd = [ - os.path.join(srcdir, 'configure'), - *shlex.split(get_config_var(srcdir, 'CONFIG_ARGS') or ''), - ] + config_args = shlex.split(sysconfig.get_config_var('CONFIG_ARGS') or '') + cmd = [os.path.join(srcdir, 'configure'), *config_args] ensure_opt(cmd, 'cache-file', os.path.join(outdir, 'python-config.cache')) prefix = os.path.join(outdir, 'python-installation') ensure_opt(cmd, 'prefix', prefix) From 8ac20e5404127d68624339c0b318abe2d14fe514 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 27 Sep 2023 13:00:22 +0200 Subject: [PATCH 365/632] [3.11] gh-109615: Fix support test_copy_python_src_ignore() (#109958) (#109962) gh-109615: Fix support test_copy_python_src_ignore() (#109958) Fix the test when run on an installed Python: use "abs_srcdir" of sysconfig, and skip the test if the Python source code cannot be found. * Tools/patchcheck/patchcheck.py, Tools/freeze/test/freeze.py and Lib/test/libregrtest/utils.py now first try to get "abs_srcdir" from sysconfig, before getting "srcdir" from sysconfig. * test.pythoninfo logs sysconfig "abs_srcdir". (cherry picked from commit b89ed9df39851348fbb1552294644f99f6b17d2c) --- Lib/test/libregrtest/main.py | 12 +++++++----- Lib/test/pythoninfo.py | 3 +++ Lib/test/test_support.py | 7 ++++++- Tools/freeze/test/freeze.py | 9 ++++++++- Tools/scripts/patchcheck.py | 9 ++++++++- 5 files changed, 32 insertions(+), 8 deletions(-) diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index e4e65e943d67bc..7192244c69c55e 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -731,11 +731,13 @@ def set_temp_dir(self): if sysconfig.is_python_build(): self.tmp_dir = sysconfig.get_config_var('abs_builddir') if self.tmp_dir is None: - # gh-74470: On Windows, only srcdir is available. Using - # abs_builddir mostly matters on UNIX when building Python - # out of the source tree, especially when the source tree - # is read only. - self.tmp_dir = sysconfig.get_config_var('srcdir') + self.tmp_dir = sysconfig.get_config_var('abs_srcdir') + if not self.tmp_dir: + # gh-74470: On Windows, only srcdir is available. Using + # abs_builddir mostly matters on UNIX when building + # Python out of the source tree, especially when the + # source tree is read only. + self.tmp_dir = sysconfig.get_config_var('srcdir') self.tmp_dir = os.path.join(self.tmp_dir, 'build') else: self.tmp_dir = tempfile.gettempdir() diff --git a/Lib/test/pythoninfo.py b/Lib/test/pythoninfo.py index 69d2fc0d061a55..db8dfe247cca25 100644 --- a/Lib/test/pythoninfo.py +++ b/Lib/test/pythoninfo.py @@ -524,7 +524,10 @@ def collect_sysconfig(info_add): 'Py_NOGIL', 'SHELL', 'SOABI', + 'abs_builddir', + 'abs_srcdir', 'prefix', + 'srcdir', ): value = sysconfig.get_config_var(name) if name == 'ANDROID_API_LEVEL' and not value: diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index a6b241e4a82b76..53220ec04a9c94 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -779,8 +779,13 @@ def recursive_function(depth): #self.assertEqual(available, 2) def test_copy_python_src_ignore(self): - src_dir = sysconfig.get_config_var('srcdir') + src_dir = sysconfig.get_config_var('abs_srcdir') + if not src_dir: + src_dir = sysconfig.get_config_var('srcdir') src_dir = os.path.abspath(src_dir) + if not os.path.exists(src_dir): + self.skipTest(f"cannot access Python source code directory:" + f" {src_dir!r}") ignored = {'.git', '__pycache__'} diff --git a/Tools/freeze/test/freeze.py b/Tools/freeze/test/freeze.py index bb15941464e3d1..cdf77c57bbb6ae 100644 --- a/Tools/freeze/test/freeze.py +++ b/Tools/freeze/test/freeze.py @@ -7,9 +7,16 @@ from test import support +def get_python_source_dir(): + src_dir = sysconfig.get_config_var('abs_srcdir') + if not src_dir: + src_dir = sysconfig.get_config_var('srcdir') + return os.path.abspath(src_dir) + + TESTS_DIR = os.path.dirname(__file__) TOOL_ROOT = os.path.dirname(TESTS_DIR) -SRCDIR = os.path.abspath(sysconfig.get_config_var('srcdir')) +SRCDIR = get_python_source_dir() MAKE = shutil.which('make') FREEZE = os.path.join(TOOL_ROOT, 'freeze.py') diff --git a/Tools/scripts/patchcheck.py b/Tools/scripts/patchcheck.py index a324eafc52b2ab..c2dceea2a2c634 100755 --- a/Tools/scripts/patchcheck.py +++ b/Tools/scripts/patchcheck.py @@ -11,6 +11,13 @@ import untabify +def get_python_source_dir(): + src_dir = sysconfig.get_config_var('abs_srcdir') + if not src_dir: + src_dir = sysconfig.get_config_var('srcdir') + return os.path.abspath(src_dir) + + # Excluded directories which are copies of external libraries: # don't check their coding style EXCLUDE_DIRS = [os.path.join('Modules', '_ctypes', 'libffi_osx'), @@ -18,7 +25,7 @@ os.path.join('Modules', '_decimal', 'libmpdec'), os.path.join('Modules', 'expat'), os.path.join('Modules', 'zlib')] -SRCDIR = sysconfig.get_config_var('srcdir') +SRCDIR = get_python_source_dir() def n_files_str(count): From cb0f41f2320e0aba567c9185202195e6e9b7203e Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 27 Sep 2023 07:16:01 -0700 Subject: [PATCH 366/632] [3.11] gh-101100: Fix sphinx warnings in `library/devmode.rst` (GH-109963) (#109967) Co-authored-by: Nikita Sobolev Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/library/devmode.rst | 5 +++-- Doc/tools/.nitignore | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/library/devmode.rst b/Doc/library/devmode.rst index bc66815d57f34d..b3a66bf5383fdb 100644 --- a/Doc/library/devmode.rst +++ b/Doc/library/devmode.rst @@ -59,8 +59,9 @@ Effects of the Python Development Mode: ``default``. * Call :func:`faulthandler.enable` at Python startup to install handlers for - the :const:`SIGSEGV`, :const:`SIGFPE`, :const:`SIGABRT`, :const:`SIGBUS` and - :const:`SIGILL` signals to dump the Python traceback on a crash. + the :const:`~signal.SIGSEGV`, :const:`~signal.SIGFPE`, + :const:`~signal.SIGABRT`, :const:`~signal.SIGBUS` and + :const:`~signal.SIGILL` signals to dump the Python traceback on a crash. It behaves as if the :option:`-X faulthandler <-X>` command line option is used or if the :envvar:`PYTHONFAULTHANDLER` environment variable is set to diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 49566e61cdc35c..03087476c83c80 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -55,7 +55,6 @@ Doc/library/ctypes.rst Doc/library/datetime.rst Doc/library/dbm.rst Doc/library/decimal.rst -Doc/library/devmode.rst Doc/library/difflib.rst Doc/library/dis.rst Doc/library/doctest.rst From 448a7071b12d019326604e0a7424d1f368f7d036 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 27 Sep 2023 08:58:20 -0700 Subject: [PATCH 367/632] [3.11] gh-109615: Fix support test_copy_python_src_ignore() on WASM (GH-109970) (#109976) gh-109615: Fix support test_copy_python_src_ignore() on WASM (GH-109970) Not only check if src_dir exists, but look also for Lib/os.py landmark. (cherry picked from commit cc54bcf17b5b5f7681f52baf3acef75b995fa1fd) Co-authored-by: Victor Stinner --- Lib/test/test_support.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index 53220ec04a9c94..2f70f919d4043f 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -779,17 +779,25 @@ def recursive_function(depth): #self.assertEqual(available, 2) def test_copy_python_src_ignore(self): + # Get source directory src_dir = sysconfig.get_config_var('abs_srcdir') if not src_dir: src_dir = sysconfig.get_config_var('srcdir') src_dir = os.path.abspath(src_dir) + + # Check that the source code is available if not os.path.exists(src_dir): self.skipTest(f"cannot access Python source code directory:" f" {src_dir!r}") + landmark = os.path.join(src_dir, 'Lib', 'os.py') + if not os.path.exists(landmark): + self.skipTest(f"cannot access Python source code directory:" + f" {landmark!r} landmark is missing") - ignored = {'.git', '__pycache__'} + # Test support.copy_python_src_ignore() # Source code directory + ignored = {'.git', '__pycache__'} names = os.listdir(src_dir) self.assertEqual(support.copy_python_src_ignore(src_dir, names), ignored | {'build'}) From 38526a29ed1f45fdc4a4b2f373f63376c4069f1a Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 27 Sep 2023 10:54:27 -0700 Subject: [PATCH 368/632] [3.11] Enhance TypedDict docs around required/optional keys (GH-109547) (#109983) As discussed in comments to GH-109544, the semantics of this attribute are somewhat confusing. Add a note explaining its limitations and steering users towards __required_keys__ and __optional_keys__ instead. (cherry picked from commit f49958c886a2f2608f1008186d588efc2a98b445) Co-authored-by: Jelle Zijlstra --- Doc/library/typing.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 79f3abd865f6eb..57384b05fe23c9 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -2131,6 +2131,13 @@ types. >>> Point3D.__total__ True + This attribute reflects *only* the value of the ``total`` argument + to the current ``TypedDict`` class, not whether the class is semantically + total. For example, a ``TypedDict`` with ``__total__`` set to True may + have keys marked with :data:`NotRequired`, or it may inherit from another + ``TypedDict`` with ``total=False``. Therefore, it is generally better to use + :attr:`__required_keys__` and :attr:`__optional_keys__` for introspection. + .. attribute:: __required_keys__ .. versionadded:: 3.9 @@ -2166,6 +2173,14 @@ types. .. versionadded:: 3.9 + .. note:: + + If ``from __future__ import annotations`` is used or if annotations + are given as strings, annotations are not evaluated when the + ``TypedDict`` is defined. Therefore, the runtime introspection that + ``__required_keys__`` and ``__optional_keys__`` rely on may not work + properly, and the values of the attributes may be incorrect. + See :pep:`589` for more examples and detailed rules of using ``TypedDict``. .. versionadded:: 3.8 From 1fd6a73bd8cc7f496a09966a9fe68c27d8180a40 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Thu, 28 Sep 2023 01:31:17 -0400 Subject: [PATCH 369/632] [3.11] gh-109991: Update GitHub CI workflows to use OpenSSL 3.0.11 and multissltests to use 1.1.1w, 3.0.11, and 3.1.3. (#110006) (cherry picked from commit c88037d137a98d7c399c7bd74d5117b5bcae1543) --- .github/workflows/build.yml | 6 +++--- .../2023-09-27-23-31-54.gh-issue-109991.sUUYY8.rst | 2 ++ Tools/ssl/multissltests.py | 6 +++--- 3 files changed, 8 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Tools-Demos/2023-09-27-23-31-54.gh-issue-109991.sUUYY8.rst diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4709c3d7659c6a..cebcc3ef182b8f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -290,7 +290,7 @@ jobs: needs: check_source if: needs.check_source.outputs.run_tests == 'true' env: - OPENSSL_VER: 1.1.1v + OPENSSL_VER: 3.0.11 PYTHONSTRICTEXTENSIONBUILD: 1 steps: - uses: actions/checkout@v4 @@ -359,7 +359,7 @@ jobs: strategy: fail-fast: false matrix: - openssl_ver: [1.1.1v, 3.0.10, 3.1.2] + openssl_ver: [1.1.1w, 3.0.11, 3.1.3] env: OPENSSL_VER: ${{ matrix.openssl_ver }} MULTISSL_DIR: ${{ github.workspace }}/multissl @@ -411,7 +411,7 @@ jobs: needs: check_source if: needs.check_source.outputs.run_tests == 'true' env: - OPENSSL_VER: 1.1.1v + OPENSSL_VER: 3.0.11 PYTHONSTRICTEXTENSIONBUILD: 1 ASAN_OPTIONS: detect_leaks=0:allocator_may_return_null=1:handle_segv=0 steps: diff --git a/Misc/NEWS.d/next/Tools-Demos/2023-09-27-23-31-54.gh-issue-109991.sUUYY8.rst b/Misc/NEWS.d/next/Tools-Demos/2023-09-27-23-31-54.gh-issue-109991.sUUYY8.rst new file mode 100644 index 00000000000000..13c1163ab53443 --- /dev/null +++ b/Misc/NEWS.d/next/Tools-Demos/2023-09-27-23-31-54.gh-issue-109991.sUUYY8.rst @@ -0,0 +1,2 @@ +Update GitHub CI workflows to use OpenSSL 3.0.11 and multissltests to use +1.1.1w, 3.0.11, and 3.1.3. diff --git a/Tools/ssl/multissltests.py b/Tools/ssl/multissltests.py index 96500b024c00b7..7b9983fe05206a 100755 --- a/Tools/ssl/multissltests.py +++ b/Tools/ssl/multissltests.py @@ -47,9 +47,9 @@ ] OPENSSL_RECENT_VERSIONS = [ - "1.1.1v", - "3.0.10", - "3.1.2", + "1.1.1w", + "3.0.11", + "3.1.3", ] LIBRESSL_OLD_VERSIONS = [ From 82dea84dc491b8d6bdf7242811b5d7427e1eeddc Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 27 Sep 2023 23:34:24 -0700 Subject: [PATCH 370/632] [3.11] gh-109991: Update macOS installer to use OpenSSL 3.0.10. (GH-110010) (cherry picked from commit 98c0c1de18e9ec02a0dde0a89b9acf9415891de2) Co-authored-by: Ned Deily --- Mac/BuildScript/build-installer.py | 6 +++--- .../macOS/2023-09-27-22-35-22.gh-issue-109991.-xJzaF.rst | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/macOS/2023-09-27-22-35-22.gh-issue-109991.-xJzaF.rst diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index c10daa0a2dd07c..980160dd3077d7 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -246,9 +246,9 @@ def library_recipes(): result.extend([ dict( - name="OpenSSL 3.0.10", - url="https://www.openssl.org/source/openssl-3.0.10.tar.gz", - checksum='1761d4f5b13a1028b9b6f3d4b8e17feb0cedc9370f6afe61d7193d2cdce83323', + name="OpenSSL 3.0.11", + url="https://www.openssl.org/source/openssl-3.0.11.tar.gz", + checksum='b3425d3bb4a2218d0697eb41f7fc0cdede016ed19ca49d168b78e8d947887f55', buildrecipe=build_universal_openssl, configure=None, install=None, diff --git a/Misc/NEWS.d/next/macOS/2023-09-27-22-35-22.gh-issue-109991.-xJzaF.rst b/Misc/NEWS.d/next/macOS/2023-09-27-22-35-22.gh-issue-109991.-xJzaF.rst new file mode 100644 index 00000000000000..8d369988274f28 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2023-09-27-22-35-22.gh-issue-109991.-xJzaF.rst @@ -0,0 +1 @@ +Update macOS installer to use OpenSSL 3.0.11. From ff3cadd2a1df9bdf0b82bf21ae1d70e5bda23a5c Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 28 Sep 2023 06:49:56 -0700 Subject: [PATCH 371/632] [3.11] gh-109594: Fix concurrent.futures test_timeout() (GH-110018) (#110022) gh-109594: Fix concurrent.futures test_timeout() (GH-110018) Fix test_timeout() of test_concurrent_futures.test_wait. Remove the future which may or may not complete depending if it takes longer than the timeout ot not. Keep the second future which does not complete before wait(). Make also the test faster: 0.5 second instead of 6 seconds, so remove @support.requires_resource('walltime') decorator. (cherry picked from commit 9be283e5e15d5d5685b78a38eb132501f7f3febb) Co-authored-by: Victor Stinner --- Lib/test/test_concurrent_futures/test_wait.py | 16 +++++++++------- ...023-09-28-14-47-14.gh-issue-109594.DB5KPP.rst | 4 ++++ 2 files changed, 13 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2023-09-28-14-47-14.gh-issue-109594.DB5KPP.rst diff --git a/Lib/test/test_concurrent_futures/test_wait.py b/Lib/test/test_concurrent_futures/test_wait.py index e4bea8b05aced6..bf96f9f577b0e6 100644 --- a/Lib/test/test_concurrent_futures/test_wait.py +++ b/Lib/test/test_concurrent_futures/test_wait.py @@ -111,22 +111,24 @@ def test_all_completed(self): self.assertEqual(set(), pending) def test_timeout(self): - future1 = self.executor.submit(mul, 6, 7) - future2 = self.executor.submit(time.sleep, 6) + short_timeout = 0.050 + long_timeout = short_timeout * 10 + + future = self.executor.submit(time.sleep, long_timeout) finished, pending = futures.wait( [CANCELLED_AND_NOTIFIED_FUTURE, EXCEPTION_FUTURE, SUCCESSFUL_FUTURE, - future1, future2], - timeout=5, + future], + timeout=short_timeout, return_when=futures.ALL_COMPLETED) self.assertEqual(set([CANCELLED_AND_NOTIFIED_FUTURE, EXCEPTION_FUTURE, - SUCCESSFUL_FUTURE, - future1]), finished) - self.assertEqual(set([future2]), pending) + SUCCESSFUL_FUTURE]), + finished) + self.assertEqual(set([future]), pending) class ThreadPoolWaitTests(ThreadPoolMixin, WaitTests, BaseTestCase): diff --git a/Misc/NEWS.d/next/Tests/2023-09-28-14-47-14.gh-issue-109594.DB5KPP.rst b/Misc/NEWS.d/next/Tests/2023-09-28-14-47-14.gh-issue-109594.DB5KPP.rst new file mode 100644 index 00000000000000..5a4ae2b0837df6 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-09-28-14-47-14.gh-issue-109594.DB5KPP.rst @@ -0,0 +1,4 @@ +Fix test_timeout() of test_concurrent_futures.test_wait. Remove the future +which may or may not complete depending if it takes longer than the timeout +ot not. Keep the second future which does not complete before wait() +timeout. Patch by Victor Stinner. From 973d549e72dc81594086cbac294274ed0ab5bf40 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 28 Sep 2023 10:48:58 -0700 Subject: [PATCH 372/632] [3.11] gh-110033: Fix signal test_interprocess_signal() (GH-110035) (#110041) gh-110033: Fix signal test_interprocess_signal() (GH-110035) Fix test_interprocess_signal() of test_signal. Make sure that the subprocess.Popen object is deleted before the test raising an exception in a signal handler. Otherwise, Popen.__del__() can get the exception which is logged as "Exception ignored in: ...." and the test fails. (cherry picked from commit 7e0fbf5175fcf21dae390ba68b7f49706d62aa49) Co-authored-by: Victor Stinner --- Lib/test/signalinterproctester.py | 8 ++++++++ .../Tests/2023-09-28-18-14-52.gh-issue-110033.2yHMx0.rst | 5 +++++ 2 files changed, 13 insertions(+) create mode 100644 Misc/NEWS.d/next/Tests/2023-09-28-18-14-52.gh-issue-110033.2yHMx0.rst diff --git a/Lib/test/signalinterproctester.py b/Lib/test/signalinterproctester.py index bc60b747f71680..5dafc029005733 100644 --- a/Lib/test/signalinterproctester.py +++ b/Lib/test/signalinterproctester.py @@ -1,3 +1,4 @@ +import gc import os import signal import subprocess @@ -60,6 +61,13 @@ def test_interprocess_signal(self): self.assertEqual(self.got_signals, {'SIGHUP': 1, 'SIGUSR1': 0, 'SIGALRM': 0}) + # gh-110033: Make sure that the subprocess.Popen is deleted before + # the next test which raises an exception. Otherwise, the exception + # may be raised when Popen.__del__() is executed and so be logged + # as "Exception ignored in: ". + child = None + gc.collect() + with self.assertRaises(SIGUSR1Exception): with self.subprocess_send_signal(pid, "SIGUSR1") as child: self.wait_signal(child, 'SIGUSR1') diff --git a/Misc/NEWS.d/next/Tests/2023-09-28-18-14-52.gh-issue-110033.2yHMx0.rst b/Misc/NEWS.d/next/Tests/2023-09-28-18-14-52.gh-issue-110033.2yHMx0.rst new file mode 100644 index 00000000000000..fb6089377083bf --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-09-28-18-14-52.gh-issue-110033.2yHMx0.rst @@ -0,0 +1,5 @@ +Fix ``test_interprocess_signal()`` of ``test_signal``. Make sure that the +``subprocess.Popen`` object is deleted before the test raising an exception +in a signal handler. Otherwise, ``Popen.__del__()`` can get the exception +which is logged as ``Exception ignored in: ...`` and the test fails. Patch by +Victor Stinner. From d6e8001467a8d1b6e7a6d9b45ac08702ac19f1cb Mon Sep 17 00:00:00 2001 From: Davide Rizzo Date: Thu, 28 Sep 2023 20:58:24 +0200 Subject: [PATCH 373/632] [3.11] gh-110038: KqueueSelector must count all read/write events (GH-110039) (#110044) [3.11] gh-110038: KqueueSelector must count all read/write events (GH-110039). (cherry picked from commit b14f0ab51cb4851b25935279617e388456dcf716) --- Lib/selectors.py | 7 ++++- Lib/test/test_selectors.py | 29 +++++++++++++++++++ ...-09-28-18-50-33.gh-issue-110038.nx_gCu.rst | 3 ++ 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2023-09-28-18-50-33.gh-issue-110038.nx_gCu.rst diff --git a/Lib/selectors.py b/Lib/selectors.py index af6a4f94b5008a..c3b065b52264aa 100644 --- a/Lib/selectors.py +++ b/Lib/selectors.py @@ -509,6 +509,7 @@ class KqueueSelector(_BaseSelectorImpl): def __init__(self): super().__init__() self._selector = select.kqueue() + self._max_events = 0 def fileno(self): return self._selector.fileno() @@ -520,10 +521,12 @@ def register(self, fileobj, events, data=None): kev = select.kevent(key.fd, select.KQ_FILTER_READ, select.KQ_EV_ADD) self._selector.control([kev], 0, 0) + self._max_events += 1 if events & EVENT_WRITE: kev = select.kevent(key.fd, select.KQ_FILTER_WRITE, select.KQ_EV_ADD) self._selector.control([kev], 0, 0) + self._max_events += 1 except: super().unregister(fileobj) raise @@ -534,6 +537,7 @@ def unregister(self, fileobj): if key.events & EVENT_READ: kev = select.kevent(key.fd, select.KQ_FILTER_READ, select.KQ_EV_DELETE) + self._max_events -= 1 try: self._selector.control([kev], 0, 0) except OSError: @@ -543,6 +547,7 @@ def unregister(self, fileobj): if key.events & EVENT_WRITE: kev = select.kevent(key.fd, select.KQ_FILTER_WRITE, select.KQ_EV_DELETE) + self._max_events -= 1 try: self._selector.control([kev], 0, 0) except OSError: @@ -555,7 +560,7 @@ def select(self, timeout=None): # If max_ev is 0, kqueue will ignore the timeout. For consistent # behavior with the other selector classes, we prevent that here # (using max). See https://bugs.python.org/issue29255 - max_ev = max(len(self._fd_to_key), 1) + max_ev = self._max_events or 1 ready = [] try: kev_list = self._selector.control(None, max_ev, timeout) diff --git a/Lib/test/test_selectors.py b/Lib/test/test_selectors.py index 12ecc50d550c4f..31757205ca37c5 100644 --- a/Lib/test/test_selectors.py +++ b/Lib/test/test_selectors.py @@ -279,6 +279,35 @@ def test_select(self): self.assertEqual([(wr_key, selectors.EVENT_WRITE)], result) + def test_select_read_write(self): + # gh-110038: when a file descriptor is registered for both read and + # write, the two events must be seen on a single call to select(). + s = self.SELECTOR() + self.addCleanup(s.close) + + sock1, sock2 = self.make_socketpair() + sock2.send(b"foo") + my_key = s.register(sock1, selectors.EVENT_READ | selectors.EVENT_WRITE) + + seen_read, seen_write = False, False + result = s.select() + # We get the read and write either in the same result entry or in two + # distinct entries with the same key. + self.assertLessEqual(len(result), 2) + for key, events in result: + self.assertTrue(isinstance(key, selectors.SelectorKey)) + self.assertEqual(key, my_key) + self.assertFalse(events & ~(selectors.EVENT_READ | + selectors.EVENT_WRITE)) + if events & selectors.EVENT_READ: + self.assertFalse(seen_read) + seen_read = True + if events & selectors.EVENT_WRITE: + self.assertFalse(seen_write) + seen_write = True + self.assertTrue(seen_read) + self.assertTrue(seen_write) + def test_context_manager(self): s = self.SELECTOR() self.addCleanup(s.close) diff --git a/Misc/NEWS.d/next/Library/2023-09-28-18-50-33.gh-issue-110038.nx_gCu.rst b/Misc/NEWS.d/next/Library/2023-09-28-18-50-33.gh-issue-110038.nx_gCu.rst new file mode 100644 index 00000000000000..6b2abd802fccdc --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-09-28-18-50-33.gh-issue-110038.nx_gCu.rst @@ -0,0 +1,3 @@ +Fixed an issue that caused :meth:`KqueueSelector.select` to not return all +the ready events in some cases when a file descriptor is registered for both +read and write. From a99729599a70d73e8796f6c9d5cdf6807bda0cd5 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 28 Sep 2023 17:30:38 -0700 Subject: [PATCH 374/632] [3.11] gh-109960: Remove test_pty timeout of 10 seconds (GH-110058) (#110061) gh-109960: Remove test_pty timeout of 10 seconds (GH-110058) In 2003, test_pty got a hardcoded timeout of 10 seconds to prevent hanging on AIX & HPUX "if run after test_openpty": commit 7d8145268ee282f14d6adce9305dc3c1c7ffec14. Since 2003, test_pty was no longer reported to hang on AIX. But today, the test can fail simply because a CI is busy running other tests in parallel. The timeout of 10 seconds is no longer needed, just remove it. Moreover, regrtest now has multiple built-in generic timeout mecanisms. (cherry picked from commit 5fdcea744024c8a19ddb57057bf5ec2889546c98) Co-authored-by: Victor Stinner --- Lib/test/test_pty.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/Lib/test/test_pty.py b/Lib/test/test_pty.py index c9c2b42861c6f4..a971f6b0250efb 100644 --- a/Lib/test/test_pty.py +++ b/Lib/test/test_pty.py @@ -80,17 +80,9 @@ def expectedFailureIfStdinIsTTY(fun): # because pty code is not too portable. class PtyTest(unittest.TestCase): def setUp(self): - old_alarm = signal.signal(signal.SIGALRM, self.handle_sig) - self.addCleanup(signal.signal, signal.SIGALRM, old_alarm) - old_sighup = signal.signal(signal.SIGHUP, self.handle_sighup) self.addCleanup(signal.signal, signal.SIGHUP, old_sighup) - # isatty() and close() can hang on some platforms. Set an alarm - # before running the test to make sure we don't hang forever. - self.addCleanup(signal.alarm, 0) - signal.alarm(10) - # Save original stdin window size. self.stdin_dim = None if _HAVE_WINSZ: @@ -101,9 +93,6 @@ def setUp(self): except tty.error: pass - def handle_sig(self, sig, frame): - self.fail("isatty hung") - @staticmethod def handle_sighup(signum, frame): pass From efe83ad2762e3ea8b396b44947033445c9b31d04 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 28 Sep 2023 18:04:58 -0700 Subject: [PATCH 375/632] [3.11] gh-110036: multiprocessing Popen.terminate() catches PermissionError (GH-110037) (#110065) gh-110036: multiprocessing Popen.terminate() catches PermissionError (GH-110037) On Windows, multiprocessing Popen.terminate() now catchs PermissionError and get the process exit code. If the process is still running, raise again the PermissionError. Otherwise, the process terminated as expected: store its exit code. (cherry picked from commit bd4518c60c9df356cf5e05b81305e3644ebb5e70) Co-authored-by: Victor Stinner --- Lib/multiprocessing/popen_spawn_win32.py | 11 +++++++++-- Lib/test/_test_multiprocessing.py | 5 +++-- .../2023-09-28-18-53-11.gh-issue-110036.fECxTj.rst | 5 +++++ 3 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-09-28-18-53-11.gh-issue-110036.fECxTj.rst diff --git a/Lib/multiprocessing/popen_spawn_win32.py b/Lib/multiprocessing/popen_spawn_win32.py index 4d60ffc030bea6..af044305709e56 100644 --- a/Lib/multiprocessing/popen_spawn_win32.py +++ b/Lib/multiprocessing/popen_spawn_win32.py @@ -14,6 +14,7 @@ # # +# Exit code used by Popen.terminate() TERMINATE = 0x10000 WINEXE = (sys.platform == 'win32' and getattr(sys, 'frozen', False)) WINSERVICE = sys.executable.lower().endswith("pythonservice.exe") @@ -122,9 +123,15 @@ def terminate(self): if self.returncode is None: try: _winapi.TerminateProcess(int(self._handle), TERMINATE) - except OSError: - if self.wait(timeout=1.0) is None: + except PermissionError: + # ERROR_ACCESS_DENIED (winerror 5) is received when the + # process already died. + code = _winapi.GetExitCodeProcess(int(self._handle)) + if code == _winapi.STILL_ACTIVE: raise + self.returncode = code + else: + self.returncode = -signal.SIGTERM kill = terminate diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index c3ba1f686b0021..6249062639b8d4 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -556,13 +556,14 @@ def handler(*args): def test_terminate(self): exitcode = self._kill_process(multiprocessing.Process.terminate) - if os.name != 'nt': - self.assertEqual(exitcode, -signal.SIGTERM) + self.assertEqual(exitcode, -signal.SIGTERM) def test_kill(self): exitcode = self._kill_process(multiprocessing.Process.kill) if os.name != 'nt': self.assertEqual(exitcode, -signal.SIGKILL) + else: + self.assertEqual(exitcode, -signal.SIGTERM) def test_cpu_count(self): try: diff --git a/Misc/NEWS.d/next/Library/2023-09-28-18-53-11.gh-issue-110036.fECxTj.rst b/Misc/NEWS.d/next/Library/2023-09-28-18-53-11.gh-issue-110036.fECxTj.rst new file mode 100644 index 00000000000000..ddb11b5c3546a1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-09-28-18-53-11.gh-issue-110036.fECxTj.rst @@ -0,0 +1,5 @@ +On Windows, multiprocessing ``Popen.terminate()`` now catchs +:exc:`PermissionError` and get the process exit code. If the process is +still running, raise again the :exc:`PermissionError`. Otherwise, the +process terminated as expected: store its exit code. Patch by Victor +Stinner. From 615d7fc34a2b584ba39980db6ed99c90a90a445c Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 29 Sep 2023 04:43:28 +0200 Subject: [PATCH 376/632] [3.11] gh-110052: Fix faulthandler for freed tstate (#110069) (#110072) gh-110052: Fix faulthandler for freed tstate (#110069) faulthandler now detected freed interp and freed tstate, and no longer dereference them. Backport to 3.11: add pycore_pymem.h include to traceback.c. (cherry picked from commit 2e37a38bcbfbe1357436e030538290e7d00b668d) --- Modules/faulthandler.c | 3 +-- Python/traceback.c | 48 +++++++++++++++++++++++++++++++++--------- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/Modules/faulthandler.c b/Modules/faulthandler.c index f69f56bfdc9381..520b7b603e6437 100644 --- a/Modules/faulthandler.c +++ b/Modules/faulthandler.c @@ -231,7 +231,6 @@ faulthandler_dump_traceback(int fd, int all_threads, PyInterpreterState *interp) { static volatile int reentrant = 0; - PyThreadState *tstate; if (reentrant) return; @@ -246,7 +245,7 @@ faulthandler_dump_traceback(int fd, int all_threads, fault if the thread released the GIL, and so this function cannot be used. Read the thread specific storage (TSS) instead: call PyGILState_GetThisThreadState(). */ - tstate = PyGILState_GetThisThreadState(); + PyThreadState *tstate = PyGILState_GetThisThreadState(); if (all_threads) { (void)_Py_DumpTracebackThreads(fd, NULL, tstate); diff --git a/Python/traceback.c b/Python/traceback.c index 7010736ad0c539..c4f5ec877bba5d 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -12,6 +12,7 @@ #include "pycore_parser.h" // _PyParser_ASTFromString #include "pycore_pyarena.h" // _PyArena_Free() #include "pycore_pyerrors.h" // _PyErr_Fetch() +#include "pycore_pymem.h" // _PyMem_IsPtrFreed() #include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_traceback.h" // EXCEPTION_TB_HEADER @@ -1234,23 +1235,45 @@ dump_frame(int fd, _PyInterpreterFrame *frame) PUTS(fd, "\n"); } +static int +tstate_is_freed(PyThreadState *tstate) +{ + if (_PyMem_IsPtrFreed(tstate)) { + return 1; + } + if (_PyMem_IsPtrFreed(tstate->interp)) { + return 1; + } + return 0; +} + + +static int +interp_is_freed(PyInterpreterState *interp) +{ + return _PyMem_IsPtrFreed(interp); +} + + static void dump_traceback(int fd, PyThreadState *tstate, int write_header) { - _PyInterpreterFrame *frame; - unsigned int depth; - if (write_header) { PUTS(fd, "Stack (most recent call first):\n"); } - frame = tstate->cframe->current_frame; + if (tstate_is_freed(tstate)) { + PUTS(fd, " \n"); + return; + } + + _PyInterpreterFrame *frame = tstate->cframe->current_frame; if (frame == NULL) { PUTS(fd, " \n"); return; } - depth = 0; + unsigned int depth = 0; while (1) { if (MAX_FRAME_DEPTH <= depth) { PUTS(fd, " ...\n"); @@ -1305,9 +1328,6 @@ const char* _Py_DumpTracebackThreads(int fd, PyInterpreterState *interp, PyThreadState *current_tstate) { - PyThreadState *tstate; - unsigned int nthreads; - if (current_tstate == NULL) { /* _Py_DumpTracebackThreads() is called from signal handlers by faulthandler. @@ -1323,6 +1343,10 @@ _Py_DumpTracebackThreads(int fd, PyInterpreterState *interp, current_tstate = PyGILState_GetThisThreadState(); } + if (current_tstate != NULL && tstate_is_freed(current_tstate)) { + return "tstate is freed"; + } + if (interp == NULL) { if (current_tstate == NULL) { interp = _PyGILState_GetInterpreterStateUnsafe(); @@ -1337,14 +1361,18 @@ _Py_DumpTracebackThreads(int fd, PyInterpreterState *interp, } assert(interp != NULL); + if (interp_is_freed(interp)) { + return "interp is freed"; + } + /* Get the current interpreter from the current thread */ - tstate = PyInterpreterState_ThreadHead(interp); + PyThreadState *tstate = PyInterpreterState_ThreadHead(interp); if (tstate == NULL) return "unable to get the thread head state"; /* Dump the traceback of each thread */ tstate = PyInterpreterState_ThreadHead(interp); - nthreads = 0; + unsigned int nthreads = 0; _Py_BEGIN_SUPPRESS_IPH do { From c534637106087db0e644a813dc49bcef351859c4 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 29 Sep 2023 09:39:31 +0300 Subject: [PATCH 377/632] [3.11] gh-109634: Use :samp: role (GH-109635) (GH-109778) (cherry picked from commit 92af0cc580051fd1129c7a86af2cbadeb2aa36dc) Co-authored-by: Jacob Coffee --- Doc/extending/windows.rst | 2 +- Doc/howto/logging-cookbook.rst | 4 +- Doc/howto/logging.rst | 2 +- Doc/library/codecs.rst | 26 +++++---- Doc/library/compileall.rst | 2 +- Doc/library/ensurepip.rst | 2 +- Doc/library/functions.rst | 2 +- Doc/library/html.parser.rst | 2 +- Doc/library/http.server.rst | 2 +- Doc/library/os.rst | 4 +- Doc/library/re.rst | 2 +- Doc/library/sys.rst | 4 +- Doc/library/urllib.parse.rst | 6 +-- Doc/reference/lexical_analysis.rst | 84 +++++++++++++++--------------- Doc/using/configure.rst | 10 ++-- Doc/using/windows.rst | 4 +- Doc/whatsnew/2.0.rst | 4 +- Doc/whatsnew/2.6.rst | 2 +- Doc/whatsnew/3.11.rst | 6 +-- Doc/whatsnew/3.3.rst | 2 +- Doc/whatsnew/3.4.rst | 2 +- Doc/whatsnew/3.8.rst | 2 +- Misc/NEWS.d/3.5.0rc1.rst | 2 +- Misc/NEWS.d/3.5.1rc1.rst | 2 +- Misc/NEWS.d/3.8.0a1.rst | 2 +- Misc/NEWS.d/3.8.0a4.rst | 2 +- Misc/NEWS.d/3.9.0a1.rst | 4 +- 27 files changed, 96 insertions(+), 92 deletions(-) diff --git a/Doc/extending/windows.rst b/Doc/extending/windows.rst index 28d0350f6f114d..6e17225d08f691 100644 --- a/Doc/extending/windows.rst +++ b/Doc/extending/windows.rst @@ -132,4 +132,4 @@ modules (including Python) to be able to see your identifiers, you have to say Developer Studio will throw in a lot of import libraries that you do not really need, adding about 100K to your executable. To get rid of them, use the Project Settings dialog, Link tab, to specify *ignore default libraries*. Add the -correct :file:`msvcrtxx.lib` to the list of libraries. +correct :file:`msvcrt{xx}.lib` to the list of libraries. diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index bf1e8636019100..9eb1f979cd3994 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -1728,7 +1728,7 @@ when (and if) the logged message is actually about to be output to a log by a handler. So the only slightly unusual thing which might trip you up is that the parentheses go around the format string and the arguments, not just the format string. That's because the __ notation is just syntax sugar for a constructor -call to one of the XXXMessage classes. +call to one of the :samp:`{XXX}Message` classes. If you prefer, you can use a :class:`LoggerAdapter` to achieve a similar effect to the above, as in the following example:: @@ -2644,7 +2644,7 @@ when (and if) the logged message is actually about to be output to a log by a handler. So the only slightly unusual thing which might trip you up is that the parentheses go around the format string and the arguments, not just the format string. That’s because the __ notation is just syntax sugar for a constructor -call to one of the ``XXXMessage`` classes shown above. +call to one of the :samp:`{XXX}Message` classes shown above. .. _filters-dictconfig: diff --git a/Doc/howto/logging.rst b/Doc/howto/logging.rst index f3ed98fd85a077..d8ea8eb27d9550 100644 --- a/Doc/howto/logging.rst +++ b/Doc/howto/logging.rst @@ -976,7 +976,7 @@ provided: #. :class:`NullHandler` instances do nothing with error messages. They are used by library developers who want to use logging, but want to avoid the 'No - handlers could be found for logger XXX' message which can be displayed if + handlers could be found for logger *XXX*' message which can be displayed if the library user has not configured logging. See :ref:`library-config` for more information. diff --git a/Doc/library/codecs.rst b/Doc/library/codecs.rst index 8225236350d22e..2db4a67d1973d5 100644 --- a/Doc/library/codecs.rst +++ b/Doc/library/codecs.rst @@ -345,9 +345,10 @@ The following error handlers can be used with all Python +-------------------------+-----------------------------------------------+ | ``'backslashreplace'`` | Replace with backslashed escape sequences. | | | On encoding, use hexadecimal form of Unicode | -| | code point with formats ``\xhh`` ``\uxxxx`` | -| | ``\Uxxxxxxxx``. On decoding, use hexadecimal | -| | form of byte value with format ``\xhh``. | +| | code point with formats :samp:`\\x{hh}` | +| | :samp:`\\u{xxxx}` :samp:`\\U{xxxxxxxx}`. | +| | On decoding, use hexadecimal form of byte | +| | value with format :samp:`\\x{hh}`. | | | Implemented in | | | :func:`backslashreplace_errors`. | +-------------------------+-----------------------------------------------+ @@ -373,8 +374,9 @@ The following error handlers are only applicable to encoding (within +=========================+===============================================+ | ``'xmlcharrefreplace'`` | Replace with XML/HTML numeric character | | | reference, which is a decimal form of Unicode | -| | code point with format ``&#num;`` Implemented | -| | in :func:`xmlcharrefreplace_errors`. | +| | code point with format :samp:`&#{num};`. | +| | Implemented in | +| | :func:`xmlcharrefreplace_errors`. | +-------------------------+-----------------------------------------------+ | ``'namereplace'`` | Replace with ``\N{...}`` escape sequences, | | | what appears in the braces is the Name | @@ -478,8 +480,9 @@ functions: Malformed data is replaced by a backslashed escape sequence. On encoding, use the hexadecimal form of Unicode code point with formats - ``\xhh`` ``\uxxxx`` ``\Uxxxxxxxx``. On decoding, use the hexadecimal form of - byte value with format ``\xhh``. + :samp:`\\x{hh}` :samp:`\\u{xxxx}` :samp:`\\U{xxxxxxxx}`. + On decoding, use the hexadecimal form of + byte value with format :samp:`\\x{hh}`. .. versionchanged:: 3.5 Works with decoding and translating. @@ -492,7 +495,7 @@ functions: The unencodable character is replaced by an appropriate XML/HTML numeric character reference, which is a decimal form of Unicode code point with - format ``&#num;`` . + format :samp:`&#{num};` . .. function:: namereplace_errors(exception) @@ -1346,9 +1349,10 @@ encodings. | | | supported. | +--------------------+---------+---------------------------+ | raw_unicode_escape | | Latin-1 encoding with | -| | | ``\uXXXX`` and | -| | | ``\UXXXXXXXX`` for other | -| | | code points. Existing | +| | | :samp:`\\u{XXXX}` and | +| | | :samp:`\\U{XXXXXXXX}` | +| | | for other code points. | +| | | Existing | | | | backslashes are not | | | | escaped in any way. | | | | It is used in the Python | diff --git a/Doc/library/compileall.rst b/Doc/library/compileall.rst index 4226348a17240a..75b97d6ff4588e 100644 --- a/Doc/library/compileall.rst +++ b/Doc/library/compileall.rst @@ -29,7 +29,7 @@ compile Python sources. Positional arguments are files to compile or directories that contain source files, traversed recursively. If no argument is given, behave as if - the command line was ``-l ``. + the command line was :samp:`-l {}`. .. cmdoption:: -l diff --git a/Doc/library/ensurepip.rst b/Doc/library/ensurepip.rst index d7f89cf96368b5..de3b93f5e61073 100644 --- a/Doc/library/ensurepip.rst +++ b/Doc/library/ensurepip.rst @@ -61,7 +61,7 @@ By default, ``pip`` is installed into the current virtual environment active virtual environment). The installation location can be controlled through two additional command line options: -* ``--root ``: Installs ``pip`` relative to the given root directory +* :samp:`--root {dir}`: Installs ``pip`` relative to the given root directory rather than the root of the currently active virtual environment (if any) or the default root for the current Python installation. * ``--user``: Installs ``pip`` into the user site packages directory rather diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 6e048aa7eaeeab..96cf5940d432c9 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -1270,7 +1270,7 @@ are always available. They are listed here in alphabetical order. * ``'xmlcharrefreplace'`` is only supported when writing to a file. Characters not supported by the encoding are replaced with the - appropriate XML character reference ``&#nnn;``. + appropriate XML character reference :samp:`&#{nnn};`. * ``'backslashreplace'`` replaces malformed data by Python's backslashed escape sequences. diff --git a/Doc/library/html.parser.rst b/Doc/library/html.parser.rst index 03aff25ce6117a..d35090111e0822 100644 --- a/Doc/library/html.parser.rst +++ b/Doc/library/html.parser.rst @@ -173,7 +173,7 @@ implementations do nothing (except for :meth:`~HTMLParser.handle_startendtag`): .. method:: HTMLParser.handle_charref(name) This method is called to process decimal and hexadecimal numeric character - references of the form ``&#NNN;`` and ``&#xNNN;``. For example, the decimal + references of the form :samp:`&#{NNN};` and :samp:`&#x{NNN};`. For example, the decimal equivalent for ``>`` is ``>``, whereas the hexadecimal is ``>``; in this case the method will receive ``'62'`` or ``'x3E'``. This method is never called if *convert_charrefs* is ``True``. diff --git a/Doc/library/http.server.rst b/Doc/library/http.server.rst index 6a564400708186..76c5956430f8e6 100644 --- a/Doc/library/http.server.rst +++ b/Doc/library/http.server.rst @@ -217,7 +217,7 @@ provides three different variants: attribute holds the default values for *message* and *explain* that will be used if no value is provided; for unknown codes the default value for both is the string ``???``. The body will be empty if the method is - HEAD or the response code is one of the following: ``1xx``, + HEAD or the response code is one of the following: :samp:`1{xx}`, ``204 No Content``, ``205 Reset Content``, ``304 Not Modified``. .. versionchanged:: 3.4 diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 75ffee0b8e3cfc..df01963e54a5e6 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -88,8 +88,8 @@ startup by the :c:func:`PyConfig_Read` function: see On some systems, conversion using the file system encoding may fail. In this case, Python uses the :ref:`surrogateescape encoding error handler `, which means that undecodable bytes are replaced by a - Unicode character U+DCxx on decoding, and these are again translated to the - original byte on encoding. + Unicode character U+DC\ *xx* on decoding, and these are again + translated to the original byte on encoding. The :term:`file system encoding ` must diff --git a/Doc/library/re.rst b/Doc/library/re.rst index 3693afc956daff..5bea6166a1e4ef 100644 --- a/Doc/library/re.rst +++ b/Doc/library/re.rst @@ -659,7 +659,7 @@ three digits in length. Unknown escapes consisting of ``'\'`` and an ASCII letter now are errors. .. versionchanged:: 3.8 - The ``'\N{name}'`` escape sequence has been added. As in string literals, + The :samp:`'\\N\\{{name}\\}'` escape sequence has been added. As in string literals, it expands to the named Unicode character (e.g. ``'\N{EM DASH}'``). diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index ab198732a427ea..34cb4cf038fe7e 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -742,7 +742,7 @@ always available. Return the current value of the flags that are used for :c:func:`dlopen` calls. Symbolic names for the flag values can be - found in the :mod:`os` module (``RTLD_xxx`` constants, e.g. + found in the :mod:`os` module (:samp:`RTLD_{xxx}` constants, e.g. :const:`os.RTLD_LAZY`). .. availability:: Unix. @@ -1409,7 +1409,7 @@ always available. lazy resolving of symbols when importing a module, if called as ``sys.setdlopenflags(0)``. To share symbols across extension modules, call as ``sys.setdlopenflags(os.RTLD_GLOBAL)``. Symbolic names for the flag values - can be found in the :mod:`os` module (``RTLD_xxx`` constants, e.g. + can be found in the :mod:`os` module (:samp:`RTLD_{xxx}` constants, e.g. :const:`os.RTLD_LAZY`). .. availability:: Unix. diff --git a/Doc/library/urllib.parse.rst b/Doc/library/urllib.parse.rst index 184d24f2c8e494..3e04131a222394 100644 --- a/Doc/library/urllib.parse.rst +++ b/Doc/library/urllib.parse.rst @@ -598,7 +598,7 @@ task isn't already covered by the URL parsing functions above. .. function:: quote(string, safe='/', encoding=None, errors=None) - Replace special characters in *string* using the ``%xx`` escape. Letters, + Replace special characters in *string* using the :samp:`%{xx}` escape. Letters, digits, and the characters ``'_.-~'`` are never quoted. By default, this function is intended for quoting the path section of a URL. The optional *safe* parameter specifies additional ASCII characters that should not be @@ -645,7 +645,7 @@ task isn't already covered by the URL parsing functions above. .. function:: unquote(string, encoding='utf-8', errors='replace') - Replace ``%xx`` escapes with their single-character equivalent. + Replace :samp:`%{xx}` escapes with their single-character equivalent. The optional *encoding* and *errors* parameters specify how to decode percent-encoded sequences into Unicode characters, as accepted by the :meth:`bytes.decode` method. @@ -676,7 +676,7 @@ task isn't already covered by the URL parsing functions above. .. function:: unquote_to_bytes(string) - Replace ``%xx`` escapes with their single-octet equivalent, and return a + Replace :samp:`%{xx}` escapes with their single-octet equivalent, and return a :class:`bytes` object. *string* may be either a :class:`str` or a :class:`bytes` object. diff --git a/Doc/reference/lexical_analysis.rst b/Doc/reference/lexical_analysis.rst index 8d07e071f71be8..4fadae3ea2b045 100644 --- a/Doc/reference/lexical_analysis.rst +++ b/Doc/reference/lexical_analysis.rst @@ -545,51 +545,51 @@ Unless an ``'r'`` or ``'R'`` prefix is present, escape sequences in string and bytes literals are interpreted according to rules similar to those used by Standard C. The recognized escape sequences are: -+-----------------+---------------------------------+-------+ -| Escape Sequence | Meaning | Notes | -+=================+=================================+=======+ -| ``\``\ | Backslash and newline ignored | \(1) | -+-----------------+---------------------------------+-------+ -| ``\\`` | Backslash (``\``) | | -+-----------------+---------------------------------+-------+ -| ``\'`` | Single quote (``'``) | | -+-----------------+---------------------------------+-------+ -| ``\"`` | Double quote (``"``) | | -+-----------------+---------------------------------+-------+ -| ``\a`` | ASCII Bell (BEL) | | -+-----------------+---------------------------------+-------+ -| ``\b`` | ASCII Backspace (BS) | | -+-----------------+---------------------------------+-------+ -| ``\f`` | ASCII Formfeed (FF) | | -+-----------------+---------------------------------+-------+ -| ``\n`` | ASCII Linefeed (LF) | | -+-----------------+---------------------------------+-------+ -| ``\r`` | ASCII Carriage Return (CR) | | -+-----------------+---------------------------------+-------+ -| ``\t`` | ASCII Horizontal Tab (TAB) | | -+-----------------+---------------------------------+-------+ -| ``\v`` | ASCII Vertical Tab (VT) | | -+-----------------+---------------------------------+-------+ -| ``\ooo`` | Character with octal value | (2,4) | -| | *ooo* | | -+-----------------+---------------------------------+-------+ -| ``\xhh`` | Character with hex value *hh* | (3,4) | -+-----------------+---------------------------------+-------+ ++-------------------------+---------------------------------+-------+ +| Escape Sequence | Meaning | Notes | ++=========================+=================================+=======+ +| ``\``\ | Backslash and newline ignored | \(1) | ++-------------------------+---------------------------------+-------+ +| ``\\`` | Backslash (``\``) | | ++-------------------------+---------------------------------+-------+ +| ``\'`` | Single quote (``'``) | | ++-------------------------+---------------------------------+-------+ +| ``\"`` | Double quote (``"``) | | ++-------------------------+---------------------------------+-------+ +| ``\a`` | ASCII Bell (BEL) | | ++-------------------------+---------------------------------+-------+ +| ``\b`` | ASCII Backspace (BS) | | ++-------------------------+---------------------------------+-------+ +| ``\f`` | ASCII Formfeed (FF) | | ++-------------------------+---------------------------------+-------+ +| ``\n`` | ASCII Linefeed (LF) | | ++-------------------------+---------------------------------+-------+ +| ``\r`` | ASCII Carriage Return (CR) | | ++-------------------------+---------------------------------+-------+ +| ``\t`` | ASCII Horizontal Tab (TAB) | | ++-------------------------+---------------------------------+-------+ +| ``\v`` | ASCII Vertical Tab (VT) | | ++-------------------------+---------------------------------+-------+ +| :samp:`\\\\{ooo}` | Character with octal value | (2,4) | +| | *ooo* | | ++-------------------------+---------------------------------+-------+ +| :samp:`\\x{hh}` | Character with hex value *hh* | (3,4) | ++-------------------------+---------------------------------+-------+ Escape sequences only recognized in string literals are: -+-----------------+---------------------------------+-------+ -| Escape Sequence | Meaning | Notes | -+=================+=================================+=======+ -| ``\N{name}`` | Character named *name* in the | \(5) | -| | Unicode database | | -+-----------------+---------------------------------+-------+ -| ``\uxxxx`` | Character with 16-bit hex value | \(6) | -| | *xxxx* | | -+-----------------+---------------------------------+-------+ -| ``\Uxxxxxxxx`` | Character with 32-bit hex value | \(7) | -| | *xxxxxxxx* | | -+-----------------+---------------------------------+-------+ ++-------------------------+---------------------------------+-------+ +| Escape Sequence | Meaning | Notes | ++=========================+=================================+=======+ +| :samp:`\\N\\{{name}\\}` | Character named *name* in the | \(5) | +| | Unicode database | | ++-------------------------+---------------------------------+-------+ +| :samp:`\\u{xxxx}` | Character with 16-bit hex value | \(6) | +| | *xxxx* | | ++-------------------------+---------------------------------+-------+ +| :samp:`\\U{xxxxxxxx}` | Character with 32-bit hex value | \(7) | +| | *xxxxxxxx* | | ++-------------------------+---------------------------------+-------+ Notes: diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst index ca3b88e079609a..04cd3769f22632 100644 --- a/Doc/using/configure.rst +++ b/Doc/using/configure.rst @@ -687,7 +687,7 @@ differently depending if the ``Py_BUILD_CORE_MODULE`` macro is defined: * Use ``Py_IMPORTED_SYMBOL`` otherwise. If the ``Py_BUILD_CORE_BUILTIN`` macro is used by mistake on a C extension -built as a shared library, its ``PyInit_xxx()`` function is not exported, +built as a shared library, its :samp:`PyInit_{xxx}()` function is not exported, causing an :exc:`ImportError` on import. @@ -708,8 +708,8 @@ Preprocessor flags .. envvar:: CPPFLAGS - (Objective) C/C++ preprocessor flags, e.g. ``-I`` if you have - headers in a nonstandard directory ````. + (Objective) C/C++ preprocessor flags, e.g. :samp:`-I{include_dir}` if you have + headers in a nonstandard directory *include_dir*. Both :envvar:`CPPFLAGS` and :envvar:`LDFLAGS` need to contain the shell's value for setup.py to be able to build extension modules using the @@ -903,8 +903,8 @@ Linker flags .. envvar:: LDFLAGS - Linker flags, e.g. ``-L`` if you have libraries in a nonstandard - directory ````. + Linker flags, e.g. :samp:`-L{lib_dir}` if you have libraries in a nonstandard + directory *lib_dir*. Both :envvar:`CPPFLAGS` and :envvar:`LDFLAGS` need to contain the shell's value for setup.py to be able to build extension modules using the diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst index 7dee82a2223a26..4c71b1db82cc6f 100644 --- a/Doc/using/windows.rst +++ b/Doc/using/windows.rst @@ -876,7 +876,7 @@ minor version. I.e. ``/usr/bin/python3.7-32`` will request usage of the The "-64" suffix is deprecated, and now implies "any architecture that is not provably i386/32-bit". To request a specific environment, use the new - ``-V:`` argument with the complete tag. + :samp:`-V:{TAG}` argument with the complete tag. The ``/usr/bin/env`` form of shebang line has one further special property. Before looking for installed Python interpreters, this form will search the @@ -1179,7 +1179,7 @@ non-standard paths in the registry and user site-packages. * Adds ``._pth`` file support and removes ``applocal`` option from ``pyvenv.cfg``. - * Adds ``pythonXX.zip`` as a potential landmark when directly adjacent + * Adds :file:`python{XX}.zip` as a potential landmark when directly adjacent to the executable. .. deprecated:: diff --git a/Doc/whatsnew/2.0.rst b/Doc/whatsnew/2.0.rst index 096248e41b8496..87cd09bd7d523b 100644 --- a/Doc/whatsnew/2.0.rst +++ b/Doc/whatsnew/2.0.rst @@ -153,9 +153,9 @@ Lundh. A detailed explanation of the interface was written up as :pep:`100`, significant points about the Unicode interfaces. In Python source code, Unicode strings are written as ``u"string"``. Arbitrary -Unicode characters can be written using a new escape sequence, ``\uHHHH``, where +Unicode characters can be written using a new escape sequence, :samp:`\\u{HHHH}`, where *HHHH* is a 4-digit hexadecimal number from 0000 to FFFF. The existing -``\xHHHH`` escape sequence can also be used, and octal escapes can be used for +:samp:`\\x{HH}` escape sequence can also be used, and octal escapes can be used for characters up to U+01FF, which is represented by ``\777``. Unicode strings, just like regular strings, are an immutable sequence type. diff --git a/Doc/whatsnew/2.6.rst b/Doc/whatsnew/2.6.rst index b975aea7d6894a..128407e3fba132 100644 --- a/Doc/whatsnew/2.6.rst +++ b/Doc/whatsnew/2.6.rst @@ -125,7 +125,7 @@ and to C extension code as :c:data:`Py_Py3kWarningFlag`. .. seealso:: - The 3xxx series of PEPs, which contains proposals for Python 3.0. + The 3\ *xxx* series of PEPs, which contains proposals for Python 3.0. :pep:`3000` describes the development process for Python 3.0. Start with :pep:`3100` that describes the general goals for Python 3.0, and then explore the higher-numbered PEPS that propose diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 175b9e5b2ab5f7..c93541d18582de 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -218,7 +218,7 @@ Windows ``py.exe`` launcher improvements The copy of the :ref:`launcher` included with Python 3.11 has been significantly updated. It now supports company/tag syntax as defined in :pep:`514` using the -``-V:/`` argument instead of the limited ``-.``. +:samp:`-V:{}/{}` argument instead of the limited :samp:`-{}.{}`. This allows launching distributions other than ``PythonCore``, the one hosted on `python.org `_. @@ -227,8 +227,8 @@ installs will be searched. For example, ``-V:OtherPython/`` will select the "best" tag registered for ``OtherPython``, while ``-V:3.11`` or ``-V:/3.11`` will select the "best" distribution with tag ``3.11``. -When using the legacy ``-``, ``-.``, -``--`` or ``-.-`` arguments, +When using the legacy :samp:`-{}`, :samp:`-{}.{}`, +:samp:`-{}-{}` or :samp:`-{}.{}-{}` arguments, all existing behaviour should be preserved from past versions, and only releases from ``PythonCore`` will be selected. However, the ``-64`` suffix now implies "not 32-bit" (not necessarily x86-64), diff --git a/Doc/whatsnew/3.3.rst b/Doc/whatsnew/3.3.rst index e0aa82dcdb8dff..becff2f27a309e 100644 --- a/Doc/whatsnew/3.3.rst +++ b/Doc/whatsnew/3.3.rst @@ -2066,7 +2066,7 @@ The :pep:`418` added new functions to the :mod:`time` module: Other new functions: * :func:`~time.clock_getres`, :func:`~time.clock_gettime` and - :func:`~time.clock_settime` functions with ``CLOCK_xxx`` constants. + :func:`~time.clock_settime` functions with :samp:`CLOCK_{xxx}` constants. (Contributed by Victor Stinner in :issue:`10278`.) To improve cross platform consistency, :func:`~time.sleep` now raises a diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst index b0d0417447691e..26f04b5f54fd46 100644 --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -2085,7 +2085,7 @@ Deprecations in the Python API :meth:`importlib.abc.MetaPathFinder.find_spec`; :meth:`importlib.abc.PathEntryFinder.find_loader` and :meth:`~importlib.abc.PathEntryFinder.find_module` are replaced by - :meth:`importlib.abc.PathEntryFinder.find_spec`; all of the ``xxxLoader`` ABC + :meth:`importlib.abc.PathEntryFinder.find_spec`; all of the :samp:`{xxx}Loader` ABC ``load_module`` methods (:meth:`importlib.abc.Loader.load_module`, :meth:`importlib.abc.InspectLoader.load_module`, :meth:`importlib.abc.FileLoader.load_module`, diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index 71b135de2d693c..b73bf14bcbf192 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -404,7 +404,7 @@ Other Language Changes or :meth:`~object.__complex__` is not available. (Contributed by Serhiy Storchaka in :issue:`20092`.) -* Added support of ``\N{name}`` escapes in :mod:`regular expressions `:: +* Added support of :samp:`\\N\\{{name}\\}` escapes in :mod:`regular expressions `:: >>> notice = 'Copyright © 2019' >>> copyright_year_pattern = re.compile(r'\N{copyright sign}\s*(\d{4})') diff --git a/Misc/NEWS.d/3.5.0rc1.rst b/Misc/NEWS.d/3.5.0rc1.rst index 1fb9bc6c04da38..64e9435b252acb 100644 --- a/Misc/NEWS.d/3.5.0rc1.rst +++ b/Misc/NEWS.d/3.5.0rc1.rst @@ -168,7 +168,7 @@ Sanad Zaki Rizvi. Idle editor default font. Switch from Courier to platform-sensitive TkFixedFont. This should not affect current customized font selections. If -there is a problem, edit $HOME/.idlerc/config-main.cfg and remove 'fontxxx' +there is a problem, edit $HOME/.idlerc/config-main.cfg and remove ':samp:`font{xxx}`' entries from [Editor Window]. Patch by Mark Roseman. .. diff --git a/Misc/NEWS.d/3.5.1rc1.rst b/Misc/NEWS.d/3.5.1rc1.rst index d06817ccb950ec..3535ca5d970370 100644 --- a/Misc/NEWS.d/3.5.1rc1.rst +++ b/Misc/NEWS.d/3.5.1rc1.rst @@ -189,7 +189,7 @@ comprehensions correspond to the opening brace. .. nonce: 0Gh-Ty .. section: Core and Builtins -Hide the private _Py_atomic_xxx symbols from the public Python.h header to +Hide the private :samp:`_Py_atomic_{xxx}` symbols from the public Python.h header to fix a compilation error with OpenMP. PyThreadState_GET() becomes an alias to PyThreadState_Get() to avoid ABI incompatibilities. diff --git a/Misc/NEWS.d/3.8.0a1.rst b/Misc/NEWS.d/3.8.0a1.rst index f0518bde996fd2..530260aba873cc 100644 --- a/Misc/NEWS.d/3.8.0a1.rst +++ b/Misc/NEWS.d/3.8.0a1.rst @@ -8253,7 +8253,7 @@ Explain how IDLE's Shell displays output. Improve the doc about IDLE running user code. The section is renamed from "IDLE -- console differences" is renamed "Running user code". It mostly -covers the implications of using custom sys.stdxxx objects. +covers the implications of using custom :samp:`sys.std{xxx}` objects. .. diff --git a/Misc/NEWS.d/3.8.0a4.rst b/Misc/NEWS.d/3.8.0a4.rst index 00079c8491fe6a..bd6faca7cd023c 100644 --- a/Misc/NEWS.d/3.8.0a4.rst +++ b/Misc/NEWS.d/3.8.0a4.rst @@ -1087,7 +1087,7 @@ on the ABI. Change ``PyAPI_FUNC(type)``, ``PyAPI_DATA(type)`` and ``PyMODINIT_FUNC`` macros of ``pyport.h`` when ``Py_BUILD_CORE_MODULE`` is defined. The ``Py_BUILD_CORE_MODULE`` define must be now be used to build a C extension -as a dynamic library accessing Python internals: export the PyInit_xxx() +as a dynamic library accessing Python internals: export the :samp:`PyInit_{xxx}()` function in DLL exports on Windows. .. diff --git a/Misc/NEWS.d/3.9.0a1.rst b/Misc/NEWS.d/3.9.0a1.rst index d18ed70841e0c5..23f53f2dc647aa 100644 --- a/Misc/NEWS.d/3.9.0a1.rst +++ b/Misc/NEWS.d/3.9.0a1.rst @@ -4118,7 +4118,7 @@ Add tests for ROT-13 codec. .. nonce: Zoe9ek .. section: Tests -Added tests for PyDateTime_xxx_GET_xxx() macros of the C API of the +Added tests for :samp:`PyDateTime_{xxx}_GET_{xxx}()` macros of the C API of the :mod:`datetime` module. Patch by Joannah Nanjekye. .. @@ -4576,7 +4576,7 @@ distutils bdist_wininst: bdist_wininst only works on Windows. .. nonce: j5ebdT .. section: Build -Many ``PyRun_XXX()`` functions like :c:func:`PyRun_String` were no longer +Many :samp:`PyRun_{XXX}()` functions like :c:func:`PyRun_String` were no longer exported in ``libpython38.dll`` by mistake. Export them again to fix the ABI compatibility. From 6bc722df2ee19579fabfc2d213a94bf2ac0e14d3 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 29 Sep 2023 02:19:34 -0600 Subject: [PATCH 378/632] [3.11] gh-101100: Fix sphinx warnings in `library/difflib.rst` (GH-110074) (#110082) Co-authored-by: Nikita Sobolev --- Doc/library/difflib.rst | 18 +++++++++--------- Doc/tools/.nitignore | 1 - 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Doc/library/difflib.rst b/Doc/library/difflib.rst index feaa7c1acde13e..431ee9c2c6ce17 100644 --- a/Doc/library/difflib.rst +++ b/Doc/library/difflib.rst @@ -574,8 +574,8 @@ The :class:`SequenceMatcher` class has this constructor: The three methods that return the ratio of matching to total characters can give different results due to differing levels of approximation, although -:meth:`quick_ratio` and :meth:`real_quick_ratio` are always at least as large as -:meth:`ratio`: +:meth:`~SequenceMatcher.quick_ratio` and :meth:`~SequenceMatcher.real_quick_ratio` +are always at least as large as :meth:`~SequenceMatcher.ratio`: >>> s = SequenceMatcher(None, "abcd", "bcde") >>> s.ratio() @@ -597,15 +597,15 @@ This example compares two strings, considering blanks to be "junk": ... "private Thread currentThread;", ... "private volatile Thread currentThread;") -:meth:`ratio` returns a float in [0, 1], measuring the similarity of the -sequences. As a rule of thumb, a :meth:`ratio` value over 0.6 means the +:meth:`~SequenceMatcher.ratio` returns a float in [0, 1], measuring the similarity of the +sequences. As a rule of thumb, a :meth:`~SequenceMatcher.ratio` value over 0.6 means the sequences are close matches: >>> print(round(s.ratio(), 3)) 0.866 If you're only interested in where the sequences match, -:meth:`get_matching_blocks` is handy: +:meth:`~SequenceMatcher.get_matching_blocks` is handy: >>> for block in s.get_matching_blocks(): ... print("a[%d] and b[%d] match for %d elements" % block) @@ -613,12 +613,12 @@ If you're only interested in where the sequences match, a[8] and b[17] match for 21 elements a[29] and b[38] match for 0 elements -Note that the last tuple returned by :meth:`get_matching_blocks` is always a -dummy, ``(len(a), len(b), 0)``, and this is the only case in which the last +Note that the last tuple returned by :meth:`~SequenceMatcher.get_matching_blocks` +is always a dummy, ``(len(a), len(b), 0)``, and this is the only case in which the last tuple element (number of elements matched) is ``0``. If you want to know how to change the first sequence into the second, use -:meth:`get_opcodes`: +:meth:`~SequenceMatcher.get_opcodes`: >>> for opcode in s.get_opcodes(): ... print("%6s a[%d:%d] b[%d:%d]" % opcode) @@ -693,7 +693,7 @@ Differ Example This example compares two texts. First we set up the texts, sequences of individual single-line strings ending with newlines (such sequences can also be -obtained from the :meth:`~io.BaseIO.readlines` method of file-like objects): +obtained from the :meth:`~io.IOBase.readlines` method of file-like objects): >>> text1 = ''' 1. Beautiful is better than ugly. ... 2. Explicit is better than implicit. diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 03087476c83c80..36ea3a95314ff0 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -55,7 +55,6 @@ Doc/library/ctypes.rst Doc/library/datetime.rst Doc/library/dbm.rst Doc/library/decimal.rst -Doc/library/difflib.rst Doc/library/dis.rst Doc/library/doctest.rst Doc/library/email.charset.rst From 58ac0ed093754c34884b152e05cbf5a009b49576 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 29 Sep 2023 01:36:24 -0700 Subject: [PATCH 379/632] [3.11] gh-101100: Fix Sphinx warnings in `tutorial/controlflow.rst` (GH-109424) (#110085) Co-authored-by: Maciej Olko Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/tools/.nitignore | 1 - Doc/tutorial/controlflow.rst | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 36ea3a95314ff0..dace135cb560f1 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -150,7 +150,6 @@ Doc/reference/datamodel.rst Doc/reference/expressions.rst Doc/reference/import.rst Doc/reference/simple_stmts.rst -Doc/tutorial/controlflow.rst Doc/tutorial/datastructures.rst Doc/tutorial/introduction.rst Doc/using/cmdline.rst diff --git a/Doc/tutorial/controlflow.rst b/Doc/tutorial/controlflow.rst index 4bcc3768111ccd..aa9caa101da40a 100644 --- a/Doc/tutorial/controlflow.rst +++ b/Doc/tutorial/controlflow.rst @@ -534,7 +534,7 @@ This example, as usual, demonstrates some new Python features: Different types define different methods. Methods of different types may have the same name without causing ambiguity. (It is possible to define your own object types and methods, using *classes*, see :ref:`tut-classes`) - The method :meth:`~list.append` shown in the example is defined for list objects; it + The method :meth:`!append` shown in the example is defined for list objects; it adds a new element at the end of the list. In this example it is equivalent to ``result = result + [a]``, but more efficient. @@ -1046,7 +1046,7 @@ Function Annotations information about the types used by user-defined functions (see :pep:`3107` and :pep:`484` for more information). -:term:`Annotations ` are stored in the :attr:`__annotations__` +:term:`Annotations ` are stored in the :attr:`!__annotations__` attribute of the function as a dictionary and have no effect on any other part of the function. Parameter annotations are defined by a colon after the parameter name, followed by an expression evaluating to the value of the annotation. Return annotations are From 6024a75b10679bacca9b5a8bb7736c106f82d508 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 29 Sep 2023 01:44:28 -0700 Subject: [PATCH 380/632] [3.11] gh-101100: Fix references to ``URLError`` and ``HTTPError`` in ``howto/urllib2.rst`` (GH-107966) (#110087) Co-authored-by: Yuki K Co-authored-by: Hugo van Kemenade --- Doc/howto/urllib2.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Doc/howto/urllib2.rst b/Doc/howto/urllib2.rst index 86137fb38c9b93..570435d48866d3 100644 --- a/Doc/howto/urllib2.rst +++ b/Doc/howto/urllib2.rst @@ -194,11 +194,11 @@ which comes after we have a look at what happens when things go wrong. Handling Exceptions =================== -*urlopen* raises :exc:`URLError` when it cannot handle a response (though as +*urlopen* raises :exc:`~urllib.error.URLError` when it cannot handle a response (though as usual with Python APIs, built-in exceptions such as :exc:`ValueError`, :exc:`TypeError` etc. may also be raised). -:exc:`HTTPError` is the subclass of :exc:`URLError` raised in the specific case of +:exc:`~urllib.error.HTTPError` is the subclass of :exc:`~urllib.error.URLError` raised in the specific case of HTTP URLs. The exception classes are exported from the :mod:`urllib.error` module. @@ -229,12 +229,12 @@ the status code indicates that the server is unable to fulfil the request. The default handlers will handle some of these responses for you (for example, if the response is a "redirection" that requests the client fetch the document from a different URL, urllib will handle that for you). For those it can't handle, -urlopen will raise an :exc:`HTTPError`. Typical errors include '404' (page not +urlopen will raise an :exc:`~urllib.error.HTTPError`. Typical errors include '404' (page not found), '403' (request forbidden), and '401' (authentication required). See section 10 of :rfc:`2616` for a reference on all the HTTP error codes. -The :exc:`HTTPError` instance raised will have an integer 'code' attribute, which +The :exc:`~urllib.error.HTTPError` instance raised will have an integer 'code' attribute, which corresponds to the error sent by the server. Error Codes @@ -317,7 +317,7 @@ dictionary is reproduced here for convenience :: } When an error is raised the server responds by returning an HTTP error code -*and* an error page. You can use the :exc:`HTTPError` instance as a response on the +*and* an error page. You can use the :exc:`~urllib.error.HTTPError` instance as a response on the page returned. This means that as well as the code attribute, it also has read, geturl, and info, methods as returned by the ``urllib.response`` module:: @@ -338,7 +338,7 @@ geturl, and info, methods as returned by the ``urllib.response`` module:: Wrapping it Up -------------- -So if you want to be prepared for :exc:`HTTPError` *or* :exc:`URLError` there are two +So if you want to be prepared for :exc:`~urllib.error.HTTPError` *or* :exc:`~urllib.error.URLError` there are two basic approaches. I prefer the second approach. Number 1 @@ -365,7 +365,7 @@ Number 1 .. note:: The ``except HTTPError`` *must* come first, otherwise ``except URLError`` - will *also* catch an :exc:`HTTPError`. + will *also* catch an :exc:`~urllib.error.HTTPError`. Number 2 ~~~~~~~~ @@ -391,7 +391,7 @@ Number 2 info and geturl =============== -The response returned by urlopen (or the :exc:`HTTPError` instance) has two +The response returned by urlopen (or the :exc:`~urllib.error.HTTPError` instance) has two useful methods :meth:`info` and :meth:`geturl` and is defined in the module :mod:`urllib.response`.. From d81bcc232765613b2e48a2b29d238bff8723a794 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Fri, 29 Sep 2023 10:17:44 +0100 Subject: [PATCH 381/632] [3.11] gh-102304: Add links to the Stable ABI and Limited C API docs (#105345) (#105371) (#109901) * Add "limited-c-api" and "stable-api" targets * Rename the "stable-abi-list" target to "limited-api-list" --- Doc/c-api/exceptions.rst | 4 +-- Doc/c-api/stable.rst | 49 +++++++++++++++++++----------- Doc/c-api/structures.rst | 2 +- Doc/c-api/type.rst | 4 +-- Doc/c-api/typeobj.rst | 2 +- Doc/c-api/unicode.rst | 2 +- Doc/howto/isolating-extensions.rst | 2 +- Doc/whatsnew/3.11.rst | 2 +- Misc/NEWS.d/3.11.0a1.rst | 2 +- Misc/NEWS.d/3.11.0a2.rst | 2 +- 10 files changed, 42 insertions(+), 29 deletions(-) diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst index 82050845237bbd..320f1e6dbcc4ea 100644 --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -806,7 +806,7 @@ because the :ref:`call protocol ` takes care of recursion handling. depth limit. .. versionchanged:: 3.9 - This function is now also available in the limited API. + This function is now also available in the :ref:`limited API `. .. c:function:: void Py_LeaveRecursiveCall(void) @@ -814,7 +814,7 @@ because the :ref:`call protocol ` takes care of recursion handling. *successful* invocation of :c:func:`Py_EnterRecursiveCall`. .. versionchanged:: 3.9 - This function is now also available in the limited API. + This function is now also available in the :ref:`limited API `. Properly implementing :c:member:`~PyTypeObject.tp_repr` for container types requires special recursion handling. In addition to protecting the stack, diff --git a/Doc/c-api/stable.rst b/Doc/c-api/stable.rst index a697f6cd3edd90..4817da41e3e217 100644 --- a/Doc/c-api/stable.rst +++ b/Doc/c-api/stable.rst @@ -25,19 +25,19 @@ are private API that can change without notice even in patch releases. Stable Application Binary Interface =================================== +For simplicity, this document talks about *extensions*, but the Limited API +and Stable ABI work the same way for all uses of the API – for example, +embedding Python. + +.. _limited-c-api: + +Limited C API +------------- + Python 3.2 introduced the *Limited API*, a subset of Python's C API. Extensions that only use the Limited API can be compiled once and work with multiple versions of Python. -Contents of the Limited API are :ref:`listed below `. - -To enable this, Python provides a *Stable ABI*: a set of symbols that will -remain compatible across Python 3.x versions. The Stable ABI contains symbols -exposed in the Limited API, but also other ones – for example, functions -necessary to support older versions of the Limited API. - -(For simplicity, this document talks about *extensions*, but the Limited API -and Stable ABI work the same way for all uses of the API – for example, -embedding Python.) +Contents of the Limited API are :ref:`listed below `. .. c:macro:: Py_LIMITED_API @@ -57,6 +57,19 @@ embedding Python.) You can also define ``Py_LIMITED_API`` to ``3``. This works the same as ``0x03020000`` (Python 3.2, the version that introduced Limited API). + +.. _stable-abi: + +Stable ABI +---------- + +To enable this, Python provides a *Stable ABI*: a set of symbols that will +remain compatible across Python 3.x versions. + +The Stable ABI contains symbols exposed in the :ref:`Limited API +`, but also other ones – for example, functions necessary to +support older versions of the Limited API. + On Windows, extensions that use the Stable ABI should be linked against ``python3.dll`` rather than a version-specific library such as ``python39.dll``. @@ -101,9 +114,9 @@ Limited API Caveats ------------------- Note that compiling with ``Py_LIMITED_API`` is *not* a complete guarantee that -code conforms to the Limited API or the Stable ABI. ``Py_LIMITED_API`` only -covers definitions, but an API also includes other issues, such as expected -semantics. +code conforms to the :ref:`Limited API ` or the :ref:`Stable ABI +`. ``Py_LIMITED_API`` only covers definitions, but an API also +includes other issues, such as expected semantics. One issue that ``Py_LIMITED_API`` does not guard against is calling a function with arguments that are invalid in a lower Python version. @@ -136,9 +149,9 @@ Platform Considerations ======================= ABI stability depends not only on Python, but also on the compiler used, -lower-level libraries and compiler options. For the purposes of the Stable ABI, -these details define a “platform”. They usually depend on the OS -type and processor architecture +lower-level libraries and compiler options. For the purposes of +the :ref:`Stable ABI `, these details define a “platform”. They +usually depend on the OS type and processor architecture It is the responsibility of each particular distributor of Python to ensure that all Python versions on a particular platform are built @@ -147,12 +160,12 @@ This is the case with Windows and macOS releases from ``python.org`` and many third-party distributors. -.. _stable-abi-list: +.. _limited-api-list: Contents of Limited API ======================= -Currently, the Limited API includes the following items: +Currently, the :ref:`Limited API ` includes the following items: .. limited-api-list:: diff --git a/Doc/c-api/structures.rst b/Doc/c-api/structures.rst index c2903b9ec3285b..304cc31575b1dc 100644 --- a/Doc/c-api/structures.rst +++ b/Doc/c-api/structures.rst @@ -318,7 +318,7 @@ There are these calling conventions: .. versionchanged:: 3.10 - ``METH_FASTCALL`` is now part of the stable ABI. + ``METH_FASTCALL`` is now part of the :ref:`stable ABI `. .. _METH_FASTCALL-METH_KEYWORDS: diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst index 01d7fc4dc1ecf1..a7ca19677134d0 100644 --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -42,7 +42,7 @@ Type Objects Return the :c:member:`~PyTypeObject.tp_flags` member of *type*. This function is primarily meant for use with ``Py_LIMITED_API``; the individual flag bits are guaranteed to be stable across Python releases, but access to - :c:member:`~PyTypeObject.tp_flags` itself is not part of the limited API. + :c:member:`~PyTypeObject.tp_flags` itself is not part of the :ref:`limited API `. .. versionadded:: 3.2 @@ -301,7 +301,7 @@ The following functions and structs are used to create .. versionchanged:: 3.11 :c:member:`~PyBufferProcs.bf_getbuffer` and :c:member:`~PyBufferProcs.bf_releasebuffer` are now available - under the limited API. + under the :ref:`limited API `. .. c:member:: void *PyType_Slot.pfunc diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 62711802a783ae..8b662ceac84e7b 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -2082,7 +2082,7 @@ This results in types that are limited relative to types defined in Python: include any subinterpreter-specific state. Also, since :c:type:`PyTypeObject` is only part of the :ref:`Limited API -` as an opaque struct, any extension modules using static types must be +` as an opaque struct, any extension modules using static types must be compiled for a specific Python minor version. diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst index 146511b9e3658a..686252d808653d 100644 --- a/Doc/c-api/unicode.rst +++ b/Doc/c-api/unicode.rst @@ -1084,7 +1084,7 @@ These are the UTF-8 codec APIs: The return type is now ``const char *`` rather of ``char *``. .. versionchanged:: 3.10 - This function is a part of the :ref:`limited API `. + This function is a part of the :ref:`limited API `. .. c:function:: const char* PyUnicode_AsUTF8(PyObject *unicode) diff --git a/Doc/howto/isolating-extensions.rst b/Doc/howto/isolating-extensions.rst index 0362fa6c685443..732fd6f993b722 100644 --- a/Doc/howto/isolating-extensions.rst +++ b/Doc/howto/isolating-extensions.rst @@ -461,7 +461,7 @@ Module State Access from Slot Methods, Getters and Setters .. After adding to limited API: - If you use the :ref:`limited API , + If you use the :ref:`limited API `, you must update ``Py_LIMITED_API`` to ``0x030b0000``, losing ABI compatibility with earlier versions. diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index c93541d18582de..fda4fbcb122dcf 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -2654,7 +2654,7 @@ Removed * :c:func:`PyMarshal_WriteObjectToString` * the ``Py_MARSHAL_VERSION`` macro - These are not part of the :ref:`limited API `. + These are not part of the :ref:`limited API `. (Contributed by Victor Stinner in :issue:`45474`.) diff --git a/Misc/NEWS.d/3.11.0a1.rst b/Misc/NEWS.d/3.11.0a1.rst index fe21d9b5e66093..46c65300abf2c2 100644 --- a/Misc/NEWS.d/3.11.0a1.rst +++ b/Misc/NEWS.d/3.11.0a1.rst @@ -4980,7 +4980,7 @@ Removed documentation for the removed ``PyParser_*`` C API. .. nonce: fy0AXK .. section: C API -The list in :ref:`stable-abi-list` now shows the public name +The list in :ref:`limited-api-list` now shows the public name :c:struct:`PyFrameObject` rather than ``_frame``. The non-existing entry ``_node`` no longer appears in the list. diff --git a/Misc/NEWS.d/3.11.0a2.rst b/Misc/NEWS.d/3.11.0a2.rst index d4ae7dc30b04b1..3f9a66298faa1b 100644 --- a/Misc/NEWS.d/3.11.0a2.rst +++ b/Misc/NEWS.d/3.11.0a2.rst @@ -1234,7 +1234,7 @@ defined: * :c:func:`PyMarshal_WriteObjectToString` * the ``Py_MARSHAL_VERSION`` macro -These are not part of the :ref:`limited API `. +These are not part of the :ref:`limited API `. Patch by Victor Stinner. From 184ce1414baa7ceb6450d54783ff8e63c907a6b0 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 29 Sep 2023 14:16:15 +0200 Subject: [PATCH 382/632] [3.11] gh-110088, gh-109878: Fix test_asyncio timeouts (#110092) (#110099) gh-110088, gh-109878: Fix test_asyncio timeouts (#110092) Fix test_asyncio timeouts: don't measure the maximum duration, a test should not measure a CI performance. Only measure the minimum duration when a task has a timeout or delay. Add CLOCK_RES to test_asyncio.utils. (cherry picked from commit db0a258e796703e12befea9d6dec04e349ca2f5b) --- Lib/test/test_asyncio/test_base_events.py | 7 ++----- Lib/test/test_asyncio/test_events.py | 8 ++++---- Lib/test/test_asyncio/test_timeouts.py | 20 ------------------- Lib/test/test_asyncio/test_waitfor.py | 15 -------------- Lib/test/test_asyncio/test_windows_events.py | 13 +++--------- Lib/test/test_asyncio/utils.py | 6 ++++++ ...-09-29-12-48-42.gh-issue-110088.qUhRga.rst | 4 ++++ 7 files changed, 19 insertions(+), 54 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2023-09-29-12-48-42.gh-issue-110088.qUhRga.rst diff --git a/Lib/test/test_asyncio/test_base_events.py b/Lib/test/test_asyncio/test_base_events.py index 6ba602dd619fba..f3f83ad318c954 100644 --- a/Lib/test/test_asyncio/test_base_events.py +++ b/Lib/test/test_asyncio/test_base_events.py @@ -273,7 +273,7 @@ def cb(): self.loop.stop() self.loop._process_events = mock.Mock() - delay = 0.1 + delay = 0.100 when = self.loop.time() + delay self.loop.call_at(when, cb) @@ -282,10 +282,7 @@ def cb(): dt = self.loop.time() - t0 # 50 ms: maximum granularity of the event loop - self.assertGreaterEqual(dt, delay - 0.050, dt) - # tolerate a difference of +800 ms because some Python buildbots - # are really slow - self.assertLessEqual(dt, 0.9, dt) + self.assertGreaterEqual(dt, delay - test_utils.CLOCK_RES) with self.assertRaises(TypeError, msg="when cannot be None"): self.loop.call_at(None, cb) diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index 4e0321a87daeca..daf2fb87994a84 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -294,10 +294,11 @@ async def coro2(): # 15.6 msec, we use fairly long sleep times here (~100 msec). def test_run_until_complete(self): + delay = 0.100 t0 = self.loop.time() - self.loop.run_until_complete(asyncio.sleep(0.1)) - t1 = self.loop.time() - self.assertTrue(0.08 <= t1-t0 <= 0.8, t1-t0) + self.loop.run_until_complete(asyncio.sleep(delay)) + dt = self.loop.time() - t0 + self.assertGreaterEqual(dt, delay - test_utils.CLOCK_RES) def test_run_until_complete_stopped(self): @@ -1695,7 +1696,6 @@ def _run_once(): self.loop._run_once = _run_once async def wait(): - loop = self.loop await asyncio.sleep(1e-2) await asyncio.sleep(1e-4) await asyncio.sleep(1e-6) diff --git a/Lib/test/test_asyncio/test_timeouts.py b/Lib/test/test_asyncio/test_timeouts.py index cc8feb8d0c1afb..5a4093e94707bc 100644 --- a/Lib/test/test_asyncio/test_timeouts.py +++ b/Lib/test/test_asyncio/test_timeouts.py @@ -47,7 +47,6 @@ async def test_nested_timeouts(self): self.assertTrue(cm2.expired()) async def test_waiter_cancelled(self): - loop = asyncio.get_running_loop() cancelled = False with self.assertRaises(TimeoutError): async with asyncio.timeout(0.01): @@ -60,39 +59,26 @@ async def test_waiter_cancelled(self): async def test_timeout_not_called(self): loop = asyncio.get_running_loop() - t0 = loop.time() async with asyncio.timeout(10) as cm: await asyncio.sleep(0.01) t1 = loop.time() self.assertFalse(cm.expired()) - # 2 sec for slow CI boxes - self.assertLess(t1-t0, 2) self.assertGreater(cm.when(), t1) async def test_timeout_disabled(self): - loop = asyncio.get_running_loop() - t0 = loop.time() async with asyncio.timeout(None) as cm: await asyncio.sleep(0.01) - t1 = loop.time() self.assertFalse(cm.expired()) self.assertIsNone(cm.when()) - # 2 sec for slow CI boxes - self.assertLess(t1-t0, 2) async def test_timeout_at_disabled(self): - loop = asyncio.get_running_loop() - t0 = loop.time() async with asyncio.timeout_at(None) as cm: await asyncio.sleep(0.01) - t1 = loop.time() self.assertFalse(cm.expired()) self.assertIsNone(cm.when()) - # 2 sec for slow CI boxes - self.assertLess(t1-t0, 2) async def test_timeout_zero(self): loop = asyncio.get_running_loop() @@ -102,8 +88,6 @@ async def test_timeout_zero(self): await asyncio.sleep(10) t1 = loop.time() self.assertTrue(cm.expired()) - # 2 sec for slow CI boxes - self.assertLess(t1-t0, 2) self.assertTrue(t0 <= cm.when() <= t1) async def test_timeout_zero_sleep_zero(self): @@ -114,8 +98,6 @@ async def test_timeout_zero_sleep_zero(self): await asyncio.sleep(0) t1 = loop.time() self.assertTrue(cm.expired()) - # 2 sec for slow CI boxes - self.assertLess(t1-t0, 2) self.assertTrue(t0 <= cm.when() <= t1) async def test_timeout_in_the_past_sleep_zero(self): @@ -126,8 +108,6 @@ async def test_timeout_in_the_past_sleep_zero(self): await asyncio.sleep(0) t1 = loop.time() self.assertTrue(cm.expired()) - # 2 sec for slow CI boxes - self.assertLess(t1-t0, 2) self.assertTrue(t0 >= cm.when() <= t1) async def test_foreign_exception_passed(self): diff --git a/Lib/test/test_asyncio/test_waitfor.py b/Lib/test/test_asyncio/test_waitfor.py index 07dbab3b39ca2b..c0cc606825b5e1 100644 --- a/Lib/test/test_asyncio/test_waitfor.py +++ b/Lib/test/test_asyncio/test_waitfor.py @@ -66,17 +66,12 @@ async def test_wait_for_timeout_less_then_0_or_0_future_done(self): fut = loop.create_future() fut.set_result('done') - t0 = loop.time() ret = await asyncio.wait_for(fut, 0) - t1 = loop.time() self.assertEqual(ret, 'done') self.assertTrue(fut.done()) - self.assertLess(t1 - t0, 0.1) async def test_wait_for_timeout_less_then_0_or_0_coroutine_do_not_started(self): - loop = asyncio.get_running_loop() - foo_started = False async def foo(): @@ -84,12 +79,9 @@ async def foo(): foo_started = True with self.assertRaises(asyncio.TimeoutError): - t0 = loop.time() await asyncio.wait_for(foo(), 0) - t1 = loop.time() self.assertEqual(foo_started, False) - self.assertLess(t1 - t0, 0.1) async def test_wait_for_timeout_less_then_0_or_0(self): loop = asyncio.get_running_loop() @@ -113,18 +105,14 @@ async def foo(): await started with self.assertRaises(asyncio.TimeoutError): - t0 = loop.time() await asyncio.wait_for(fut, timeout) - t1 = loop.time() self.assertTrue(fut.done()) # it should have been cancelled due to the timeout self.assertTrue(fut.cancelled()) self.assertEqual(foo_running, False) - self.assertLess(t1 - t0, 0.1) async def test_wait_for(self): - loop = asyncio.get_running_loop() foo_running = None async def foo(): @@ -139,13 +127,10 @@ async def foo(): fut = asyncio.create_task(foo()) with self.assertRaises(asyncio.TimeoutError): - t0 = loop.time() await asyncio.wait_for(fut, 0.1) - t1 = loop.time() self.assertTrue(fut.done()) # it should have been cancelled due to the timeout self.assertTrue(fut.cancelled()) - self.assertLess(t1 - t0, support.SHORT_TIMEOUT) self.assertEqual(foo_running, False) async def test_wait_for_blocking(self): diff --git a/Lib/test/test_asyncio/test_windows_events.py b/Lib/test/test_asyncio/test_windows_events.py index a36119a8004f9d..6e6c90a247b291 100644 --- a/Lib/test/test_asyncio/test_windows_events.py +++ b/Lib/test/test_asyncio/test_windows_events.py @@ -163,29 +163,25 @@ def test_wait_for_handle(self): # Wait for unset event with 0.5s timeout; # result should be False at timeout - fut = self.loop._proactor.wait_for_handle(event, 0.5) + timeout = 0.5 + fut = self.loop._proactor.wait_for_handle(event, timeout) start = self.loop.time() done = self.loop.run_until_complete(fut) elapsed = self.loop.time() - start self.assertEqual(done, False) self.assertFalse(fut.result()) - # bpo-31008: Tolerate only 450 ms (at least 500 ms expected), - # because of bad clock resolution on Windows - self.assertTrue(0.45 <= elapsed <= 0.9, elapsed) + self.assertGreaterEqual(elapsed, timeout - test_utils.CLOCK_RES) _overlapped.SetEvent(event) # Wait for set event; # result should be True immediately fut = self.loop._proactor.wait_for_handle(event, 10) - start = self.loop.time() done = self.loop.run_until_complete(fut) - elapsed = self.loop.time() - start self.assertEqual(done, True) self.assertTrue(fut.result()) - self.assertTrue(0 <= elapsed < 0.3, elapsed) # asyncio issue #195: cancelling a done _WaitHandleFuture # must not crash @@ -199,11 +195,8 @@ def test_wait_for_handle_cancel(self): # CancelledError should be raised immediately fut = self.loop._proactor.wait_for_handle(event, 10) fut.cancel() - start = self.loop.time() with self.assertRaises(asyncio.CancelledError): self.loop.run_until_complete(fut) - elapsed = self.loop.time() - start - self.assertTrue(0 <= elapsed < 0.1, elapsed) # asyncio issue #195: cancelling a _WaitHandleFuture twice # must not crash diff --git a/Lib/test/test_asyncio/utils.py b/Lib/test/test_asyncio/utils.py index c32494d40ccea8..19de8540fdb546 100644 --- a/Lib/test/test_asyncio/utils.py +++ b/Lib/test/test_asyncio/utils.py @@ -37,6 +37,12 @@ from test.support import threading_helper +# Use the maximum known clock resolution (gh-75191, gh-110088): Windows +# GetTickCount64() has a resolution of 15.6 ms. Use 20 ms to tolerate rounding +# issues. +CLOCK_RES = 0.020 + + def data_file(filename): if hasattr(support, 'TEST_HOME_DIR'): fullname = os.path.join(support.TEST_HOME_DIR, filename) diff --git a/Misc/NEWS.d/next/Tests/2023-09-29-12-48-42.gh-issue-110088.qUhRga.rst b/Misc/NEWS.d/next/Tests/2023-09-29-12-48-42.gh-issue-110088.qUhRga.rst new file mode 100644 index 00000000000000..cf44a123c2c925 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-09-29-12-48-42.gh-issue-110088.qUhRga.rst @@ -0,0 +1,4 @@ +Fix test_asyncio timeouts: don't measure the maximum duration, a test should +not measure a CI performance. Only measure the minimum duration when a task has +a timeout or delay. Add ``CLOCK_RES`` to ``test_asyncio.utils``. Patch by +Victor Stinner. From 8ac6890d5e7b852c5f0055d83ee62a60bb48e75a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 29 Sep 2023 15:19:37 +0200 Subject: [PATCH 383/632] [3.11] gh-110031: Skip test_threading fork tests if ASAN (#110100) (#110104) gh-110031: Skip test_threading fork tests if ASAN (#110100) Skip test_threading tests using thread+fork if Python is built with Address Sanitizer (ASAN). (cherry picked from commit 86e76ab8af9a5018acbcdcbb6285678175b1bd8a) --- Lib/test/test_threading.py | 43 +++++++++++-------- ...-09-29-14-11-30.gh-issue-110031.fQnFnc.rst | 2 + 2 files changed, 28 insertions(+), 17 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2023-09-29-14-11-30.gh-issue-110031.fQnFnc.rst diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index b352c44a4da4db..a4192acd6dbc5c 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -37,6 +37,23 @@ Py_DEBUG = hasattr(sys, 'gettotalrefcount') +# gh-89363: Skip fork() test if Python is built with Address Sanitizer (ASAN) +# to work around a libasan race condition, dead lock in pthread_create(). +skip_if_asan_fork = support.skip_if_sanitizer( + "libasan has a pthread_create() dead lock", + address=True) + + +def skip_unless_reliable_fork(test): + if not support.has_fork_support: + return unittest.skip("requires working os.fork()")(test) + if sys.platform in platforms_to_skip: + return unittest.skip("due to known OS bug related to thread+fork")(test) + if support.check_sanitizer(address=True): + return unittest.skip("libasan has a pthread_create() dead lock related to thread+fork")(test) + return test + + def restore_default_excepthook(testcase): testcase.addCleanup(setattr, threading, 'excepthook', threading.excepthook) threading.excepthook = threading.__excepthook__ @@ -533,7 +550,7 @@ def test_daemon_param(self): t = threading.Thread(daemon=True) self.assertTrue(t.daemon) - @support.requires_fork() + @skip_unless_reliable_fork def test_fork_at_exit(self): # bpo-42350: Calling os.fork() after threading._shutdown() must # not log an error. @@ -561,7 +578,7 @@ def exit_handler(): self.assertEqual(out, b'') self.assertEqual(err.rstrip(), b'child process ok') - @support.requires_fork() + @skip_unless_reliable_fork def test_dummy_thread_after_fork(self): # Issue #14308: a dummy thread in the active list doesn't mess up # the after-fork mechanism. @@ -588,11 +605,7 @@ def background_thread(evt): self.assertEqual(out, b'') self.assertEqual(err, b'') - @support.requires_fork() - # gh-89363: Skip multiprocessing tests if Python is built with ASAN to - # work around a libasan race condition: dead lock in pthread_create(). - @support.skip_if_sanitizer("libasan has a pthread_create() dead lock", - address=True) + @skip_unless_reliable_fork def test_is_alive_after_fork(self): # Try hard to trigger #18418: is_alive() could sometimes be True on # threads that vanished after a fork. @@ -626,7 +639,7 @@ def f(): th.start() th.join() - @support.requires_fork() + @skip_unless_reliable_fork @unittest.skipUnless(hasattr(os, 'waitpid'), "test needs os.waitpid()") def test_main_thread_after_fork(self): code = """if 1: @@ -647,8 +660,7 @@ def test_main_thread_after_fork(self): self.assertEqual(err, b"") self.assertEqual(data, "MainThread\nTrue\nTrue\n") - @unittest.skipIf(sys.platform in platforms_to_skip, "due to known OS bug") - @support.requires_fork() + @skip_unless_reliable_fork @unittest.skipUnless(hasattr(os, 'waitpid'), "test needs os.waitpid()") def test_main_thread_after_fork_from_nonmain_thread(self): code = """if 1: @@ -1025,8 +1037,7 @@ def test_1_join_on_shutdown(self): """ self._run_and_join(script) - @support.requires_fork() - @unittest.skipIf(sys.platform in platforms_to_skip, "due to known OS bug") + @skip_unless_reliable_fork def test_2_join_in_forked_process(self): # Like the test above, but from a forked interpreter script = """if 1: @@ -1046,8 +1057,7 @@ def test_2_join_in_forked_process(self): """ self._run_and_join(script) - @support.requires_fork() - @unittest.skipIf(sys.platform in platforms_to_skip, "due to known OS bug") + @skip_unless_reliable_fork def test_3_join_in_forked_from_thread(self): # Like the test above, but fork() was called from a worker thread # In the forked process, the main Thread object must be marked as stopped. @@ -1117,8 +1127,7 @@ def main(): rc, out, err = assert_python_ok('-c', script) self.assertFalse(err) - @support.requires_fork() - @unittest.skipIf(sys.platform in platforms_to_skip, "due to known OS bug") + @skip_unless_reliable_fork def test_reinit_tls_after_fork(self): # Issue #13817: fork() would deadlock in a multithreaded program with # the ad-hoc TLS implementation. @@ -1141,7 +1150,7 @@ def do_fork_and_wait(): for t in threads: t.join() - @support.requires_fork() + @skip_unless_reliable_fork def test_clear_threads_states_after_fork(self): # Issue #17094: check that threads states are cleared after fork() diff --git a/Misc/NEWS.d/next/Tests/2023-09-29-14-11-30.gh-issue-110031.fQnFnc.rst b/Misc/NEWS.d/next/Tests/2023-09-29-14-11-30.gh-issue-110031.fQnFnc.rst new file mode 100644 index 00000000000000..a8a163c567d2b3 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-09-29-14-11-30.gh-issue-110031.fQnFnc.rst @@ -0,0 +1,2 @@ +Skip test_threading tests using thread+fork if Python is built with Address +Sanitizer (ASAN). Patch by Victor Stinner. From 4b97c724e0715af049b2c0b226b4988e1f30df70 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 29 Sep 2023 06:25:34 -0700 Subject: [PATCH 384/632] [3.11] gh-109955 : Update state transition comments for asyncio.Task (GH-109910) (#109993) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gh-109955 : Update state transition comments for asyncio.Task (GH-109910) (cherry picked from commit 45cf5b0c69bb5c51f33fc681d90c45147e311ddf) Co-authored-by: Kristján Valur Jónsson Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Lib/asyncio/tasks.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py index 3e07ce5294c8dd..6ca545e30acd98 100644 --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -81,15 +81,25 @@ class Task(futures._PyFuture): # Inherit Python Task implementation """A coroutine wrapped in a Future.""" # An important invariant maintained while a Task not done: + # _fut_waiter is either None or a Future. The Future + # can be either done() or not done(). + # The task can be in any of 3 states: # - # - Either _fut_waiter is None, and _step() is scheduled; - # - or _fut_waiter is some Future, and _step() is *not* scheduled. + # - 1: _fut_waiter is not None and not _fut_waiter.done(): + # __step() is *not* scheduled and the Task is waiting for _fut_waiter. + # - 2: (_fut_waiter is None or _fut_waiter.done()) and __step() is scheduled: + # the Task is waiting for __step() to be executed. + # - 3: _fut_waiter is None and __step() is *not* scheduled: + # the Task is currently executing (in __step()). # - # The only transition from the latter to the former is through - # _wakeup(). When _fut_waiter is not None, one of its callbacks - # must be _wakeup(). - - # If False, don't log a message if the task is destroyed whereas its + # * In state 1, one of the callbacks of __fut_waiter must be __wakeup(). + # * The transition from 1 to 2 happens when _fut_waiter becomes done(), + # as it schedules __wakeup() to be called (which calls __step() so + # we way that __step() is scheduled). + # * It transitions from 2 to 3 when __step() is executed, and it clears + # _fut_waiter to None. + + # If False, don't log a message if the task is destroyed while its # status is still pending _log_destroy_pending = True From b07661d88984c9d5f682de351b58f9e4a4fc0a8e Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 29 Sep 2023 06:47:24 -0700 Subject: [PATCH 385/632] [3.11] gh-109592: test_eintr tolerates 20 ms when comparing timings (GH-110102) (#110107) gh-109592: test_eintr tolerates 20 ms when comparing timings (GH-110102) (cherry picked from commit 9c73a9acec095c05a178e7dff638f7d9769318f3) Co-authored-by: Victor Stinner --- Lib/test/_test_eintr.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/Lib/test/_test_eintr.py b/Lib/test/_test_eintr.py index e43b59d064f55a..54a5412f8286cd 100644 --- a/Lib/test/_test_eintr.py +++ b/Lib/test/_test_eintr.py @@ -25,6 +25,12 @@ from test.support import os_helper from test.support import socket_helper + +# gh-109592: Tolerate a difference of 20 ms when comparing timings +# (clock resolution) +CLOCK_RES = 0.020 + + @contextlib.contextmanager def kill_on_error(proc): """Context manager killing the subprocess if a Python exception is raised.""" @@ -75,6 +81,9 @@ def subprocess(self, *args, **kw): cmd_args = (sys.executable, '-c') + args return subprocess.Popen(cmd_args, **kw) + def check_elapsed_time(self, elapsed): + self.assertGreaterEqual(elapsed, self.sleep_time - CLOCK_RES) + @unittest.skipUnless(hasattr(signal, "setitimer"), "requires setitimer()") class OSEINTRTest(EINTRBaseTest): @@ -373,7 +382,7 @@ def test_sleep(self): time.sleep(self.sleep_time) self.stop_alarm() dt = time.monotonic() - t0 - self.assertGreaterEqual(dt, self.sleep_time) + self.check_elapsed_time(dt) @unittest.skipUnless(hasattr(signal, "setitimer"), "requires setitimer()") @@ -437,7 +446,7 @@ def test_select(self): select.select([], [], [], self.sleep_time) dt = time.monotonic() - t0 self.stop_alarm() - self.assertGreaterEqual(dt, self.sleep_time) + self.check_elapsed_time(dt) @unittest.skipIf(sys.platform == "darwin", "poll may fail on macOS; see issue #28087") @@ -449,7 +458,7 @@ def test_poll(self): poller.poll(self.sleep_time * 1e3) dt = time.monotonic() - t0 self.stop_alarm() - self.assertGreaterEqual(dt, self.sleep_time) + self.check_elapsed_time(dt) @unittest.skipUnless(hasattr(select, 'epoll'), 'need select.epoll') def test_epoll(self): @@ -460,7 +469,7 @@ def test_epoll(self): poller.poll(self.sleep_time) dt = time.monotonic() - t0 self.stop_alarm() - self.assertGreaterEqual(dt, self.sleep_time) + self.check_elapsed_time(dt) @unittest.skipUnless(hasattr(select, 'kqueue'), 'need select.kqueue') def test_kqueue(self): @@ -471,7 +480,7 @@ def test_kqueue(self): kqueue.control(None, 1, self.sleep_time) dt = time.monotonic() - t0 self.stop_alarm() - self.assertGreaterEqual(dt, self.sleep_time) + self.check_elapsed_time(dt) @unittest.skipUnless(hasattr(select, 'devpoll'), 'need select.devpoll') def test_devpoll(self): @@ -482,7 +491,7 @@ def test_devpoll(self): poller.poll(self.sleep_time * 1e3) dt = time.monotonic() - t0 self.stop_alarm() - self.assertGreaterEqual(dt, self.sleep_time) + self.check_elapsed_time(dt) class FNTLEINTRTest(EINTRBaseTest): @@ -512,8 +521,8 @@ def _lock(self, lock_func, lock_name): # potential context switch delay lock_func(f, fcntl.LOCK_EX) dt = time.monotonic() - start_time - self.assertGreaterEqual(dt, self.sleep_time) self.stop_alarm() + self.check_elapsed_time(dt) proc.wait() # Issue 35633: See https://bugs.python.org/issue35633#msg333662 From d87217ff4481140b4a0db0d3286f9e7c68ce7d38 Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Fri, 29 Sep 2023 10:03:48 -0500 Subject: [PATCH 386/632] [3.11] gh-109991: Update Windows build to use OpenSSL 3.0.11 (GH-110059) (cherry picked from commit cf4c29725636e1a0dd2ebab443613b56ca6c9486) --- .../Windows/2023-09-28-17-09-23.gh-issue-109991.CIMftz.rst | 1 + PCbuild/get_externals.bat | 4 ++-- PCbuild/python.props | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2023-09-28-17-09-23.gh-issue-109991.CIMftz.rst diff --git a/Misc/NEWS.d/next/Windows/2023-09-28-17-09-23.gh-issue-109991.CIMftz.rst b/Misc/NEWS.d/next/Windows/2023-09-28-17-09-23.gh-issue-109991.CIMftz.rst new file mode 100644 index 00000000000000..ee988f90863426 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-09-28-17-09-23.gh-issue-109991.CIMftz.rst @@ -0,0 +1 @@ +Update Windows build to use OpenSSL 3.0.11. diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index 83afe7851ec292..60dc725bb6a2fb 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -53,7 +53,7 @@ echo.Fetching external libraries... set libraries= set libraries=%libraries% bzip2-1.0.8 if NOT "%IncludeLibffiSrc%"=="false" set libraries=%libraries% libffi-3.4.4 -if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-3.0.10 +if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-3.0.11 set libraries=%libraries% sqlite-3.42.0.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.12.1 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.12.1 @@ -77,7 +77,7 @@ echo.Fetching external binaries... set binaries= if NOT "%IncludeLibffi%"=="false" set binaries=%binaries% libffi-3.4.4 -if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-3.0.10 +if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-3.0.11 if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.12.1 if NOT "%IncludeSSLSrc%"=="false" set binaries=%binaries% nasm-2.11.06 diff --git a/PCbuild/python.props b/PCbuild/python.props index 94faa8221eac5a..35c6e92be45dc9 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -74,8 +74,8 @@ $(ExternalsDir)libffi-3.4.4\ $(libffiDir)$(ArchName)\ $(libffiOutDir)include - $(ExternalsDir)openssl-3.0.10\ - $(ExternalsDir)openssl-bin-3.0.10\$(ArchName)\ + $(ExternalsDir)openssl-3.0.11\ + $(ExternalsDir)openssl-bin-3.0.11\$(ArchName)\ $(opensslOutDir)include $(ExternalsDir)\nasm-2.11.06\ $(ExternalsDir)\zlib-1.2.13\ From a95e8cf9081ef7ff7450d6060bd968ec79a275f9 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 29 Sep 2023 08:48:59 -0700 Subject: [PATCH 387/632] gh-109615: Look for 'Modules' as landmark for test_copy_python_src_ignore (GH-110108) (cherry picked from commit 20bc5f7c28a6f8a2e156c4a748ffabb5efc7c761) Co-authored-by: Steve Dower --- Lib/test/test_support.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index 2f70f919d4043f..9894c2647d7c93 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -789,7 +789,9 @@ def test_copy_python_src_ignore(self): if not os.path.exists(src_dir): self.skipTest(f"cannot access Python source code directory:" f" {src_dir!r}") - landmark = os.path.join(src_dir, 'Lib', 'os.py') + # Check that the landmark copy_python_src_ignore() expects is available + # (Previously we looked for 'Lib\os.py', which is always present on Windows.) + landmark = os.path.join(src_dir, 'Modules') if not os.path.exists(landmark): self.skipTest(f"cannot access Python source code directory:" f" {landmark!r} landmark is missing") From 190e8fbfb7284e9c253388f0c2363cd8387e6e7f Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 29 Sep 2023 23:54:46 +0200 Subject: [PATCH 388/632] [3.11] gh-108851: Fix support.get_recursion_available() for USE_STACKCHECK (#110127) Add _testcapi.USE_STACKCHECK. USE_STACKCHECK on using on Windows 32-bit. --- Lib/test/support/__init__.py | 11 ++++++++++- Lib/test/test_support.py | 17 +++++++++++++---- Modules/_testcapimodule.c | 10 ++++++++-- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 2c6b22fdee5a21..2e6518cf241f14 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -2243,7 +2243,16 @@ def get_recursion_available(): """ limit = sys.getrecursionlimit() depth = get_recursion_depth() - return limit - depth + + try: + from _testcapi import USE_STACKCHECK + except ImportError: + USE_STACKCHECK = False + + if USE_STACKCHECK: + return max(limit - depth - 1, 0) + else: + return limit - depth @contextlib.contextmanager def set_recursion_limit(limit): diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index 9894c2647d7c93..2efdbd22d90e2e 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -704,6 +704,10 @@ def test_get_recursion_depth(self): code = textwrap.dedent(""" from test import support import sys + try: + from _testcapi import USE_STACKCHECK + except ImportError: + USE_STACKCHECK = False def check(cond): if not cond: @@ -728,19 +732,24 @@ def test_recursive(depth, limit): check(get_depth == depth) test_recursive(depth + 1, limit) + if USE_STACKCHECK: + # f-string consumes 2 frames and -1 for USE_STACKCHECK + IGNORE = 3 + else: + # f-string consumes 2 frames + IGNORE = 2 + # depth up to 25 with support.infinite_recursion(max_depth=25): limit = sys.getrecursionlimit() print(f"test with sys.getrecursionlimit()={limit}") - # Use limit-2 since f-string seems to consume 2 frames. - test_recursive(2, limit - 2) + test_recursive(2, limit - IGNORE) # depth up to 500 with support.infinite_recursion(max_depth=500): limit = sys.getrecursionlimit() print(f"test with sys.getrecursionlimit()={limit}") - # limit-2 since f-string seems to consume 2 frames - test_recursive(2, limit - 2) + test_recursive(2, limit - IGNORE) """) script_helper.assert_python_ok("-c", code) diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 5c00b48001a919..2f1801f781017d 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -8203,8 +8203,14 @@ PyInit__testcapi(void) #else v = Py_False; #endif - Py_INCREF(v); - PyModule_AddObject(m, "WITH_PYMALLOC", v); + PyModule_AddObject(m, "WITH_PYMALLOC", Py_NewRef(v)); + +#ifdef USE_STACKCHECK + v = Py_True; +#else + v = Py_False; +#endif + PyModule_AddObject(m, "USE_STACKCHECK", Py_NewRef(v)); TestError = PyErr_NewException("_testcapi.error", NULL, NULL); Py_INCREF(TestError); From 42b6883d5f648a95e33f815364c8a529e4574caa Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 29 Sep 2023 15:19:33 -0700 Subject: [PATCH 389/632] [3.11] gh-107888: Fix test_mmap PROT_EXEC comment (GH-110125) (#110130) gh-107888: Fix test_mmap PROT_EXEC comment (GH-110125) (cherry picked from commit 14098b78f7453adbd40c53e32c29588611b7c87b) Co-authored-by: Victor Stinner --- Lib/test/test_mmap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_mmap.py b/Lib/test/test_mmap.py index 92c99d645b25cc..dfcf3039422af5 100644 --- a/Lib/test/test_mmap.py +++ b/Lib/test/test_mmap.py @@ -258,7 +258,7 @@ def test_access_parameter(self): try: m = mmap.mmap(f.fileno(), mapsize, prot=prot) except PermissionError: - # on macOS 14, PROT_READ | PROT_WRITE is not allowed + # on macOS 14, PROT_READ | PROT_EXEC is not allowed pass else: self.assertRaises(TypeError, m.write, b"abcdef") From a673248d6cb1ae5d5b7fbf0ab6c8db8c32df0eee Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 29 Sep 2023 23:42:37 -0700 Subject: [PATCH 390/632] [3.11] GH-101100: Fix reference warnings for ``namedtuple`` (GH-110113) (#110136) GH-101100: Fix reference warnings for ``namedtuple`` (GH-110113) (cherry picked from commit cbdacc738a52a876aae5b74b4665d30a5f204766) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/whatsnew/2.6.rst | 6 +++--- Misc/NEWS.d/3.8.0a1.rst | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/whatsnew/2.6.rst b/Doc/whatsnew/2.6.rst index 128407e3fba132..96d9b792b3723a 100644 --- a/Doc/whatsnew/2.6.rst +++ b/Doc/whatsnew/2.6.rst @@ -1850,8 +1850,8 @@ changes, or look through the Subversion logs for all the details. special values and floating-point exceptions in a manner consistent with Annex 'G' of the C99 standard. -* A new data type in the :mod:`collections` module: :class:`namedtuple(typename, - fieldnames)` is a factory function that creates subclasses of the standard tuple +* A new data type in the :mod:`collections` module: ``namedtuple(typename, fieldnames)`` + is a factory function that creates subclasses of the standard tuple whose fields are accessible by name as well as index. For example:: >>> var_type = collections.namedtuple('variable', @@ -1873,7 +1873,7 @@ changes, or look through the Subversion logs for all the details. variable(id=1, name='amplitude', type='int', size=4) Several places in the standard library that returned tuples have - been modified to return :class:`namedtuple` instances. For example, + been modified to return :func:`namedtuple` instances. For example, the :meth:`Decimal.as_tuple` method now returns a named tuple with :attr:`sign`, :attr:`digits`, and :attr:`exponent` fields. diff --git a/Misc/NEWS.d/3.8.0a1.rst b/Misc/NEWS.d/3.8.0a1.rst index 530260aba873cc..4adacfd41809d5 100644 --- a/Misc/NEWS.d/3.8.0a1.rst +++ b/Misc/NEWS.d/3.8.0a1.rst @@ -380,7 +380,7 @@ Implement :pep:`572` (assignment expressions). Patch by Emily Morehouse. .. nonce: voIdcp .. section: Core and Builtins -Speed up :class:`namedtuple` attribute access by 1.6x using a C fast-path +Speed up :func:`namedtuple` attribute access by 1.6x using a C fast-path for the name descriptors. Patch by Pablo Galindo. .. From cb1f49991e86d773f6d36a49b81be12abf811057 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 30 Sep 2023 04:21:27 -0700 Subject: [PATCH 391/632] [3.11] GH-101100: Fix reference warnings for ``gettext`` (GH-110115) (#110141) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/library/gettext.rst | 55 +++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/Doc/library/gettext.rst b/Doc/library/gettext.rst index 88a65b980d310f..7ebe91b372d35a 100644 --- a/Doc/library/gettext.rst +++ b/Doc/library/gettext.rst @@ -58,7 +58,7 @@ class-based API instead. Return the localized translation of *message*, based on the current global domain, language, and locale directory. This function is usually aliased as - :func:`_` in the local namespace (see examples below). + :func:`!_` in the local namespace (see examples below). .. function:: dgettext(domain, message) @@ -98,7 +98,7 @@ class-based API instead. .. versionadded:: 3.8 -Note that GNU :program:`gettext` also defines a :func:`dcgettext` method, but +Note that GNU :program:`gettext` also defines a :func:`!dcgettext` method, but this was deemed not useful and so it is currently unimplemented. Here's an example of typical usage for this API:: @@ -119,7 +119,7 @@ greater convenience than the GNU :program:`gettext` API. It is the recommended way of localizing your Python applications and modules. :mod:`!gettext` defines a :class:`GNUTranslations` class which implements the parsing of GNU :file:`.mo` format files, and has methods for returning strings. Instances of this class can also -install themselves in the built-in namespace as the function :func:`_`. +install themselves in the built-in namespace as the function :func:`!_`. .. function:: find(domain, localedir=None, languages=None, all=False) @@ -150,15 +150,12 @@ install themselves in the built-in namespace as the function :func:`_`. .. function:: translation(domain, localedir=None, languages=None, class_=None, fallback=False) - Return a :class:`*Translations` instance based on the *domain*, *localedir*, + Return a ``*Translations`` instance based on the *domain*, *localedir*, and *languages*, which are first passed to :func:`find` to get a list of the associated :file:`.mo` file paths. Instances with identical :file:`.mo` file names are cached. The actual class instantiated is *class_* if provided, otherwise :class:`GNUTranslations`. The class's constructor must - take a single :term:`file object` argument. If provided, *codeset* will change - the charset used to encode translated strings in the - :meth:`~NullTranslations.lgettext` and :meth:`~NullTranslations.lngettext` - methods. + take a single :term:`file object` argument. If multiple files are found, later files are used as fallbacks for earlier ones. To allow setting the fallback, :func:`copy.copy` is used to clone each @@ -177,19 +174,19 @@ install themselves in the built-in namespace as the function :func:`_`. .. function:: install(domain, localedir=None, *, names=None) - This installs the function :func:`_` in Python's builtins namespace, based on + This installs the function :func:`!_` in Python's builtins namespace, based on *domain* and *localedir* which are passed to the function :func:`translation`. For the *names* parameter, please see the description of the translation object's :meth:`~NullTranslations.install` method. As seen below, you usually mark the strings in your application that are - candidates for translation, by wrapping them in a call to the :func:`_` + candidates for translation, by wrapping them in a call to the :func:`!_` function, like this:: print(_('This string will be translated.')) - For convenience, you want the :func:`_` function to be installed in Python's + For convenience, you want the :func:`!_` function to be installed in Python's builtins namespace, so it is easily accessible in all modules of your application. @@ -276,20 +273,20 @@ are the methods of :class:`!NullTranslations`: If the *names* parameter is given, it must be a sequence containing the names of functions you want to install in the builtins namespace in - addition to :func:`_`. Supported names are ``'gettext'``, ``'ngettext'``, - ``'pgettext'``, ``'npgettext'``, ``'lgettext'``, and ``'lngettext'``. + addition to :func:`!_`. Supported names are ``'gettext'``, ``'ngettext'``, + ``'pgettext'``, and ``'npgettext'``. Note that this is only one way, albeit the most convenient way, to make - the :func:`_` function available to your application. Because it affects + the :func:`!_` function available to your application. Because it affects the entire application globally, and specifically the built-in namespace, - localized modules should never install :func:`_`. Instead, they should use - this code to make :func:`_` available to their module:: + localized modules should never install :func:`!_`. Instead, they should use + this code to make :func:`!_` available to their module:: import gettext t = gettext.translation('mymodule', ...) _ = t.gettext - This puts :func:`_` only in the module's global namespace and so only + This puts :func:`!_` only in the module's global namespace and so only affects calls within this module. .. versionchanged:: 3.8 @@ -314,7 +311,7 @@ initialize the "protected" :attr:`_charset` instance variable, defaulting to ids and message strings read from the catalog are converted to Unicode using this encoding, else ASCII is assumed. -Since message ids are read as Unicode strings too, all :meth:`*gettext` methods +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 @@ -404,7 +401,7 @@ version has a slightly different API. Its documented usage was:: _ = cat.gettext print(_('hello world')) -For compatibility with this older module, the function :func:`Catalog` is an +For compatibility with this older module, the function :func:`!Catalog` is an alias for the :func:`translation` function described above. One difference between this module and Henstridge's: his catalog objects @@ -432,7 +429,7 @@ take the following steps: In order to prepare your code for I18N, you need to look at all the strings in your files. Any string that needs to be translated should be marked by wrapping -it in ``_('...')`` --- that is, a call to the function :func:`_`. For example:: +it in ``_('...')`` --- that is, a call to the function :func:`_ `. For example:: filename = 'mylog.txt' message = _('writing a log message') @@ -504,7 +501,7 @@ module:: Localizing your application ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -If you are localizing your application, you can install the :func:`_` function +If you are localizing your application, you can install the :func:`!_` function globally into the built-in namespace, usually in the main driver file of your application. This will let all your application-specific files just use ``_('...')`` without having to explicitly install it in each file. @@ -581,13 +578,13 @@ Here is one way you can handle this situation:: for a in animals: print(_(a)) -This works because the dummy definition of :func:`_` simply returns the string +This works because the dummy definition of :func:`!_` simply returns the string unchanged. And this dummy definition will temporarily override any definition -of :func:`_` in the built-in namespace (until the :keyword:`del` command). Take -care, though if you have a previous definition of :func:`_` in the local +of :func:`!_` in the built-in namespace (until the :keyword:`del` command). Take +care, though if you have a previous definition of :func:`!_` in the local namespace. -Note that the second use of :func:`_` will not identify "a" as being +Note that the second use of :func:`!_` will not identify "a" as being translatable to the :program:`gettext` program, because the parameter is not a string literal. @@ -606,13 +603,13 @@ Another way to handle this is with the following example:: print(_(a)) In this case, you are marking translatable strings with the function -:func:`N_`, which won't conflict with any definition of :func:`_`. +:func:`!N_`, which won't conflict with any definition of :func:`!_`. However, you will need to teach your message extraction program to -look for translatable strings marked with :func:`N_`. :program:`xgettext`, +look for translatable strings marked with :func:`!N_`. :program:`xgettext`, :program:`pygettext`, ``pybabel extract``, and :program:`xpot` all support this through the use of the :option:`!-k` command-line switch. -The choice of :func:`N_` here is totally arbitrary; it could have just -as easily been :func:`MarkThisStringForTranslation`. +The choice of :func:`!N_` here is totally arbitrary; it could have just +as easily been :func:`!MarkThisStringForTranslation`. Acknowledgements From 080f5b98d1eac67abc13fe9654ff8a3feeef55ea Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 30 Sep 2023 11:46:37 -0700 Subject: [PATCH 392/632] [3.11] gh-109748: Fix again venv test_zippath_from_non_installed_posix() (GH-110149) (#110153) gh-109748: Fix again venv test_zippath_from_non_installed_posix() (GH-110149) Call also copy_python_src_ignore() on listdir() names. shutil.copytree(): replace set() with an empty tuple. An empty tuple becomes a constant in the compiler and checking if an item is in an empty tuple is cheap. (cherry picked from commit 0def8c712bb6f66f1081cab71deb3681566b846d) Co-authored-by: Victor Stinner --- Lib/shutil.py | 2 +- Lib/test/test_support.py | 2 +- Lib/test/test_venv.py | 6 +++++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Lib/shutil.py b/Lib/shutil.py index 1c3a75da55ba5b..d108986d130267 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -454,7 +454,7 @@ def _copytree(entries, src, dst, symlinks, ignore, copy_function, if ignore is not None: ignored_names = ignore(os.fspath(src), [x.name for x in entries]) else: - ignored_names = set() + ignored_names = () os.makedirs(dst, exist_ok=dirs_exist_ok) errors = [] diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index 2efdbd22d90e2e..01ba88ce42c5f6 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -818,7 +818,7 @@ def test_copy_python_src_ignore(self): self.assertEqual(support.copy_python_src_ignore(path, os.listdir(path)), ignored | {'build', 'venv'}) - # An other directory + # Another directory path = os.path.join(src_dir, 'Objects') self.assertEqual(support.copy_python_src_ignore(path, os.listdir(path)), ignored) diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index 884ac3ac567c07..eb9227a3b70379 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -572,7 +572,11 @@ def test_zippath_from_non_installed_posix(self): eachpath, os.path.join(non_installed_dir, platlibdir)) elif os.path.isfile(os.path.join(eachpath, "os.py")): - for name in os.listdir(eachpath): + names = os.listdir(eachpath) + ignored_names = copy_python_src_ignore(eachpath, names) + for name in names: + if name in ignored_names: + continue if name == "site-packages": continue fn = os.path.join(eachpath, name) From 446c81c3c3a5e82c1df475b8a98009aeb5b5433e Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 30 Sep 2023 14:01:33 -0700 Subject: [PATCH 393/632] [3.11] gh-110088: Fix asyncio test_prompt_cancellation() (GH-110157) (#110159) gh-110088: Fix asyncio test_prompt_cancellation() (GH-110157) Don't measure the CI performance: don't test the maximum elapsed time. The check failed on a slow CI. (cherry picked from commit c62b49ecc8da13fa9522865ef6fe0aec194fd0d8) Co-authored-by: Victor Stinner --- Lib/test/test_asyncio/test_events.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index daf2fb87994a84..d7871d3e538cba 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -1671,12 +1671,9 @@ async def main(): self.loop.stop() return res - start = time.monotonic() t = self.loop.create_task(main()) self.loop.run_forever() - elapsed = time.monotonic() - start - self.assertLess(elapsed, 0.1) self.assertEqual(t.result(), 'cancelled') self.assertRaises(asyncio.CancelledError, f.result) if ov is not None: From 00da3ccd0a7b4f0cfb04e94bcd766326d12a5af1 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 1 Oct 2023 10:47:06 -0700 Subject: [PATCH 394/632] [3.11] gh-101100: Fix sphinx warnings in `library/site.rst` (GH-110144) (#110185) Co-authored-by: Nikita Sobolev Co-authored-by: Alex Waygood --- Doc/library/exceptions.rst | 14 ++++++++++---- Doc/library/site.rst | 22 +++++++++++++++------- Doc/tools/.nitignore | 2 -- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst index dcfbc486eeb358..d210f82f3926f7 100644 --- a/Doc/library/exceptions.rst +++ b/Doc/library/exceptions.rst @@ -220,10 +220,16 @@ The following exceptions are the exceptions that are usually raised. load a module. Also raised when the "from list" in ``from ... import`` has a name that cannot be found. - The :attr:`name` and :attr:`path` attributes can be set using keyword-only - arguments to the constructor. When set they represent the name of the module - that was attempted to be imported and the path to any file which triggered - the exception, respectively. + The optional *name* and *path* keyword-only arguments + set the corresponding attributes: + + .. attribute:: name + + The name of the module that was attempted to be imported. + + .. attribute:: path + + The path to any file which triggered the exception. .. versionchanged:: 3.3 Added the :attr:`name` and :attr:`path` attributes. diff --git a/Doc/library/site.rst b/Doc/library/site.rst index 5b70dbced13e01..c025af608e318b 100644 --- a/Doc/library/site.rst +++ b/Doc/library/site.rst @@ -19,7 +19,7 @@ Importing this module will append site-specific paths to the module search path and add a few builtins, unless :option:`-S` was used. In that case, this module can be safely imported with no automatic modifications to the module search path or additions to the builtins. To explicitly trigger the usual site-specific -additions, call the :func:`site.main` function. +additions, call the :func:`main` function. .. versionchanged:: 3.3 Importing the module used to trigger paths manipulation even when using @@ -109,32 +109,40 @@ directory precedes the :file:`foo` directory because :file:`bar.pth` comes alphabetically before :file:`foo.pth`; and :file:`spam` is omitted because it is not mentioned in either path configuration file. -.. index:: pair: module; sitecustomize +:mod:`sitecustomize` +-------------------- + +.. module:: sitecustomize After these path manipulations, an attempt is made to import a module named :mod:`sitecustomize`, which can perform arbitrary site-specific customizations. It is typically created by a system administrator in the site-packages directory. If this import fails with an :exc:`ImportError` or its subclass -exception, and the exception's :attr:`name` attribute equals to ``'sitecustomize'``, +exception, and the exception's :attr:`~ImportError.name` +attribute equals to ``'sitecustomize'``, it is silently ignored. If Python is started without output streams available, as with :file:`pythonw.exe` on Windows (which is used by default to start IDLE), attempted output from :mod:`sitecustomize` is ignored. Any other exception causes a silent and perhaps mysterious failure of the process. -.. index:: pair: module; usercustomize +:mod:`usercustomize` +-------------------- + +.. module:: usercustomize After this, an attempt is made to import a module named :mod:`usercustomize`, which can perform arbitrary user-specific customizations, if -:data:`ENABLE_USER_SITE` is true. This file is intended to be created in the +:data:`~site.ENABLE_USER_SITE` is true. This file is intended to be created in the user site-packages directory (see below), which is part of ``sys.path`` unless disabled by :option:`-s`. If this import fails with an :exc:`ImportError` or -its subclass exception, and the exception's :attr:`name` attribute equals to -``'usercustomize'``, it is silently ignored. +its subclass exception, and the exception's :attr:`~ImportError.name` +attribute equals to ``'usercustomize'``, it is silently ignored. Note that for some non-Unix systems, ``sys.prefix`` and ``sys.exec_prefix`` are empty, and the path manipulations are skipped; however the import of :mod:`sitecustomize` and :mod:`usercustomize` is still attempted. +.. currentmodule:: site .. _rlcompleter-config: diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index dace135cb560f1..8e1c28a4c7b424 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -110,7 +110,6 @@ Doc/library/select.rst Doc/library/selectors.rst Doc/library/shelve.rst Doc/library/signal.rst -Doc/library/site.rst Doc/library/smtplib.rst Doc/library/socket.rst Doc/library/socketserver.rst @@ -118,7 +117,6 @@ Doc/library/ssl.rst Doc/library/stdtypes.rst Doc/library/string.rst Doc/library/subprocess.rst -Doc/library/sys_path_init.rst Doc/library/syslog.rst Doc/library/tarfile.rst Doc/library/tempfile.rst From 0914b13ba5f216e79341430a4fb6d2f2e04bdce8 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 1 Oct 2023 12:32:44 -0700 Subject: [PATCH 395/632] [3.11] gh-110138: Improve grammar in idiomatic usage of ``__main__.py`` (GH-110142) (#110189) gh-110138: Improve grammar in idiomatic usage of ``__main__.py`` (GH-110142) (cherry picked from commit adf0f15a06c6e8ddd1a6d59b28efcbb26289f080) Co-authored-by: Quentin Agren --- Doc/library/__main__.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/library/__main__.rst b/Doc/library/__main__.rst index 90973d0058e883..c35f67245c97ec 100644 --- a/Doc/library/__main__.rst +++ b/Doc/library/__main__.rst @@ -238,9 +238,9 @@ package. For more details, see :ref:`intra-package-references` in the Idiomatic Usage ^^^^^^^^^^^^^^^ -The contents of ``__main__.py`` typically isn't fenced with -``if __name__ == '__main__'`` blocks. Instead, those files are kept short, -functions to execute from other modules. Those other modules can then be +The content of ``__main__.py`` typically isn't fenced with an +``if __name__ == '__main__'`` block. Instead, those files are kept +short and import functions to execute from other modules. Those other modules can then be easily unit-tested and are properly reusable. If used, an ``if __name__ == '__main__'`` block will still work as expected From 86084d001f3b4ecc1892fd05bb0626db0228c3cc Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sun, 1 Oct 2023 22:43:57 +0200 Subject: [PATCH 396/632] =?UTF-8?q?[3.11]=20gh-110160:=20Fix=20flaky=20`te?= =?UTF-8?q?st=5Ffind=5Fperiodic=5Fpattern`=20in=20`string=5Ftests`=20(?= =?UTF-8?q?=E2=80=A6=20(#110183)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gh-110160: Fix flaky `test_find_periodic_pattern` in `string_tests` (#110170) (cherry picked from commit 06faa9a39bd93c5e7999d52b52043ecdd0774dac) Co-authored-by: Nikita Sobolev --- Lib/test/string_tests.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Lib/test/string_tests.py b/Lib/test/string_tests.py index d69edd7bf458da..fdced1c26cf4d5 100644 --- a/Lib/test/string_tests.py +++ b/Lib/test/string_tests.py @@ -327,11 +327,12 @@ def reference_find(p, s): for i in range(len(s)): if s.startswith(p, i): return i + if p == '' and s == '': + return 0 return -1 - rr = random.randrange - choices = random.choices - for _ in range(1000): + def check_pattern(rr): + choices = random.choices p0 = ''.join(choices('abcde', k=rr(10))) * rr(10, 20) p = p0[:len(p0) - rr(10)] # pop off some characters left = ''.join(choices('abcdef', k=rr(2000))) @@ -341,6 +342,13 @@ def reference_find(p, s): self.checkequal(reference_find(p, text), text, 'find', p) + rr = random.randrange + for _ in range(1000): + check_pattern(rr) + + # Test that empty string always work: + check_pattern(lambda *args: 0) + def test_find_shift_table_overflow(self): """When the table of 8-bit shifts overflows.""" N = 2**8 + 100 From 2c79a3fe007512f05e11766c39074d9a351a0c77 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 2 Oct 2023 03:42:57 -0700 Subject: [PATCH 397/632] [3.11] Docs: bump Pygments to fix contrast ratios to meet WCAG AA guidelines (GH-110208) (#110211) Co-authored-by: Hugo van Kemenade --- Doc/constraints.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Doc/constraints.txt b/Doc/constraints.txt index 54888eaab242ee..147de1271eb2b7 100644 --- a/Doc/constraints.txt +++ b/Doc/constraints.txt @@ -10,8 +10,7 @@ colorama<0.5 imagesize<1.5 Jinja2<3.2 packaging<24 -# Pygments==2.15.0 breaks CI -Pygments<2.16,!=2.15.0 +Pygments>=2.16.1,<3 requests<3 snowballstemmer<3 sphinxcontrib-applehelp<1.1 From 8b6ee5ba3bd08368ba3c763cea28ce1464a88e62 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Mon, 2 Oct 2023 14:26:51 +0100 Subject: [PATCH 398/632] Python 3.11.6 --- Include/patchlevel.h | 4 +- Lib/pydoc_data/topics.py | 2037 +++++++++-------- Misc/NEWS.d/3.11.6.rst | 607 +++++ ...-09-01-01-39-26.gh-issue-108740.JHExAQ.rst | 4 - ...3-09-02-18-04-15.gh-issue-63760.r8hJ6q.rst | 3 - ...-08-30-15-41-47.gh-issue-108520.u0ZGP_.rst | 3 - ...-09-05-20-52-17.gh-issue-108959.6z45Sy.rst | 2 - ...3-09-07-16-05-36.gh-issue-88943.rH_X3W.rst | 3 - ...-09-09-21-17-18.gh-issue-109179.ZR8qs2.rst | 1 - ...-09-10-18-53-55.gh-issue-109207.Fei8bY.rst | 1 - ...-09-12-16-00-42.gh-issue-109351.kznGeR.rst | 2 - ...-03-19-09-39-31.gh-issue-102823.OzsOz0.rst | 2 - ...-05-29-14-10-24.gh-issue-105052.MGFwbm.rst | 1 - ...-09-10-02-39-06.gh-issue-109209.0LBewo.rst | 1 - ...2-12-24-12-50-54.gh-issue-84867.OhaLbU.rst | 2 - ...-05-22-18-39-53.gh-issue-104372.7tDRaK.rst | 5 - ...-08-14-11-18-13.gh-issue-107913.4ooY6i.rst | 3 - ...3-08-22-22-29-42.gh-issue-64662.jHl_Bt.rst | 2 - ...-08-26-12-35-39.gh-issue-105829.kyYhWI.rst | 1 - ...-08-30-20-10-28.gh-issue-108682.c2gzLQ.rst | 2 - ...-09-06-04-30-05.gh-issue-108843.WJMhsS.rst | 1 - ...-09-08-12-09-55.gh-issue-108987.x5AIG8.rst | 4 - ...3-09-09-15-08-37.gh-issue-50644.JUAZOh.rst | 4 - ...-09-11-00-32-18.gh-issue-107219.3zqyFT.rst | 5 - ...-09-13-17-22-44.gh-issue-109375.ijJHZ9.rst | 1 - ...-09-20-17-45-46.gh-issue-109613.P13ogN.rst | 4 - ...-09-22-20-16-44.gh-issue-109593.LboaNM.rst | 1 - ...-09-25-23-00-37.gh-issue-109631.eWSqpO.rst | 3 - ...-09-28-18-50-33.gh-issue-110038.nx_gCu.rst | 3 - ...-09-28-18-53-11.gh-issue-110036.fECxTj.rst | 5 - ...2-06-16-17-50-58.gh-issue-93353.JdpATx.rst | 2 - ...2-10-20-17-49-50.gh-issue-95027.viRpJB.rst | 4 - ...2-10-31-14-47-49.gh-issue-98903.7KinCV.rst | 2 - ...-12-08-00-03-37.gh-issue-100086.1zYpto.rst | 3 - ...-06-28-02-51-08.gh-issue-101634.Rayczr.rst | 3 - ...-08-24-04-23-35.gh-issue-108388.mr0MeE.rst | 4 - ...-08-24-06-10-36.gh-issue-108388.YCVB0D.rst | 2 - ...-09-02-19-06-52.gh-issue-108822.arTbBI.rst | 4 - ...-09-03-21-18-35.gh-issue-108851.CCuHyI.rst | 2 - ...-09-03-21-41-10.gh-issue-108851.xFTYOE.rst | 3 - ...3-09-04-15-18-14.gh-issue-89392.8A4T5p.rst | 2 - ...-09-05-23-00-09.gh-issue-108962.R4NwuU.rst | 3 - ...3-09-06-15-36-51.gh-issue-91960.P3nD5v.rst | 7 - ...-09-06-18-27-53.gh-issue-109015.1dS1AQ.rst | 6 - ...-09-10-19-59-57.gh-issue-109230.SRNLFQ.rst | 5 - ...-09-10-22-32-20.gh-issue-109237.SvgKwD.rst | 4 - ...-09-13-05-58-09.gh-issue-104736.lA25Fu.rst | 4 - ...-09-14-22-58-47.gh-issue-109396.J1a4jR.rst | 3 - ...-09-20-02-32-17.gh-issue-103053.AoUJuK.rst | 4 - ...-09-26-00-49-18.gh-issue-109748.nxlT1i.rst | 3 - ...-09-28-14-47-14.gh-issue-109594.DB5KPP.rst | 4 - ...-09-28-18-14-52.gh-issue-110033.2yHMx0.rst | 5 - ...-09-29-12-48-42.gh-issue-110088.qUhRga.rst | 4 - ...-09-29-14-11-30.gh-issue-110031.fQnFnc.rst | 2 - ...-09-27-23-31-54.gh-issue-109991.sUUYY8.rst | 2 - ...-09-05-10-08-47.gh-issue-107565.CIMftz.rst | 1 - ...-09-28-17-09-23.gh-issue-109991.CIMftz.rst | 1 - ...-09-27-22-35-22.gh-issue-109991.-xJzaF.rst | 1 - README.rst | 2 +- 59 files changed, 1640 insertions(+), 1169 deletions(-) create mode 100644 Misc/NEWS.d/3.11.6.rst delete mode 100644 Misc/NEWS.d/next/Build/2023-09-01-01-39-26.gh-issue-108740.JHExAQ.rst delete mode 100644 Misc/NEWS.d/next/Build/2023-09-02-18-04-15.gh-issue-63760.r8hJ6q.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-08-30-15-41-47.gh-issue-108520.u0ZGP_.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-09-05-20-52-17.gh-issue-108959.6z45Sy.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-09-07-16-05-36.gh-issue-88943.rH_X3W.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-09-09-21-17-18.gh-issue-109179.ZR8qs2.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-09-10-18-53-55.gh-issue-109207.Fei8bY.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-09-12-16-00-42.gh-issue-109351.kznGeR.rst delete mode 100644 Misc/NEWS.d/next/Documentation/2023-03-19-09-39-31.gh-issue-102823.OzsOz0.rst delete mode 100644 Misc/NEWS.d/next/Documentation/2023-05-29-14-10-24.gh-issue-105052.MGFwbm.rst delete mode 100644 Misc/NEWS.d/next/Documentation/2023-09-10-02-39-06.gh-issue-109209.0LBewo.rst delete mode 100644 Misc/NEWS.d/next/Library/2022-12-24-12-50-54.gh-issue-84867.OhaLbU.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-05-22-18-39-53.gh-issue-104372.7tDRaK.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-08-14-11-18-13.gh-issue-107913.4ooY6i.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-08-22-22-29-42.gh-issue-64662.jHl_Bt.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-08-26-12-35-39.gh-issue-105829.kyYhWI.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-08-30-20-10-28.gh-issue-108682.c2gzLQ.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-09-06-04-30-05.gh-issue-108843.WJMhsS.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-09-08-12-09-55.gh-issue-108987.x5AIG8.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-09-09-15-08-37.gh-issue-50644.JUAZOh.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-09-11-00-32-18.gh-issue-107219.3zqyFT.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-09-13-17-22-44.gh-issue-109375.ijJHZ9.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-09-20-17-45-46.gh-issue-109613.P13ogN.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-09-22-20-16-44.gh-issue-109593.LboaNM.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-09-25-23-00-37.gh-issue-109631.eWSqpO.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-09-28-18-50-33.gh-issue-110038.nx_gCu.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-09-28-18-53-11.gh-issue-110036.fECxTj.rst delete mode 100644 Misc/NEWS.d/next/Tests/2022-06-16-17-50-58.gh-issue-93353.JdpATx.rst delete mode 100644 Misc/NEWS.d/next/Tests/2022-10-20-17-49-50.gh-issue-95027.viRpJB.rst delete mode 100644 Misc/NEWS.d/next/Tests/2022-10-31-14-47-49.gh-issue-98903.7KinCV.rst delete mode 100644 Misc/NEWS.d/next/Tests/2022-12-08-00-03-37.gh-issue-100086.1zYpto.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-06-28-02-51-08.gh-issue-101634.Rayczr.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-08-24-04-23-35.gh-issue-108388.mr0MeE.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-08-24-06-10-36.gh-issue-108388.YCVB0D.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-09-02-19-06-52.gh-issue-108822.arTbBI.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-09-03-21-18-35.gh-issue-108851.CCuHyI.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-09-03-21-41-10.gh-issue-108851.xFTYOE.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-09-04-15-18-14.gh-issue-89392.8A4T5p.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-09-05-23-00-09.gh-issue-108962.R4NwuU.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-09-06-15-36-51.gh-issue-91960.P3nD5v.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-09-06-18-27-53.gh-issue-109015.1dS1AQ.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-09-10-19-59-57.gh-issue-109230.SRNLFQ.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-09-10-22-32-20.gh-issue-109237.SvgKwD.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-09-13-05-58-09.gh-issue-104736.lA25Fu.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-09-14-22-58-47.gh-issue-109396.J1a4jR.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-09-20-02-32-17.gh-issue-103053.AoUJuK.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-09-26-00-49-18.gh-issue-109748.nxlT1i.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-09-28-14-47-14.gh-issue-109594.DB5KPP.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-09-28-18-14-52.gh-issue-110033.2yHMx0.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-09-29-12-48-42.gh-issue-110088.qUhRga.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-09-29-14-11-30.gh-issue-110031.fQnFnc.rst delete mode 100644 Misc/NEWS.d/next/Tools-Demos/2023-09-27-23-31-54.gh-issue-109991.sUUYY8.rst delete mode 100644 Misc/NEWS.d/next/Windows/2023-09-05-10-08-47.gh-issue-107565.CIMftz.rst delete mode 100644 Misc/NEWS.d/next/Windows/2023-09-28-17-09-23.gh-issue-109991.CIMftz.rst delete mode 100644 Misc/NEWS.d/next/macOS/2023-09-27-22-35-22.gh-issue-109991.-xJzaF.rst diff --git a/Include/patchlevel.h b/Include/patchlevel.h index 59f90803983732..70d71c11377ede 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -18,12 +18,12 @@ /*--start constants--*/ #define PY_MAJOR_VERSION 3 #define PY_MINOR_VERSION 11 -#define PY_MICRO_VERSION 5 +#define PY_MICRO_VERSION 6 #define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_FINAL #define PY_RELEASE_SERIAL 0 /* Version as a string */ -#define PY_VERSION "3.11.5+" +#define PY_VERSION "3.11.6" /*--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 1b1251b690998e..06422d21c6b944 100644 --- a/Lib/pydoc_data/topics.py +++ b/Lib/pydoc_data/topics.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Autogenerated by Sphinx on Thu Aug 24 13:07:17 2023 +# Autogenerated by Sphinx on Mon Oct 2 14:27:48 2023 # as part of the release process. topics = {'assert': 'The "assert" statement\n' '**********************\n' @@ -1076,9 +1076,7 @@ 'for each\n' ' instance.\n' '\n' - '\n' - 'Notes on using *__slots__*\n' - '--------------------------\n' + 'Notes on using *__slots__*:\n' '\n' '* When inheriting from a class without *__slots__*, the ' '"__dict__" and\n' @@ -10003,9 +10001,7 @@ 'each\n' ' instance.\n' '\n' - '\n' - 'Notes on using *__slots__*\n' - '~~~~~~~~~~~~~~~~~~~~~~~~~~\n' + 'Notes on using *__slots__*:\n' '\n' '* When inheriting from a class without *__slots__*, the ' '"__dict__" and\n' @@ -12374,71 +12370,71 @@ 'those\n' 'used by Standard C. The recognized escape sequences are:\n' '\n' - '+-------------------+-----------------------------------+---------+\n' - '| Escape Sequence | Meaning | Notes ' - '|\n' - '|===================|===================================|=========|\n' - '| "\\" | Backslash and newline ignored | ' - '(1) |\n' - '+-------------------+-----------------------------------+---------+\n' - '| "\\\\" | Backslash ("\\") ' - '| |\n' - '+-------------------+-----------------------------------+---------+\n' - '| "\\\'" | Single quote ("\'") ' + '+---------------------------+-----------------------------------+---------+\n' + '| Escape Sequence | Meaning | ' + 'Notes |\n' + '|===========================|===================================|=========|\n' + '| "\\" | Backslash and newline ignored ' + '| (1) |\n' + '+---------------------------+-----------------------------------+---------+\n' + '| "\\\\" | Backslash ' + '("\\") | |\n' + '+---------------------------+-----------------------------------+---------+\n' + '| "\\\'" | Single quote ' + '("\'") | |\n' + '+---------------------------+-----------------------------------+---------+\n' + '| "\\"" | Double quote (""") ' '| |\n' - '+-------------------+-----------------------------------+---------+\n' - '| "\\"" | Double quote (""") ' + '+---------------------------+-----------------------------------+---------+\n' + '| "\\a" | ASCII Bell (BEL) ' '| |\n' - '+-------------------+-----------------------------------+---------+\n' - '| "\\a" | ASCII Bell (BEL) ' + '+---------------------------+-----------------------------------+---------+\n' + '| "\\b" | ASCII Backspace (BS) ' '| |\n' - '+-------------------+-----------------------------------+---------+\n' - '| "\\b" | ASCII Backspace (BS) ' + '+---------------------------+-----------------------------------+---------+\n' + '| "\\f" | ASCII Formfeed (FF) ' '| |\n' - '+-------------------+-----------------------------------+---------+\n' - '| "\\f" | ASCII Formfeed (FF) ' + '+---------------------------+-----------------------------------+---------+\n' + '| "\\n" | ASCII Linefeed (LF) ' '| |\n' - '+-------------------+-----------------------------------+---------+\n' - '| "\\n" | ASCII Linefeed (LF) ' + '+---------------------------+-----------------------------------+---------+\n' + '| "\\r" | ASCII Carriage Return (CR) ' '| |\n' - '+-------------------+-----------------------------------+---------+\n' - '| "\\r" | ASCII Carriage Return (CR) ' + '+---------------------------+-----------------------------------+---------+\n' + '| "\\t" | ASCII Horizontal Tab (TAB) ' '| |\n' - '+-------------------+-----------------------------------+---------+\n' - '| "\\t" | ASCII Horizontal Tab (TAB) ' + '+---------------------------+-----------------------------------+---------+\n' + '| "\\v" | ASCII Vertical Tab (VT) ' '| |\n' - '+-------------------+-----------------------------------+---------+\n' - '| "\\v" | ASCII Vertical Tab (VT) ' - '| |\n' - '+-------------------+-----------------------------------+---------+\n' - '| "\\ooo" | Character with octal value *ooo* | ' - '(2,4) |\n' - '+-------------------+-----------------------------------+---------+\n' - '| "\\xhh" | Character with hex value *hh* | ' - '(3,4) |\n' - '+-------------------+-----------------------------------+---------+\n' + '+---------------------------+-----------------------------------+---------+\n' + '| "\\*ooo*" | Character with octal value *ooo* ' + '| (2,4) |\n' + '+---------------------------+-----------------------------------+---------+\n' + '| "\\x*hh*" | Character with hex value *hh* ' + '| (3,4) |\n' + '+---------------------------+-----------------------------------+---------+\n' '\n' 'Escape sequences only recognized in string literals are:\n' '\n' - '+-------------------+-----------------------------------+---------+\n' - '| Escape Sequence | Meaning | Notes ' - '|\n' - '|===================|===================================|=========|\n' - '| "\\N{name}" | Character named *name* in the | ' - '(5) |\n' - '| | Unicode database | ' - '|\n' - '+-------------------+-----------------------------------+---------+\n' - '| "\\uxxxx" | Character with 16-bit hex value | ' - '(6) |\n' - '| | *xxxx* | ' - '|\n' - '+-------------------+-----------------------------------+---------+\n' - '| "\\Uxxxxxxxx" | Character with 32-bit hex value | ' - '(7) |\n' - '| | *xxxxxxxx* | ' - '|\n' - '+-------------------+-----------------------------------+---------+\n' + '+---------------------------+-----------------------------------+---------+\n' + '| Escape Sequence | Meaning | ' + 'Notes |\n' + '|===========================|===================================|=========|\n' + '| "\\N{*name*}" | Character named *name* in the ' + '| (5) |\n' + '| | Unicode database ' + '| |\n' + '+---------------------------+-----------------------------------+---------+\n' + '| "\\u*xxxx*" | Character with 16-bit hex value ' + '| (6) |\n' + '| | *xxxx* ' + '| |\n' + '+---------------------------+-----------------------------------+---------+\n' + '| "\\U*xxxxxxxx*" | Character with 32-bit hex value ' + '| (7) |\n' + '| | *xxxxxxxx* ' + '| |\n' + '+---------------------------+-----------------------------------+---------+\n' '\n' 'Notes:\n' '\n' @@ -12896,1145 +12892,1172 @@ 'definition\n' 'may change in the future.\n' '\n' + '\n' 'None\n' - ' This type has a single value. There is a single object with ' - 'this\n' - ' value. This object is accessed through the built-in name "None". ' - 'It\n' - ' is used to signify the absence of a value in many situations, ' - 'e.g.,\n' - ' it is returned from functions that don’t explicitly return\n' - ' anything. Its truth value is false.\n' + '====\n' + '\n' + 'This type has a single value. There is a single object with this\n' + 'value. This object is accessed through the built-in name "None". It ' + 'is\n' + 'used to signify the absence of a value in many situations, e.g., it ' + 'is\n' + 'returned from functions that don’t explicitly return anything. Its\n' + 'truth value is false.\n' + '\n' '\n' 'NotImplemented\n' - ' This type has a single value. There is a single object with ' - 'this\n' - ' value. This object is accessed through the built-in name\n' - ' "NotImplemented". Numeric methods and rich comparison methods\n' - ' should return this value if they do not implement the operation ' - 'for\n' - ' the operands provided. (The interpreter will then try the\n' - ' reflected operation, or some other fallback, depending on the\n' - ' operator.) It should not be evaluated in a boolean context.\n' + '==============\n' + '\n' + 'This type has a single value. There is a single object with this\n' + 'value. This object is accessed through the built-in name\n' + '"NotImplemented". Numeric methods and rich comparison methods ' + 'should\n' + 'return this value if they do not implement the operation for the\n' + 'operands provided. (The interpreter will then try the reflected\n' + 'operation, or some other fallback, depending on the operator.) It\n' + 'should not be evaluated in a boolean context.\n' '\n' - ' See Implementing the arithmetic operations for more details.\n' + 'See Implementing the arithmetic operations for more details.\n' + '\n' + 'Changed in version 3.9: Evaluating "NotImplemented" in a boolean\n' + 'context is deprecated. While it currently evaluates as true, it ' + 'will\n' + 'emit a "DeprecationWarning". It will raise a "TypeError" in a ' + 'future\n' + 'version of Python.\n' '\n' - ' Changed in version 3.9: Evaluating "NotImplemented" in a ' - 'boolean\n' - ' context is deprecated. While it currently evaluates as true, it\n' - ' will emit a "DeprecationWarning". It will raise a "TypeError" in ' - 'a\n' - ' future version of Python.\n' '\n' 'Ellipsis\n' - ' This type has a single value. There is a single object with ' - 'this\n' - ' value. This object is accessed through the literal "..." or the\n' - ' built-in name "Ellipsis". Its truth value is true.\n' + '========\n' + '\n' + 'This type has a single value. There is a single object with this\n' + 'value. This object is accessed through the literal "..." or the ' + 'built-\n' + 'in name "Ellipsis". Its truth value is true.\n' + '\n' '\n' '"numbers.Number"\n' - ' These are created by numeric literals and returned as results ' - 'by\n' - ' arithmetic operators and arithmetic built-in functions. ' - 'Numeric\n' - ' objects are immutable; once created their value never changes.\n' - ' Python numbers are of course strongly related to mathematical\n' - ' numbers, but subject to the limitations of numerical ' - 'representation\n' - ' in computers.\n' - '\n' - ' The string representations of the numeric classes, computed by\n' - ' "__repr__()" and "__str__()", have the following properties:\n' - '\n' - ' * They are valid numeric literals which, when passed to their ' + '================\n' + '\n' + 'These are created by numeric literals and returned as results by\n' + 'arithmetic operators and arithmetic built-in functions. Numeric\n' + 'objects are immutable; once created their value never changes. ' + 'Python\n' + 'numbers are of course strongly related to mathematical numbers, ' + 'but\n' + 'subject to the limitations of numerical representation in ' + 'computers.\n' + '\n' + 'The string representations of the numeric classes, computed by\n' + '"__repr__()" and "__str__()", have the following properties:\n' + '\n' + '* They are valid numeric literals which, when passed to their ' 'class\n' - ' constructor, produce an object having the value of the ' - 'original\n' - ' numeric.\n' + ' constructor, produce an object having the value of the original\n' + ' numeric.\n' '\n' - ' * The representation is in base 10, when possible.\n' + '* The representation is in base 10, when possible.\n' '\n' - ' * Leading zeros, possibly excepting a single zero before a ' - 'decimal\n' - ' point, are not shown.\n' + '* Leading zeros, possibly excepting a single zero before a decimal\n' + ' point, are not shown.\n' '\n' - ' * Trailing zeros, possibly excepting a single zero after a ' - 'decimal\n' - ' point, are not shown.\n' + '* Trailing zeros, possibly excepting a single zero after a decimal\n' + ' point, are not shown.\n' '\n' - ' * A sign is shown only when the number is negative.\n' + '* A sign is shown only when the number is negative.\n' '\n' - ' Python distinguishes between integers, floating point numbers, ' - 'and\n' - ' complex numbers:\n' + 'Python distinguishes between integers, floating point numbers, and\n' + 'complex numbers:\n' '\n' - ' "numbers.Integral"\n' - ' These represent elements from the mathematical set of ' - 'integers\n' - ' (positive and negative).\n' '\n' - ' There are two types of integers:\n' + '"numbers.Integral"\n' + '------------------\n' '\n' - ' Integers ("int")\n' - ' These represent numbers in an unlimited range, subject to\n' - ' available (virtual) memory only. For the purpose of ' - 'shift\n' - ' and mask operations, a binary representation is assumed, ' - 'and\n' - ' negative numbers are represented in a variant of 2’s\n' - ' complement which gives the illusion of an infinite string ' - 'of\n' - ' sign bits extending to the left.\n' + 'These represent elements from the mathematical set of integers\n' + '(positive and negative).\n' '\n' - ' Booleans ("bool")\n' - ' These represent the truth values False and True. The two\n' - ' objects representing the values "False" and "True" are ' - 'the\n' - ' only Boolean objects. The Boolean type is a subtype of ' + 'Note:\n' + '\n' + ' The rules for integer representation are intended to give the ' + 'most\n' + ' meaningful interpretation of shift and mask operations involving\n' + ' negative integers.\n' + '\n' + 'There are two types of integers:\n' + '\n' + 'Integers ("int")\n' + ' These represent numbers in an unlimited range, subject to ' + 'available\n' + ' (virtual) memory only. For the purpose of shift and mask\n' + ' operations, a binary representation is assumed, and negative\n' + ' numbers are represented in a variant of 2’s complement which ' + 'gives\n' + ' the illusion of an infinite string of sign bits extending to ' 'the\n' - ' integer type, and Boolean values behave like the values 0 ' - 'and\n' - ' 1, respectively, in almost all contexts, the exception ' - 'being\n' - ' that when converted to a string, the strings ""False"" or\n' - ' ""True"" are returned, respectively.\n' + ' left.\n' + '\n' + 'Booleans ("bool")\n' + ' These represent the truth values False and True. The two ' + 'objects\n' + ' representing the values "False" and "True" are the only Boolean\n' + ' objects. The Boolean type is a subtype of the integer type, and\n' + ' Boolean values behave like the values 0 and 1, respectively, in\n' + ' almost all contexts, the exception being that when converted to ' + 'a\n' + ' string, the strings ""False"" or ""True"" are returned,\n' + ' respectively.\n' + '\n' + '\n' + '"numbers.Real" ("float")\n' + '------------------------\n' '\n' - ' The rules for integer representation are intended to give ' + 'These represent machine-level double precision floating point ' + 'numbers.\n' + 'You are at the mercy of the underlying machine architecture (and C ' + 'or\n' + 'Java implementation) for the accepted range and handling of ' + 'overflow.\n' + 'Python does not support single-precision floating point numbers; ' 'the\n' - ' most meaningful interpretation of shift and mask operations\n' - ' involving negative integers.\n' - '\n' - ' "numbers.Real" ("float")\n' - ' These represent machine-level double precision floating ' - 'point\n' - ' numbers. You are at the mercy of the underlying machine\n' - ' architecture (and C or Java implementation) for the accepted\n' - ' range and handling of overflow. Python does not support ' - 'single-\n' - ' precision floating point numbers; the savings in processor ' - 'and\n' - ' memory usage that are usually the reason for using these are\n' - ' dwarfed by the overhead of using objects in Python, so there ' - 'is\n' - ' no reason to complicate the language with two kinds of ' - 'floating\n' - ' point numbers.\n' - '\n' - ' "numbers.Complex" ("complex")\n' - ' These represent complex numbers as a pair of machine-level\n' - ' double precision floating point numbers. The same caveats ' - 'apply\n' - ' as for floating point numbers. The real and imaginary parts ' - 'of a\n' - ' complex number "z" can be retrieved through the read-only\n' - ' attributes "z.real" and "z.imag".\n' + 'savings in processor and memory usage that are usually the reason ' + 'for\n' + 'using these are dwarfed by the overhead of using objects in Python, ' + 'so\n' + 'there is no reason to complicate the language with two kinds of\n' + 'floating point numbers.\n' + '\n' + '\n' + '"numbers.Complex" ("complex")\n' + '-----------------------------\n' + '\n' + 'These represent complex numbers as a pair of machine-level double\n' + 'precision floating point numbers. The same caveats apply as for\n' + 'floating point numbers. The real and imaginary parts of a complex\n' + 'number "z" can be retrieved through the read-only attributes ' + '"z.real"\n' + 'and "z.imag".\n' + '\n' '\n' 'Sequences\n' - ' These represent finite ordered sets indexed by non-negative\n' - ' numbers. The built-in function "len()" returns the number of ' - 'items\n' - ' of a sequence. When the length of a sequence is *n*, the index ' + '=========\n' + '\n' + 'These represent finite ordered sets indexed by non-negative ' + 'numbers.\n' + 'The built-in function "len()" returns the number of items of a\n' + 'sequence. When the length of a sequence is *n*, the index set ' + 'contains\n' + 'the numbers 0, 1, …, *n*-1. Item *i* of sequence *a* is selected ' + 'by\n' + '"a[i]".\n' + '\n' + 'Sequences also support slicing: "a[i:j]" selects all items with ' + 'index\n' + '*k* such that *i* "<=" *k* "<" *j*. When used as an expression, a\n' + 'slice is a sequence of the same type. This implies that the index ' 'set\n' - ' contains the numbers 0, 1, …, *n*-1. Item *i* of sequence *a* ' - 'is\n' - ' selected by "a[i]".\n' + 'is renumbered so that it starts at 0.\n' '\n' - ' Sequences also support slicing: "a[i:j]" selects all items with\n' - ' index *k* such that *i* "<=" *k* "<" *j*. When used as an\n' - ' expression, a slice is a sequence of the same type. This ' - 'implies\n' - ' that the index set is renumbered so that it starts at 0.\n' + 'Some sequences also support “extended slicing” with a third “step”\n' + 'parameter: "a[i:j:k]" selects all items of *a* with index *x* where ' + '"x\n' + '= i + n*k", *n* ">=" "0" and *i* "<=" *x* "<" *j*.\n' '\n' - ' Some sequences also support “extended slicing” with a third ' - '“step”\n' - ' parameter: "a[i:j:k]" selects all items of *a* with index *x* ' - 'where\n' - ' "x = i + n*k", *n* ">=" "0" and *i* "<=" *x* "<" *j*.\n' + 'Sequences are distinguished according to their mutability:\n' '\n' - ' Sequences are distinguished according to their mutability:\n' '\n' - ' Immutable sequences\n' - ' An object of an immutable sequence type cannot change once it ' - 'is\n' - ' created. (If the object contains references to other ' - 'objects,\n' - ' these other objects may be mutable and may be changed; ' - 'however,\n' - ' the collection of objects directly referenced by an ' - 'immutable\n' - ' object cannot change.)\n' + 'Immutable sequences\n' + '-------------------\n' '\n' - ' The following types are immutable sequences:\n' + 'An object of an immutable sequence type cannot change once it is\n' + 'created. (If the object contains references to other objects, ' + 'these\n' + 'other objects may be mutable and may be changed; however, the\n' + 'collection of objects directly referenced by an immutable object\n' + 'cannot change.)\n' '\n' - ' Strings\n' - ' A string is a sequence of values that represent Unicode ' - 'code\n' - ' points. All the code points in the range "U+0000 - ' - 'U+10FFFF"\n' - ' can be represented in a string. Python doesn’t have a ' - 'char\n' - ' type; instead, every code point in the string is ' - 'represented\n' - ' as a string object with length "1". The built-in ' - 'function\n' - ' "ord()" converts a code point from its string form to an\n' - ' integer in the range "0 - 10FFFF"; "chr()" converts an\n' - ' integer in the range "0 - 10FFFF" to the corresponding ' - 'length\n' - ' "1" string object. "str.encode()" can be used to convert ' - 'a\n' - ' "str" to "bytes" using the given text encoding, and\n' - ' "bytes.decode()" can be used to achieve the opposite.\n' + 'The following types are immutable sequences:\n' '\n' - ' Tuples\n' - ' The items of a tuple are arbitrary Python objects. Tuples ' - 'of\n' - ' two or more items are formed by comma-separated lists of\n' - ' expressions. A tuple of one item (a ‘singleton’) can be\n' - ' formed by affixing a comma to an expression (an expression ' - 'by\n' - ' itself does not create a tuple, since parentheses must be\n' - ' usable for grouping of expressions). An empty tuple can ' + 'Strings\n' + ' A string is a sequence of values that represent Unicode code\n' + ' points. All the code points in the range "U+0000 - U+10FFFF" can ' 'be\n' - ' formed by an empty pair of parentheses.\n' - '\n' - ' Bytes\n' - ' A bytes object is an immutable array. The items are ' - '8-bit\n' - ' bytes, represented by integers in the range 0 <= x < 256.\n' - ' Bytes literals (like "b\'abc\'") and the built-in ' - '"bytes()"\n' - ' constructor can be used to create bytes objects. Also, ' - 'bytes\n' - ' objects can be decoded to strings via the "decode()" ' - 'method.\n' + ' represented in a string. Python doesn’t have a char type; ' + 'instead,\n' + ' every code point in the string is represented as a string ' + 'object\n' + ' with length "1". The built-in function "ord()" converts a code\n' + ' point from its string form to an integer in the range "0 - ' + '10FFFF";\n' + ' "chr()" converts an integer in the range "0 - 10FFFF" to the\n' + ' corresponding length "1" string object. "str.encode()" can be ' + 'used\n' + ' to convert a "str" to "bytes" using the given text encoding, ' + 'and\n' + ' "bytes.decode()" can be used to achieve the opposite.\n' '\n' - ' Mutable sequences\n' - ' Mutable sequences can be changed after they are created. ' - 'The\n' - ' subscription and slicing notations can be used as the target ' + 'Tuples\n' + ' The items of a tuple are arbitrary Python objects. Tuples of two ' + 'or\n' + ' more items are formed by comma-separated lists of expressions. ' + 'A\n' + ' tuple of one item (a ‘singleton’) can be formed by affixing a ' + 'comma\n' + ' to an expression (an expression by itself does not create a ' + 'tuple,\n' + ' since parentheses must be usable for grouping of expressions). ' + 'An\n' + ' empty tuple can be formed by an empty pair of parentheses.\n' + '\n' + 'Bytes\n' + ' A bytes object is an immutable array. The items are 8-bit ' + 'bytes,\n' + ' represented by integers in the range 0 <= x < 256. Bytes ' + 'literals\n' + ' (like "b\'abc\'") and the built-in "bytes()" constructor can be ' + 'used\n' + ' to create bytes objects. Also, bytes objects can be decoded to\n' + ' strings via the "decode()" method.\n' + '\n' + '\n' + 'Mutable sequences\n' + '-----------------\n' + '\n' + 'Mutable sequences can be changed after they are created. The\n' + 'subscription and slicing notations can be used as the target of\n' + 'assignment and "del" (delete) statements.\n' + '\n' + 'Note:\n' + '\n' + ' The "collections" and "array" module provide additional examples ' 'of\n' - ' assignment and "del" (delete) statements.\n' + ' mutable sequence types.\n' '\n' - ' There are currently two intrinsic mutable sequence types:\n' + 'There are currently two intrinsic mutable sequence types:\n' '\n' - ' Lists\n' - ' The items of a list are arbitrary Python objects. Lists ' - 'are\n' - ' formed by placing a comma-separated list of expressions ' - 'in\n' - ' square brackets. (Note that there are no special cases ' - 'needed\n' - ' to form lists of length 0 or 1.)\n' + 'Lists\n' + ' The items of a list are arbitrary Python objects. Lists are ' + 'formed\n' + ' by placing a comma-separated list of expressions in square\n' + ' brackets. (Note that there are no special cases needed to form\n' + ' lists of length 0 or 1.)\n' '\n' - ' Byte Arrays\n' - ' A bytearray object is a mutable array. They are created ' - 'by\n' - ' the built-in "bytearray()" constructor. Aside from being\n' - ' mutable (and hence unhashable), byte arrays otherwise ' - 'provide\n' - ' the same interface and functionality as immutable "bytes"\n' - ' objects.\n' + 'Byte Arrays\n' + ' A bytearray object is a mutable array. They are created by the\n' + ' built-in "bytearray()" constructor. Aside from being mutable ' + '(and\n' + ' hence unhashable), byte arrays otherwise provide the same ' + 'interface\n' + ' and functionality as immutable "bytes" objects.\n' '\n' - ' The extension module "array" provides an additional example ' - 'of a\n' - ' mutable sequence type, as does the "collections" module.\n' '\n' 'Set types\n' - ' These represent unordered, finite sets of unique, immutable\n' - ' objects. As such, they cannot be indexed by any subscript. ' - 'However,\n' - ' they can be iterated over, and the built-in function "len()"\n' - ' returns the number of items in a set. Common uses for sets are ' - 'fast\n' - ' membership testing, removing duplicates from a sequence, and\n' - ' computing mathematical operations such as intersection, union,\n' - ' difference, and symmetric difference.\n' - '\n' - ' For set elements, the same immutability rules apply as for\n' - ' dictionary keys. Note that numeric types obey the normal rules ' - 'for\n' - ' numeric comparison: if two numbers compare equal (e.g., "1" and\n' - ' "1.0"), only one of them can be contained in a set.\n' + '=========\n' + '\n' + 'These represent unordered, finite sets of unique, immutable ' + 'objects.\n' + 'As such, they cannot be indexed by any subscript. However, they can ' + 'be\n' + 'iterated over, and the built-in function "len()" returns the number ' + 'of\n' + 'items in a set. Common uses for sets are fast membership testing,\n' + 'removing duplicates from a sequence, and computing mathematical\n' + 'operations such as intersection, union, difference, and symmetric\n' + 'difference.\n' + '\n' + 'For set elements, the same immutability rules apply as for ' + 'dictionary\n' + 'keys. Note that numeric types obey the normal rules for numeric\n' + 'comparison: if two numbers compare equal (e.g., "1" and "1.0"), ' + 'only\n' + 'one of them can be contained in a set.\n' + '\n' + 'There are currently two intrinsic set types:\n' '\n' - ' There are currently two intrinsic set types:\n' + 'Sets\n' + ' These represent a mutable set. They are created by the built-in\n' + ' "set()" constructor and can be modified afterwards by several\n' + ' methods, such as "add()".\n' '\n' - ' Sets\n' - ' These represent a mutable set. They are created by the ' + 'Frozen sets\n' + ' These represent an immutable set. They are created by the ' 'built-in\n' - ' "set()" constructor and can be modified afterwards by ' - 'several\n' - ' methods, such as "add()".\n' - '\n' - ' Frozen sets\n' - ' These represent an immutable set. They are created by the\n' - ' built-in "frozenset()" constructor. As a frozenset is ' - 'immutable\n' - ' and *hashable*, it can be used again as an element of ' - 'another\n' - ' set, or as a dictionary key.\n' + ' "frozenset()" constructor. As a frozenset is immutable and\n' + ' *hashable*, it can be used again as an element of another set, ' + 'or\n' + ' as a dictionary key.\n' + '\n' '\n' 'Mappings\n' - ' These represent finite sets of objects indexed by arbitrary ' - 'index\n' - ' sets. The subscript notation "a[k]" selects the item indexed by ' + '========\n' + '\n' + 'These represent finite sets of objects indexed by arbitrary index\n' + 'sets. The subscript notation "a[k]" selects the item indexed by ' '"k"\n' - ' from the mapping "a"; this can be used in expressions and as ' - 'the\n' - ' target of assignments or "del" statements. The built-in ' - 'function\n' - ' "len()" returns the number of items in a mapping.\n' + 'from the mapping "a"; this can be used in expressions and as the\n' + 'target of assignments or "del" statements. The built-in function\n' + '"len()" returns the number of items in a mapping.\n' '\n' - ' There is currently a single intrinsic mapping type:\n' + 'There is currently a single intrinsic mapping type:\n' '\n' - ' Dictionaries\n' - ' These represent finite sets of objects indexed by nearly\n' - ' arbitrary values. The only types of values not acceptable ' - 'as\n' - ' keys are values containing lists or dictionaries or other\n' - ' mutable types that are compared by value rather than by ' - 'object\n' - ' identity, the reason being that the efficient implementation ' - 'of\n' - ' dictionaries requires a key’s hash value to remain constant.\n' - ' Numeric types used for keys obey the normal rules for ' - 'numeric\n' - ' comparison: if two numbers compare equal (e.g., "1" and ' - '"1.0")\n' - ' then they can be used interchangeably to index the same\n' - ' dictionary entry.\n' - '\n' - ' Dictionaries preserve insertion order, meaning that keys will ' - 'be\n' - ' produced in the same order they were added sequentially over ' - 'the\n' - ' dictionary. Replacing an existing key does not change the ' - 'order,\n' - ' however removing a key and re-inserting it will add it to ' + '\n' + 'Dictionaries\n' + '------------\n' + '\n' + 'These represent finite sets of objects indexed by nearly arbitrary\n' + 'values. The only types of values not acceptable as keys are ' + 'values\n' + 'containing lists or dictionaries or other mutable types that are\n' + 'compared by value rather than by object identity, the reason being\n' + 'that the efficient implementation of dictionaries requires a key’s\n' + 'hash value to remain constant. Numeric types used for keys obey ' 'the\n' - ' end instead of keeping its old place.\n' + 'normal rules for numeric comparison: if two numbers compare equal\n' + '(e.g., "1" and "1.0") then they can be used interchangeably to ' + 'index\n' + 'the same dictionary entry.\n' '\n' - ' Dictionaries are mutable; they can be created by the "{...}"\n' - ' notation (see section Dictionary displays).\n' + 'Dictionaries preserve insertion order, meaning that keys will be\n' + 'produced in the same order they were added sequentially over the\n' + 'dictionary. Replacing an existing key does not change the order,\n' + 'however removing a key and re-inserting it will add it to the end\n' + 'instead of keeping its old place.\n' '\n' - ' The extension modules "dbm.ndbm" and "dbm.gnu" provide\n' - ' additional examples of mapping types, as does the ' - '"collections"\n' - ' module.\n' + 'Dictionaries are mutable; they can be created by the "{...}" ' + 'notation\n' + '(see section Dictionary displays).\n' + '\n' + 'The extension modules "dbm.ndbm" and "dbm.gnu" provide additional\n' + 'examples of mapping types, as does the "collections" module.\n' + '\n' + 'Changed in version 3.7: Dictionaries did not preserve insertion ' + 'order\n' + 'in versions of Python before 3.6. In CPython 3.6, insertion order ' + 'was\n' + 'preserved, but it was considered an implementation detail at that ' + 'time\n' + 'rather than a language guarantee.\n' '\n' - ' Changed in version 3.7: Dictionaries did not preserve ' - 'insertion\n' - ' order in versions of Python before 3.6. In CPython 3.6,\n' - ' insertion order was preserved, but it was considered an\n' - ' implementation detail at that time rather than a language\n' - ' guarantee.\n' '\n' 'Callable types\n' - ' These are the types to which the function call operation (see\n' - ' section Calls) can be applied:\n' + '==============\n' '\n' - ' User-defined functions\n' - ' A user-defined function object is created by a function\n' - ' definition (see section Function definitions). It should be\n' - ' called with an argument list containing the same number of ' - 'items\n' - ' as the function’s formal parameter list.\n' + 'These are the types to which the function call operation (see ' + 'section\n' + 'Calls) can be applied:\n' '\n' - ' Special attributes:\n' '\n' - ' ' + 'User-defined functions\n' + '----------------------\n' + '\n' + 'A user-defined function object is created by a function definition\n' + '(see section Function definitions). It should be called with an\n' + 'argument list containing the same number of items as the ' + 'function’s\n' + 'formal parameter list.\n' + '\n' + 'Special attributes:\n' + '\n' '+---------------------------+---------------------------------+-------------+\n' - ' | Attribute | Meaning ' + '| Attribute | Meaning ' '| |\n' - ' ' '|===========================|=================================|=============|\n' - ' | "__doc__" | The function’s documentation ' - '| Writable |\n' - ' | | string, or "None" if ' + '| "__doc__" | The function’s documentation | ' + 'Writable |\n' + '| | string, or "None" if ' '| |\n' - ' | | unavailable; not inherited by ' + '| | unavailable; not inherited by ' '| |\n' - ' | | subclasses. ' + '| | subclasses. ' '| |\n' - ' ' '+---------------------------+---------------------------------+-------------+\n' - ' | "__name__" | The function’s name. ' - '| Writable |\n' - ' ' + '| "__name__" | The function’s name. | ' + 'Writable |\n' '+---------------------------+---------------------------------+-------------+\n' - ' | "__qualname__" | The function’s *qualified ' - '| Writable |\n' - ' | | name*. New in version 3.3. ' + '| "__qualname__" | The function’s *qualified | ' + 'Writable |\n' + '| | name*. New in version 3.3. ' '| |\n' - ' ' '+---------------------------+---------------------------------+-------------+\n' - ' | "__module__" | The name of the module the ' - '| Writable |\n' - ' | | function was defined in, or ' + '| "__module__" | The name of the module the | ' + 'Writable |\n' + '| | function was defined in, or ' '| |\n' - ' | | "None" if unavailable. ' + '| | "None" if unavailable. ' '| |\n' - ' ' '+---------------------------+---------------------------------+-------------+\n' - ' | "__defaults__" | A tuple containing default ' - '| Writable |\n' - ' | | argument values for those ' + '| "__defaults__" | A tuple containing default | ' + 'Writable |\n' + '| | argument values for those ' '| |\n' - ' | | arguments that have defaults, ' + '| | arguments that have defaults, ' '| |\n' - ' | | or "None" if no arguments have ' + '| | or "None" if no arguments have ' '| |\n' - ' | | a default value. ' + '| | a default value. ' '| |\n' - ' ' '+---------------------------+---------------------------------+-------------+\n' - ' | "__code__" | The code object representing ' - '| Writable |\n' - ' | | the compiled function body. ' + '| "__code__" | The code object representing | ' + 'Writable |\n' + '| | the compiled function body. ' '| |\n' - ' ' '+---------------------------+---------------------------------+-------------+\n' - ' | "__globals__" | A reference to the dictionary ' - '| Read-only |\n' - ' | | that holds the function’s ' + '| "__globals__" | A reference to the dictionary | ' + 'Read-only |\n' + '| | that holds the function’s ' '| |\n' - ' | | global variables — the global ' + '| | global variables — the global ' '| |\n' - ' | | namespace of the module in ' + '| | namespace of the module in ' '| |\n' - ' | | which the function was defined. ' + '| | which the function was defined. ' '| |\n' - ' ' '+---------------------------+---------------------------------+-------------+\n' - ' | "__dict__" | The namespace supporting ' - '| Writable |\n' - ' | | arbitrary function attributes. ' + '| "__dict__" | The namespace supporting | ' + 'Writable |\n' + '| | arbitrary function attributes. ' '| |\n' - ' ' '+---------------------------+---------------------------------+-------------+\n' - ' | "__closure__" | "None" or a tuple of cells that ' - '| Read-only |\n' - ' | | contain bindings for the ' + '| "__closure__" | "None" or a tuple of cells that | ' + 'Read-only |\n' + '| | contain bindings for the ' '| |\n' - ' | | function’s free variables. See ' + '| | function’s free variables. See ' '| |\n' - ' | | below for information on the ' + '| | below for information on the ' '| |\n' - ' | | "cell_contents" attribute. ' + '| | "cell_contents" attribute. ' '| |\n' - ' ' '+---------------------------+---------------------------------+-------------+\n' - ' | "__annotations__" | A dict containing annotations ' - '| Writable |\n' - ' | | of parameters. The keys of the ' + '| "__annotations__" | A dict containing annotations | ' + 'Writable |\n' + '| | of parameters. The keys of the ' + '| |\n' + '| | dict are the parameter names, ' '| |\n' - ' | | dict are the parameter names, ' + '| | and "\'return\'" for the return ' '| |\n' - ' | | and "\'return\'" for the ' - 'return | |\n' - ' | | annotation, if provided. For ' + '| | annotation, if provided. For ' '| |\n' - ' | | more information on working ' + '| | more information on working ' '| |\n' - ' | | with this attribute, see ' + '| | with this attribute, see ' '| |\n' - ' | | Annotations Best Practices. ' + '| | Annotations Best Practices. ' '| |\n' - ' ' '+---------------------------+---------------------------------+-------------+\n' - ' | "__kwdefaults__" | A dict containing defaults for ' - '| Writable |\n' - ' | | keyword-only parameters. ' + '| "__kwdefaults__" | A dict containing defaults for | ' + 'Writable |\n' + '| | keyword-only parameters. ' '| |\n' - ' ' '+---------------------------+---------------------------------+-------------+\n' '\n' - ' Most of the attributes labelled “Writable” check the type of ' - 'the\n' - ' assigned value.\n' + 'Most of the attributes labelled “Writable” check the type of the\n' + 'assigned value.\n' '\n' - ' Function objects also support getting and setting arbitrary\n' - ' attributes, which can be used, for example, to attach ' - 'metadata\n' - ' to functions. Regular attribute dot-notation is used to get ' - 'and\n' - ' set such attributes. *Note that the current implementation ' - 'only\n' - ' supports function attributes on user-defined functions. ' - 'Function\n' - ' attributes on built-in functions may be supported in the\n' - ' future.*\n' - '\n' - ' A cell object has the attribute "cell_contents". This can be\n' - ' used to get the value of the cell, as well as set the value.\n' - '\n' - ' Additional information about a function’s definition can be\n' - ' retrieved from its code object; see the description of ' - 'internal\n' - ' types below. The "cell" type can be accessed in the "types"\n' - ' module.\n' - '\n' - ' Instance methods\n' - ' An instance method object combines a class, a class instance ' - 'and\n' - ' any callable object (normally a user-defined function).\n' - '\n' - ' Special read-only attributes: "__self__" is the class ' - 'instance\n' - ' object, "__func__" is the function object; "__doc__" is the\n' - ' method’s documentation (same as "__func__.__doc__"); ' - '"__name__"\n' - ' is the method name (same as "__func__.__name__"); ' - '"__module__"\n' - ' is the name of the module the method was defined in, or ' - '"None"\n' - ' if unavailable.\n' + 'Function objects also support getting and setting arbitrary\n' + 'attributes, which can be used, for example, to attach metadata to\n' + 'functions. Regular attribute dot-notation is used to get and set ' + 'such\n' + 'attributes. *Note that the current implementation only supports\n' + 'function attributes on user-defined functions. Function attributes ' + 'on\n' + 'built-in functions may be supported in the future.*\n' '\n' - ' Methods also support accessing (but not setting) the ' - 'arbitrary\n' - ' function attributes on the underlying function object.\n' + 'A cell object has the attribute "cell_contents". This can be used ' + 'to\n' + 'get the value of the cell, as well as set the value.\n' '\n' - ' User-defined method objects may be created when getting an\n' - ' attribute of a class (perhaps via an instance of that class), ' - 'if\n' - ' that attribute is a user-defined function object or a class\n' - ' method object.\n' - '\n' - ' When an instance method object is created by retrieving a ' - 'user-\n' - ' defined function object from a class via one of its ' - 'instances,\n' - ' its "__self__" attribute is the instance, and the method ' - 'object\n' - ' is said to be bound. The new method’s "__func__" attribute ' - 'is\n' - ' the original function object.\n' + 'Additional information about a function’s definition can be ' + 'retrieved\n' + 'from its code object; see the description of internal types below. ' + 'The\n' + '"cell" type can be accessed in the "types" module.\n' '\n' - ' When an instance method object is created by retrieving a ' - 'class\n' - ' method object from a class or instance, its "__self__" ' - 'attribute\n' - ' is the class itself, and its "__func__" attribute is the\n' - ' function object underlying the class method.\n' '\n' - ' When an instance method object is called, the underlying\n' - ' function ("__func__") is called, inserting the class ' - 'instance\n' - ' ("__self__") in front of the argument list. For instance, ' - 'when\n' - ' "C" is a class which contains a definition for a function ' - '"f()",\n' - ' and "x" is an instance of "C", calling "x.f(1)" is equivalent ' - 'to\n' - ' calling "C.f(x, 1)".\n' + 'Instance methods\n' + '----------------\n' + '\n' + 'An instance method object combines a class, a class instance and ' + 'any\n' + 'callable object (normally a user-defined function).\n' '\n' - ' When an instance method object is derived from a class ' + 'Special read-only attributes: "__self__" is the class instance ' + 'object,\n' + '"__func__" is the function object; "__doc__" is the method’s\n' + 'documentation (same as "__func__.__doc__"); "__name__" is the ' 'method\n' - ' object, the “class instance” stored in "__self__" will ' - 'actually\n' - ' be the class itself, so that calling either "x.f(1)" or ' - '"C.f(1)"\n' - ' is equivalent to calling "f(C,1)" where "f" is the ' - 'underlying\n' - ' function.\n' - '\n' - ' Note that the transformation from function object to ' - 'instance\n' - ' method object happens each time the attribute is retrieved ' - 'from\n' - ' the instance. In some cases, a fruitful optimization is to\n' - ' assign the attribute to a local variable and call that local\n' - ' variable. Also notice that this transformation only happens ' - 'for\n' - ' user-defined functions; other callable objects (and all non-\n' - ' callable objects) are retrieved without transformation. It ' - 'is\n' - ' also important to note that user-defined functions which are\n' - ' attributes of a class instance are not converted to bound\n' - ' methods; this *only* happens when the function is an ' + 'name (same as "__func__.__name__"); "__module__" is the name of ' + 'the\n' + 'module the method was defined in, or "None" if unavailable.\n' + '\n' + 'Methods also support accessing (but not setting) the arbitrary\n' + 'function attributes on the underlying function object.\n' + '\n' + 'User-defined method objects may be created when getting an ' + 'attribute\n' + 'of a class (perhaps via an instance of that class), if that ' 'attribute\n' - ' of the class.\n' + 'is a user-defined function object or a class method object.\n' '\n' - ' Generator functions\n' - ' A function or method which uses the "yield" statement (see\n' - ' section The yield statement) is called a *generator ' - 'function*.\n' - ' Such a function, when called, always returns an *iterator*\n' - ' object which can be used to execute the body of the ' - 'function:\n' - ' calling the iterator’s "iterator.__next__()" method will ' - 'cause\n' - ' the function to execute until it provides a value using the\n' - ' "yield" statement. When the function executes a "return"\n' - ' statement or falls off the end, a "StopIteration" exception ' - 'is\n' - ' raised and the iterator will have reached the end of the set ' + 'When an instance method object is created by retrieving a ' + 'user-defined\n' + 'function object from a class via one of its instances, its ' + '"__self__"\n' + 'attribute is the instance, and the method object is said to be ' + 'bound.\n' + 'The new method’s "__func__" attribute is the original function ' + 'object.\n' + '\n' + 'When an instance method object is created by retrieving a class ' + 'method\n' + 'object from a class or instance, its "__self__" attribute is the ' + 'class\n' + 'itself, and its "__func__" attribute is the function object ' + 'underlying\n' + 'the class method.\n' + '\n' + 'When an instance method object is called, the underlying function\n' + '("__func__") is called, inserting the class instance ("__self__") ' + 'in\n' + 'front of the argument list. For instance, when "C" is a class ' + 'which\n' + 'contains a definition for a function "f()", and "x" is an instance ' 'of\n' - ' values to be returned.\n' - '\n' - ' Coroutine functions\n' - ' A function or method which is defined using "async def" is\n' - ' called a *coroutine function*. Such a function, when ' - 'called,\n' - ' returns a *coroutine* object. It may contain "await"\n' - ' expressions, as well as "async with" and "async for" ' - 'statements.\n' - ' See also the Coroutine Objects section.\n' - '\n' - ' Asynchronous generator functions\n' - ' A function or method which is defined using "async def" and\n' - ' which uses the "yield" statement is called a *asynchronous\n' - ' generator function*. Such a function, when called, returns ' - 'an\n' - ' *asynchronous iterator* object which can be used in an ' - '"async\n' - ' for" statement to execute the body of the function.\n' + '"C", calling "x.f(1)" is equivalent to calling "C.f(x, 1)".\n' '\n' - ' Calling the asynchronous iterator’s "aiterator.__anext__" ' + 'When an instance method object is derived from a class method ' + 'object,\n' + 'the “class instance” stored in "__self__" will actually be the ' + 'class\n' + 'itself, so that calling either "x.f(1)" or "C.f(1)" is equivalent ' + 'to\n' + 'calling "f(C,1)" where "f" is the underlying function.\n' + '\n' + 'Note that the transformation from function object to instance ' 'method\n' - ' will return an *awaitable* which when awaited will execute ' + 'object happens each time the attribute is retrieved from the ' + 'instance.\n' + 'In some cases, a fruitful optimization is to assign the attribute ' + 'to a\n' + 'local variable and call that local variable. Also notice that this\n' + 'transformation only happens for user-defined functions; other ' + 'callable\n' + 'objects (and all non-callable objects) are retrieved without\n' + 'transformation. It is also important to note that user-defined\n' + 'functions which are attributes of a class instance are not ' + 'converted\n' + 'to bound methods; this *only* happens when the function is an\n' + 'attribute of the class.\n' + '\n' + '\n' + 'Generator functions\n' + '-------------------\n' + '\n' + 'A function or method which uses the "yield" statement (see section ' + 'The\n' + 'yield statement) is called a *generator function*. Such a ' + 'function,\n' + 'when called, always returns an *iterator* object which can be used ' + 'to\n' + 'execute the body of the function: calling the iterator’s\n' + '"iterator.__next__()" method will cause the function to execute ' 'until\n' - ' it provides a value using the "yield" expression. When the\n' - ' function executes an empty "return" statement or falls off ' + 'it provides a value using the "yield" statement. When the ' + 'function\n' + 'executes a "return" statement or falls off the end, a ' + '"StopIteration"\n' + 'exception is raised and the iterator will have reached the end of ' 'the\n' - ' end, a "StopAsyncIteration" exception is raised and the\n' - ' asynchronous iterator will have reached the end of the set ' - 'of\n' - ' values to be yielded.\n' + 'set of values to be returned.\n' '\n' - ' Built-in functions\n' - ' A built-in function object is a wrapper around a C function.\n' - ' Examples of built-in functions are "len()" and "math.sin()"\n' - ' ("math" is a standard built-in module). The number and type ' - 'of\n' - ' the arguments are determined by the C function. Special ' - 'read-\n' - ' only attributes: "__doc__" is the function’s documentation\n' - ' string, or "None" if unavailable; "__name__" is the ' - 'function’s\n' - ' name; "__self__" is set to "None" (but see the next item);\n' - ' "__module__" is the name of the module the function was ' - 'defined\n' - ' in or "None" if unavailable.\n' '\n' - ' Built-in methods\n' - ' This is really a different disguise of a built-in function, ' - 'this\n' - ' time containing an object passed to the C function as an\n' - ' implicit extra argument. An example of a built-in method is\n' - ' "alist.append()", assuming *alist* is a list object. In this\n' - ' case, the special read-only attribute "__self__" is set to ' + 'Coroutine functions\n' + '-------------------\n' + '\n' + 'A function or method which is defined using "async def" is called ' + 'a\n' + '*coroutine function*. Such a function, when called, returns a\n' + '*coroutine* object. It may contain "await" expressions, as well ' + 'as\n' + '"async with" and "async for" statements. See also the Coroutine\n' + 'Objects section.\n' + '\n' + '\n' + 'Asynchronous generator functions\n' + '--------------------------------\n' + '\n' + 'A function or method which is defined using "async def" and which ' + 'uses\n' + 'the "yield" statement is called a *asynchronous generator ' + 'function*.\n' + 'Such a function, when called, returns an *asynchronous iterator*\n' + 'object which can be used in an "async for" statement to execute ' 'the\n' - ' object denoted by *alist*.\n' + 'body of the function.\n' + '\n' + 'Calling the asynchronous iterator’s "aiterator.__anext__" method ' + 'will\n' + 'return an *awaitable* which when awaited will execute until it\n' + 'provides a value using the "yield" expression. When the function\n' + 'executes an empty "return" statement or falls off the end, a\n' + '"StopAsyncIteration" exception is raised and the asynchronous ' + 'iterator\n' + 'will have reached the end of the set of values to be yielded.\n' + '\n' + '\n' + 'Built-in functions\n' + '------------------\n' + '\n' + 'A built-in function object is a wrapper around a C function. ' + 'Examples\n' + 'of built-in functions are "len()" and "math.sin()" ("math" is a\n' + 'standard built-in module). The number and type of the arguments ' + 'are\n' + 'determined by the C function. Special read-only attributes: ' + '"__doc__"\n' + 'is the function’s documentation string, or "None" if unavailable;\n' + '"__name__" is the function’s name; "__self__" is set to "None" ' + '(but\n' + 'see the next item); "__module__" is the name of the module the\n' + 'function was defined in or "None" if unavailable.\n' + '\n' + '\n' + 'Built-in methods\n' + '----------------\n' + '\n' + 'This is really a different disguise of a built-in function, this ' + 'time\n' + 'containing an object passed to the C function as an implicit extra\n' + 'argument. An example of a built-in method is "alist.append()",\n' + 'assuming *alist* is a list object. In this case, the special ' + 'read-only\n' + 'attribute "__self__" is set to the object denoted by *alist*.\n' + '\n' + '\n' + 'Classes\n' + '-------\n' + '\n' + 'Classes are callable. These objects normally act as factories for ' + 'new\n' + 'instances of themselves, but variations are possible for class ' + 'types\n' + 'that override "__new__()". The arguments of the call are passed ' + 'to\n' + '"__new__()" and, in the typical case, to "__init__()" to ' + 'initialize\n' + 'the new instance.\n' '\n' - ' Classes\n' - ' Classes are callable. These objects normally act as ' - 'factories\n' - ' for new instances of themselves, but variations are possible ' - 'for\n' - ' class types that override "__new__()". The arguments of the\n' - ' call are passed to "__new__()" and, in the typical case, to\n' - ' "__init__()" to initialize the new instance.\n' '\n' - ' Class Instances\n' - ' Instances of arbitrary classes can be made callable by ' - 'defining\n' - ' a "__call__()" method in their class.\n' + 'Class Instances\n' + '---------------\n' + '\n' + 'Instances of arbitrary classes can be made callable by defining a\n' + '"__call__()" method in their class.\n' + '\n' '\n' 'Modules\n' - ' Modules are a basic organizational unit of Python code, and are\n' - ' created by the import system as invoked either by the "import"\n' - ' statement, or by calling functions such as\n' - ' "importlib.import_module()" and built-in "__import__()". A ' - 'module\n' - ' object has a namespace implemented by a dictionary object (this ' - 'is\n' - ' the dictionary referenced by the "__globals__" attribute of\n' - ' functions defined in the module). Attribute references are\n' - ' translated to lookups in this dictionary, e.g., "m.x" is ' - 'equivalent\n' - ' to "m.__dict__["x"]". A module object does not contain the code\n' - ' object used to initialize the module (since it isn’t needed ' - 'once\n' - ' the initialization is done).\n' + '=======\n' + '\n' + 'Modules are a basic organizational unit of Python code, and are\n' + 'created by the import system as invoked either by the "import"\n' + 'statement, or by calling functions such as ' + '"importlib.import_module()"\n' + 'and built-in "__import__()". A module object has a namespace\n' + 'implemented by a dictionary object (this is the dictionary ' + 'referenced\n' + 'by the "__globals__" attribute of functions defined in the ' + 'module).\n' + 'Attribute references are translated to lookups in this dictionary,\n' + 'e.g., "m.x" is equivalent to "m.__dict__["x"]". A module object ' + 'does\n' + 'not contain the code object used to initialize the module (since ' + 'it\n' + 'isn’t needed once the initialization is done).\n' + '\n' + 'Attribute assignment updates the module’s namespace dictionary, ' + 'e.g.,\n' + '"m.x = 1" is equivalent to "m.__dict__["x"] = 1".\n' '\n' - ' Attribute assignment updates the module’s namespace dictionary,\n' - ' e.g., "m.x = 1" is equivalent to "m.__dict__["x"] = 1".\n' + 'Predefined (writable) attributes:\n' '\n' - ' Predefined (writable) attributes:\n' + ' "__name__"\n' + ' The module’s name.\n' '\n' - ' "__name__"\n' - ' The module’s name.\n' + ' "__doc__"\n' + ' The module’s documentation string, or "None" if unavailable.\n' '\n' - ' "__doc__"\n' - ' The module’s documentation string, or "None" if ' - 'unavailable.\n' + ' "__file__"\n' + ' The pathname of the file from which the module was loaded, if ' + 'it\n' + ' was loaded from a file. The "__file__" attribute may be ' + 'missing\n' + ' for certain types of modules, such as C modules that are\n' + ' statically linked into the interpreter. For extension ' + 'modules\n' + ' loaded dynamically from a shared library, it’s the pathname ' + 'of\n' + ' the shared library file.\n' '\n' - ' "__file__"\n' - ' The pathname of the file from which the module was loaded, ' - 'if\n' - ' it was loaded from a file. The "__file__" attribute may ' - 'be\n' - ' missing for certain types of modules, such as C modules ' - 'that\n' - ' are statically linked into the interpreter. For ' - 'extension\n' - ' modules loaded dynamically from a shared library, it’s ' - 'the\n' - ' pathname of the shared library file.\n' - '\n' - ' "__annotations__"\n' - ' A dictionary containing *variable annotations* collected\n' - ' during module body execution. For best practices on ' - 'working\n' - ' with "__annotations__", please see Annotations Best\n' - ' Practices.\n' - '\n' - ' Special read-only attribute: "__dict__" is the module’s ' - 'namespace\n' - ' as a dictionary object.\n' - '\n' - ' **CPython implementation detail:** Because of the way CPython\n' - ' clears module dictionaries, the module dictionary will be ' - 'cleared\n' - ' when the module falls out of scope even if the dictionary still ' - 'has\n' - ' live references. To avoid this, copy the dictionary or keep ' + ' "__annotations__"\n' + ' A dictionary containing *variable annotations* collected ' + 'during\n' + ' module body execution. For best practices on working with\n' + ' "__annotations__", please see Annotations Best Practices.\n' + '\n' + 'Special read-only attribute: "__dict__" is the module’s namespace ' + 'as a\n' + 'dictionary object.\n' + '\n' + '**CPython implementation detail:** Because of the way CPython ' + 'clears\n' + 'module dictionaries, the module dictionary will be cleared when ' 'the\n' - ' module around while using its dictionary directly.\n' + 'module falls out of scope even if the dictionary still has live\n' + 'references. To avoid this, copy the dictionary or keep the module\n' + 'around while using its dictionary directly.\n' + '\n' '\n' 'Custom classes\n' - ' Custom class types are typically created by class definitions ' - '(see\n' - ' section Class definitions). A class has a namespace implemented ' - 'by\n' - ' a dictionary object. Class attribute references are translated ' - 'to\n' - ' lookups in this dictionary, e.g., "C.x" is translated to\n' - ' "C.__dict__["x"]" (although there are a number of hooks which ' + '==============\n' + '\n' + 'Custom class types are typically created by class definitions (see\n' + 'section Class definitions). A class has a namespace implemented by ' + 'a\n' + 'dictionary object. Class attribute references are translated to\n' + 'lookups in this dictionary, e.g., "C.x" is translated to\n' + '"C.__dict__["x"]" (although there are a number of hooks which ' 'allow\n' - ' for other means of locating attributes). When the attribute name ' + 'for other means of locating attributes). When the attribute name ' 'is\n' - ' not found there, the attribute search continues in the base\n' - ' classes. This search of the base classes uses the C3 method\n' - ' resolution order which behaves correctly even in the presence ' - 'of\n' - ' ‘diamond’ inheritance structures where there are multiple\n' - ' inheritance paths leading back to a common ancestor. Additional\n' - ' details on the C3 MRO used by Python can be found in the\n' - ' documentation accompanying the 2.3 release at\n' - ' https://www.python.org/download/releases/2.3/mro/.\n' - '\n' - ' When a class attribute reference (for class "C", say) would ' - 'yield a\n' - ' class method object, it is transformed into an instance method\n' - ' object whose "__self__" attribute is "C". When it would yield ' + 'not found there, the attribute search continues in the base ' + 'classes.\n' + 'This search of the base classes uses the C3 method resolution ' + 'order\n' + 'which behaves correctly even in the presence of ‘diamond’ ' + 'inheritance\n' + 'structures where there are multiple inheritance paths leading back ' + 'to\n' + 'a common ancestor. Additional details on the C3 MRO used by Python ' + 'can\n' + 'be found in the documentation accompanying the 2.3 release at\n' + 'https://www.python.org/download/releases/2.3/mro/.\n' + '\n' + 'When a class attribute reference (for class "C", say) would yield ' 'a\n' - ' static method object, it is transformed into the object wrapped ' - 'by\n' - ' the static method object. See section Implementing Descriptors ' - 'for\n' - ' another way in which attributes retrieved from a class may ' - 'differ\n' - ' from those actually contained in its "__dict__".\n' + 'class method object, it is transformed into an instance method ' + 'object\n' + 'whose "__self__" attribute is "C". When it would yield a static\n' + 'method object, it is transformed into the object wrapped by the ' + 'static\n' + 'method object. See section Implementing Descriptors for another way ' + 'in\n' + 'which attributes retrieved from a class may differ from those ' + 'actually\n' + 'contained in its "__dict__".\n' '\n' - ' Class attribute assignments update the class’s dictionary, ' - 'never\n' - ' the dictionary of a base class.\n' + 'Class attribute assignments update the class’s dictionary, never ' + 'the\n' + 'dictionary of a base class.\n' + '\n' + 'A class object can be called (see above) to yield a class instance\n' + '(see below).\n' '\n' - ' A class object can be called (see above) to yield a class ' - 'instance\n' - ' (see below).\n' + 'Special attributes:\n' '\n' - ' Special attributes:\n' + ' "__name__"\n' + ' The class name.\n' '\n' - ' "__name__"\n' - ' The class name.\n' + ' "__module__"\n' + ' The name of the module in which the class was defined.\n' '\n' - ' "__module__"\n' - ' The name of the module in which the class was defined.\n' + ' "__dict__"\n' + ' The dictionary containing the class’s namespace.\n' '\n' - ' "__dict__"\n' - ' The dictionary containing the class’s namespace.\n' + ' "__bases__"\n' + ' A tuple containing the base classes, in the order of their\n' + ' occurrence in the base class list.\n' '\n' - ' "__bases__"\n' - ' A tuple containing the base classes, in the order of ' - 'their\n' - ' occurrence in the base class list.\n' + ' "__doc__"\n' + ' The class’s documentation string, or "None" if undefined.\n' '\n' - ' "__doc__"\n' - ' The class’s documentation string, or "None" if undefined.\n' + ' "__annotations__"\n' + ' A dictionary containing *variable annotations* collected ' + 'during\n' + ' class body execution. For best practices on working with\n' + ' "__annotations__", please see Annotations Best Practices.\n' '\n' - ' "__annotations__"\n' - ' A dictionary containing *variable annotations* collected\n' - ' during class body execution. For best practices on ' - 'working\n' - ' with "__annotations__", please see Annotations Best\n' - ' Practices.\n' '\n' 'Class instances\n' - ' A class instance is created by calling a class object (see ' - 'above).\n' - ' A class instance has a namespace implemented as a dictionary ' - 'which\n' - ' is the first place in which attribute references are searched.\n' - ' When an attribute is not found there, and the instance’s class ' - 'has\n' - ' an attribute by that name, the search continues with the class\n' - ' attributes. If a class attribute is found that is a ' - 'user-defined\n' - ' function object, it is transformed into an instance method ' - 'object\n' - ' whose "__self__" attribute is the instance. Static method and\n' - ' class method objects are also transformed; see above under\n' - ' “Classes”. See section Implementing Descriptors for another way ' - 'in\n' - ' which attributes of a class retrieved via its instances may ' - 'differ\n' - ' from the objects actually stored in the class’s "__dict__". If ' - 'no\n' - ' class attribute is found, and the object’s class has a\n' - ' "__getattr__()" method, that is called to satisfy the lookup.\n' + '===============\n' '\n' - ' Attribute assignments and deletions update the instance’s\n' - ' dictionary, never a class’s dictionary. If the class has a\n' - ' "__setattr__()" or "__delattr__()" method, this is called ' - 'instead\n' - ' of updating the instance dictionary directly.\n' + 'A class instance is created by calling a class object (see above). ' + 'A\n' + 'class instance has a namespace implemented as a dictionary which ' + 'is\n' + 'the first place in which attribute references are searched. When ' + 'an\n' + 'attribute is not found there, and the instance’s class has an\n' + 'attribute by that name, the search continues with the class\n' + 'attributes. If a class attribute is found that is a user-defined\n' + 'function object, it is transformed into an instance method object\n' + 'whose "__self__" attribute is the instance. Static method and ' + 'class\n' + 'method objects are also transformed; see above under “Classes”. ' + 'See\n' + 'section Implementing Descriptors for another way in which ' + 'attributes\n' + 'of a class retrieved via its instances may differ from the objects\n' + 'actually stored in the class’s "__dict__". If no class attribute ' + 'is\n' + 'found, and the object’s class has a "__getattr__()" method, that ' + 'is\n' + 'called to satisfy the lookup.\n' '\n' - ' Class instances can pretend to be numbers, sequences, or ' - 'mappings\n' - ' if they have methods with certain special names. See section\n' - ' Special method names.\n' + 'Attribute assignments and deletions update the instance’s ' + 'dictionary,\n' + 'never a class’s dictionary. If the class has a "__setattr__()" or\n' + '"__delattr__()" method, this is called instead of updating the\n' + 'instance dictionary directly.\n' + '\n' + 'Class instances can pretend to be numbers, sequences, or mappings ' + 'if\n' + 'they have methods with certain special names. See section Special\n' + 'method names.\n' + '\n' + 'Special attributes: "__dict__" is the attribute dictionary;\n' + '"__class__" is the instance’s class.\n' '\n' - ' Special attributes: "__dict__" is the attribute dictionary;\n' - ' "__class__" is the instance’s class.\n' '\n' 'I/O objects (also known as file objects)\n' - ' A *file object* represents an open file. Various shortcuts are\n' - ' available to create file objects: the "open()" built-in ' - 'function,\n' - ' and also "os.popen()", "os.fdopen()", and the "makefile()" ' - 'method\n' - ' of socket objects (and perhaps by other functions or methods\n' - ' provided by extension modules).\n' + '========================================\n' + '\n' + 'A *file object* represents an open file. Various shortcuts are\n' + 'available to create file objects: the "open()" built-in function, ' + 'and\n' + 'also "os.popen()", "os.fdopen()", and the "makefile()" method of\n' + 'socket objects (and perhaps by other functions or methods provided ' + 'by\n' + 'extension modules).\n' + '\n' + 'The objects "sys.stdin", "sys.stdout" and "sys.stderr" are ' + 'initialized\n' + 'to file objects corresponding to the interpreter’s standard input,\n' + 'output and error streams; they are all open in text mode and ' + 'therefore\n' + 'follow the interface defined by the "io.TextIOBase" abstract ' + 'class.\n' '\n' - ' The objects "sys.stdin", "sys.stdout" and "sys.stderr" are\n' - ' initialized to file objects corresponding to the interpreter’s\n' - ' standard input, output and error streams; they are all open in ' - 'text\n' - ' mode and therefore follow the interface defined by the\n' - ' "io.TextIOBase" abstract class.\n' '\n' 'Internal types\n' - ' A few types used internally by the interpreter are exposed to ' - 'the\n' - ' user. Their definitions may change with future versions of the\n' - ' interpreter, but they are mentioned here for completeness.\n' - '\n' - ' Code objects\n' - ' Code objects represent *byte-compiled* executable Python ' - 'code,\n' - ' or *bytecode*. The difference between a code object and a\n' - ' function object is that the function object contains an ' - 'explicit\n' - ' reference to the function’s globals (the module in which it ' - 'was\n' - ' defined), while a code object contains no context; also the\n' - ' default argument values are stored in the function object, ' - 'not\n' - ' in the code object (because they represent values calculated ' - 'at\n' - ' run-time). Unlike function objects, code objects are ' - 'immutable\n' - ' and contain no references (directly or indirectly) to ' - 'mutable\n' - ' objects.\n' - '\n' - ' Special read-only attributes: "co_name" gives the function ' - 'name;\n' - ' "co_qualname" gives the fully qualified function name;\n' - ' "co_argcount" is the total number of positional arguments\n' - ' (including positional-only arguments and arguments with ' - 'default\n' - ' values); "co_posonlyargcount" is the number of ' - 'positional-only\n' - ' arguments (including arguments with default values);\n' - ' "co_kwonlyargcount" is the number of keyword-only arguments\n' - ' (including arguments with default values); "co_nlocals" is ' - 'the\n' - ' number of local variables used by the function (including\n' - ' arguments); "co_varnames" is a tuple containing the names of ' - 'the\n' - ' local variables (starting with the argument names);\n' - ' "co_cellvars" is a tuple containing the names of local ' - 'variables\n' - ' that are referenced by nested functions; "co_freevars" is a\n' - ' tuple containing the names of free variables; "co_code" is a\n' - ' string representing the sequence of bytecode instructions;\n' - ' "co_consts" is a tuple containing the literals used by the\n' - ' bytecode; "co_names" is a tuple containing the names used by ' - 'the\n' - ' bytecode; "co_filename" is the filename from which the code ' - 'was\n' - ' compiled; "co_firstlineno" is the first line number of the\n' - ' function; "co_lnotab" is a string encoding the mapping from\n' - ' bytecode offsets to line numbers (for details see the source\n' - ' code of the interpreter); "co_stacksize" is the required ' - 'stack\n' - ' size; "co_flags" is an integer encoding a number of flags ' - 'for\n' - ' the interpreter.\n' + '==============\n' + '\n' + 'A few types used internally by the interpreter are exposed to the\n' + 'user. Their definitions may change with future versions of the\n' + 'interpreter, but they are mentioned here for completeness.\n' '\n' - ' The following flag bits are defined for "co_flags": bit ' - '"0x04"\n' - ' is set if the function uses the "*arguments" syntax to accept ' - 'an\n' - ' arbitrary number of positional arguments; bit "0x08" is set ' - 'if\n' - ' the function uses the "**keywords" syntax to accept ' - 'arbitrary\n' - ' keyword arguments; bit "0x20" is set if the function is a\n' - ' generator.\n' '\n' - ' Future feature declarations ("from __future__ import ' - 'division")\n' - ' also use bits in "co_flags" to indicate whether a code ' + 'Code objects\n' + '------------\n' + '\n' + 'Code objects represent *byte-compiled* executable Python code, or\n' + '*bytecode*. The difference between a code object and a function ' 'object\n' - ' was compiled with a particular feature enabled: bit "0x2000" ' + 'is that the function object contains an explicit reference to the\n' + 'function’s globals (the module in which it was defined), while a ' + 'code\n' + 'object contains no context; also the default argument values are\n' + 'stored in the function object, not in the code object (because ' + 'they\n' + 'represent values calculated at run-time). Unlike function ' + 'objects,\n' + 'code objects are immutable and contain no references (directly or\n' + 'indirectly) to mutable objects.\n' + '\n' + 'Special read-only attributes: "co_name" gives the function name;\n' + '"co_qualname" gives the fully qualified function name; ' + '"co_argcount"\n' + 'is the total number of positional arguments (including ' + 'positional-only\n' + 'arguments and arguments with default values); "co_posonlyargcount" ' 'is\n' - ' set if the function was compiled with future division ' - 'enabled;\n' - ' bits "0x10" and "0x1000" were used in earlier versions of\n' - ' Python.\n' + 'the number of positional-only arguments (including arguments with\n' + 'default values); "co_kwonlyargcount" is the number of keyword-only\n' + 'arguments (including arguments with default values); "co_nlocals" ' + 'is\n' + 'the number of local variables used by the function (including\n' + 'arguments); "co_varnames" is a tuple containing the names of the ' + 'local\n' + 'variables (starting with the argument names); "co_cellvars" is a ' + 'tuple\n' + 'containing the names of local variables that are referenced by ' + 'nested\n' + 'functions; "co_freevars" is a tuple containing the names of free\n' + 'variables; "co_code" is a string representing the sequence of ' + 'bytecode\n' + 'instructions; "co_consts" is a tuple containing the literals used ' + 'by\n' + 'the bytecode; "co_names" is a tuple containing the names used by ' + 'the\n' + 'bytecode; "co_filename" is the filename from which the code was\n' + 'compiled; "co_firstlineno" is the first line number of the ' + 'function;\n' + '"co_lnotab" is a string encoding the mapping from bytecode offsets ' + 'to\n' + 'line numbers (for details see the source code of the interpreter);\n' + '"co_stacksize" is the required stack size; "co_flags" is an ' + 'integer\n' + 'encoding a number of flags for the interpreter.\n' '\n' - ' Other bits in "co_flags" are reserved for internal use.\n' + 'The following flag bits are defined for "co_flags": bit "0x04" is ' + 'set\n' + 'if the function uses the "*arguments" syntax to accept an ' + 'arbitrary\n' + 'number of positional arguments; bit "0x08" is set if the function ' + 'uses\n' + 'the "**keywords" syntax to accept arbitrary keyword arguments; bit\n' + '"0x20" is set if the function is a generator.\n' + '\n' + 'Future feature declarations ("from __future__ import division") ' + 'also\n' + 'use bits in "co_flags" to indicate whether a code object was ' + 'compiled\n' + 'with a particular feature enabled: bit "0x2000" is set if the ' + 'function\n' + 'was compiled with future division enabled; bits "0x10" and ' + '"0x1000"\n' + 'were used in earlier versions of Python.\n' '\n' - ' If a code object represents a function, the first item in\n' - ' "co_consts" is the documentation string of the function, or\n' - ' "None" if undefined.\n' + 'Other bits in "co_flags" are reserved for internal use.\n' '\n' - ' codeobject.co_positions()\n' + 'If a code object represents a function, the first item in ' + '"co_consts"\n' + 'is the documentation string of the function, or "None" if ' + 'undefined.\n' '\n' - ' Returns an iterable over the source code positions of ' - 'each\n' - ' bytecode instruction in the code object.\n' + 'codeobject.co_positions()\n' '\n' - ' The iterator returns tuples containing the "(start_line,\n' - ' end_line, start_column, end_column)". The *i-th* tuple\n' - ' corresponds to the position of the source code that ' - 'compiled\n' - ' to the *i-th* instruction. Column information is ' - '0-indexed\n' - ' utf-8 byte offsets on the given source line.\n' + ' Returns an iterable over the source code positions of each ' + 'bytecode\n' + ' instruction in the code object.\n' '\n' - ' This positional information can be missing. A ' - 'non-exhaustive\n' - ' lists of cases where this may happen:\n' + ' The iterator returns tuples containing the "(start_line, ' + 'end_line,\n' + ' start_column, end_column)". The *i-th* tuple corresponds to the\n' + ' position of the source code that compiled to the *i-th*\n' + ' instruction. Column information is 0-indexed utf-8 byte offsets ' + 'on\n' + ' the given source line.\n' '\n' - ' * Running the interpreter with "-X" "no_debug_ranges".\n' + ' This positional information can be missing. A non-exhaustive ' + 'lists\n' + ' of cases where this may happen:\n' '\n' - ' * Loading a pyc file compiled while using "-X"\n' - ' "no_debug_ranges".\n' + ' * Running the interpreter with "-X" "no_debug_ranges".\n' '\n' - ' * Position tuples corresponding to artificial ' - 'instructions.\n' + ' * Loading a pyc file compiled while using "-X" ' + '"no_debug_ranges".\n' '\n' - ' * Line and column numbers that can’t be represented due ' - 'to\n' - ' implementation specific limitations.\n' + ' * Position tuples corresponding to artificial instructions.\n' '\n' - ' When this occurs, some or all of the tuple elements can ' - 'be\n' - ' "None".\n' + ' * Line and column numbers that can’t be represented due to\n' + ' implementation specific limitations.\n' '\n' - ' New in version 3.11.\n' + ' When this occurs, some or all of the tuple elements can be ' + '"None".\n' '\n' - ' Note:\n' + ' New in version 3.11.\n' '\n' - ' This feature requires storing column positions in code\n' - ' objects which may result in a small increase of disk ' - 'usage\n' - ' of compiled Python files or interpreter memory usage. ' - 'To\n' - ' avoid storing the extra information and/or deactivate\n' - ' printing the extra traceback information, the "-X"\n' - ' "no_debug_ranges" command line flag or the\n' - ' "PYTHONNODEBUGRANGES" environment variable can be used.\n' + ' Note:\n' '\n' - ' Frame objects\n' - ' Frame objects represent execution frames. They may occur in\n' - ' traceback objects (see below), and are also passed to ' - 'registered\n' - ' trace functions.\n' + ' This feature requires storing column positions in code ' + 'objects\n' + ' which may result in a small increase of disk usage of ' + 'compiled\n' + ' Python files or interpreter memory usage. To avoid storing ' + 'the\n' + ' extra information and/or deactivate printing the extra ' + 'traceback\n' + ' information, the "-X" "no_debug_ranges" command line flag or ' + 'the\n' + ' "PYTHONNODEBUGRANGES" environment variable can be used.\n' + '\n' + '\n' + 'Frame objects\n' + '-------------\n' + '\n' + 'Frame objects represent execution frames. They may occur in ' + 'traceback\n' + 'objects (see below), and are also passed to registered trace\n' + 'functions.\n' + '\n' + 'Special read-only attributes: "f_back" is to the previous stack ' + 'frame\n' + '(towards the caller), or "None" if this is the bottom stack frame;\n' + '"f_code" is the code object being executed in this frame; ' + '"f_locals"\n' + 'is the dictionary used to look up local variables; "f_globals" is ' + 'used\n' + 'for global variables; "f_builtins" is used for built-in ' + '(intrinsic)\n' + 'names; "f_lasti" gives the precise instruction (this is an index ' + 'into\n' + 'the bytecode string of the code object).\n' + '\n' + 'Accessing "f_code" raises an auditing event "object.__getattr__" ' + 'with\n' + 'arguments "obj" and ""f_code"".\n' + '\n' + 'Special writable attributes: "f_trace", if not "None", is a ' + 'function\n' + 'called for various events during code execution (this is used by ' + 'the\n' + 'debugger). Normally an event is triggered for each new source line ' + '-\n' + 'this can be disabled by setting "f_trace_lines" to "False".\n' '\n' - ' Special read-only attributes: "f_back" is to the previous ' - 'stack\n' - ' frame (towards the caller), or "None" if this is the bottom\n' - ' stack frame; "f_code" is the code object being executed in ' + 'Implementations *may* allow per-opcode events to be requested by\n' + 'setting "f_trace_opcodes" to "True". Note that this may lead to\n' + 'undefined interpreter behaviour if exceptions raised by the trace\n' + 'function escape to the function being traced.\n' + '\n' + '"f_lineno" is the current line number of the frame — writing to ' 'this\n' - ' frame; "f_locals" is the dictionary used to look up local\n' - ' variables; "f_globals" is used for global variables;\n' - ' "f_builtins" is used for built-in (intrinsic) names; ' - '"f_lasti"\n' - ' gives the precise instruction (this is an index into the\n' - ' bytecode string of the code object).\n' - '\n' - ' Accessing "f_code" raises an auditing event ' - '"object.__getattr__"\n' - ' with arguments "obj" and ""f_code"".\n' - '\n' - ' Special writable attributes: "f_trace", if not "None", is a\n' - ' function called for various events during code execution ' - '(this\n' - ' is used by the debugger). Normally an event is triggered for\n' - ' each new source line - this can be disabled by setting\n' - ' "f_trace_lines" to "False".\n' - '\n' - ' Implementations *may* allow per-opcode events to be requested ' - 'by\n' - ' setting "f_trace_opcodes" to "True". Note that this may lead ' - 'to\n' - ' undefined interpreter behaviour if exceptions raised by the\n' - ' trace function escape to the function being traced.\n' + 'from within a trace function jumps to the given line (only for the\n' + 'bottom-most frame). A debugger can implement a Jump command (aka ' + 'Set\n' + 'Next Statement) by writing to f_lineno.\n' '\n' - ' "f_lineno" is the current line number of the frame — writing ' - 'to\n' - ' this from within a trace function jumps to the given line ' - '(only\n' - ' for the bottom-most frame). A debugger can implement a Jump\n' - ' command (aka Set Next Statement) by writing to f_lineno.\n' + 'Frame objects support one method:\n' '\n' - ' Frame objects support one method:\n' + 'frame.clear()\n' '\n' - ' frame.clear()\n' + ' This method clears all references to local variables held by ' + 'the\n' + ' frame. Also, if the frame belonged to a generator, the ' + 'generator\n' + ' is finalized. This helps break reference cycles involving ' + 'frame\n' + ' objects (for example when catching an exception and storing its\n' + ' traceback for later use).\n' '\n' - ' This method clears all references to local variables held ' - 'by\n' - ' the frame. Also, if the frame belonged to a generator, ' + ' "RuntimeError" is raised if the frame is currently executing.\n' + '\n' + ' New in version 3.4.\n' + '\n' + '\n' + 'Traceback objects\n' + '-----------------\n' + '\n' + 'Traceback objects represent a stack trace of an exception. A\n' + 'traceback object is implicitly created when an exception occurs, ' + 'and\n' + 'may also be explicitly created by calling "types.TracebackType".\n' + '\n' + 'For implicitly created tracebacks, when the search for an ' + 'exception\n' + 'handler unwinds the execution stack, at each unwound level a ' + 'traceback\n' + 'object is inserted in front of the current traceback. When an\n' + 'exception handler is entered, the stack trace is made available to ' 'the\n' - ' generator is finalized. This helps break reference ' - 'cycles\n' - ' involving frame objects (for example when catching an\n' - ' exception and storing its traceback for later use).\n' + 'program. (See section The try statement.) It is accessible as the\n' + 'third item of the tuple returned by "sys.exc_info()", and as the\n' + '"__traceback__" attribute of the caught exception.\n' '\n' - ' "RuntimeError" is raised if the frame is currently ' - 'executing.\n' + 'When the program contains no suitable handler, the stack trace is\n' + 'written (nicely formatted) to the standard error stream; if the\n' + 'interpreter is interactive, it is also made available to the user ' + 'as\n' + '"sys.last_traceback".\n' '\n' - ' New in version 3.4.\n' + 'For explicitly created tracebacks, it is up to the creator of the\n' + 'traceback to determine how the "tb_next" attributes should be ' + 'linked\n' + 'to form a full stack trace.\n' '\n' - ' Traceback objects\n' - ' Traceback objects represent a stack trace of an exception. ' - 'A\n' - ' traceback object is implicitly created when an exception ' - 'occurs,\n' - ' and may also be explicitly created by calling\n' - ' "types.TracebackType".\n' - '\n' - ' For implicitly created tracebacks, when the search for an\n' - ' exception handler unwinds the execution stack, at each ' - 'unwound\n' - ' level a traceback object is inserted in front of the current\n' - ' traceback. When an exception handler is entered, the stack\n' - ' trace is made available to the program. (See section The try\n' - ' statement.) It is accessible as the third item of the tuple\n' - ' returned by "sys.exc_info()", and as the "__traceback__"\n' - ' attribute of the caught exception.\n' - '\n' - ' When the program contains no suitable handler, the stack ' - 'trace\n' - ' is written (nicely formatted) to the standard error stream; ' - 'if\n' - ' the interpreter is interactive, it is also made available to ' + 'Special read-only attributes: "tb_frame" points to the execution ' + 'frame\n' + 'of the current level; "tb_lineno" gives the line number where the\n' + 'exception occurred; "tb_lasti" indicates the precise instruction. ' + 'The\n' + 'line number and last instruction in the traceback may differ from ' 'the\n' - ' user as "sys.last_traceback".\n' + 'line number of its frame object if the exception occurred in a ' + '"try"\n' + 'statement with no matching except clause or with a finally clause.\n' '\n' - ' For explicitly created tracebacks, it is up to the creator ' - 'of\n' - ' the traceback to determine how the "tb_next" attributes ' - 'should\n' - ' be linked to form a full stack trace.\n' - '\n' - ' Special read-only attributes: "tb_frame" points to the ' - 'execution\n' - ' frame of the current level; "tb_lineno" gives the line ' - 'number\n' - ' where the exception occurred; "tb_lasti" indicates the ' - 'precise\n' - ' instruction. The line number and last instruction in the\n' - ' traceback may differ from the line number of its frame object ' + 'Accessing "tb_frame" raises an auditing event "object.__getattr__"\n' + 'with arguments "obj" and ""tb_frame"".\n' + '\n' + 'Special writable attribute: "tb_next" is the next level in the ' + 'stack\n' + 'trace (towards the frame where the exception occurred), or "None" ' 'if\n' - ' the exception occurred in a "try" statement with no matching\n' - ' except clause or with a finally clause.\n' + 'there is no next level.\n' '\n' - ' Accessing "tb_frame" raises an auditing event\n' - ' "object.__getattr__" with arguments "obj" and ""tb_frame"".\n' + 'Changed in version 3.7: Traceback objects can now be explicitly\n' + 'instantiated from Python code, and the "tb_next" attribute of ' + 'existing\n' + 'instances can be updated.\n' '\n' - ' Special writable attribute: "tb_next" is the next level in ' - 'the\n' - ' stack trace (towards the frame where the exception occurred), ' - 'or\n' - ' "None" if there is no next level.\n' '\n' - ' Changed in version 3.7: Traceback objects can now be ' - 'explicitly\n' - ' instantiated from Python code, and the "tb_next" attribute ' - 'of\n' - ' existing instances can be updated.\n' + 'Slice objects\n' + '-------------\n' '\n' - ' Slice objects\n' - ' Slice objects are used to represent slices for ' - '"__getitem__()"\n' - ' methods. They are also created by the built-in "slice()"\n' - ' function.\n' + 'Slice objects are used to represent slices for "__getitem__()"\n' + 'methods. They are also created by the built-in "slice()" ' + 'function.\n' '\n' - ' Special read-only attributes: "start" is the lower bound; ' - '"stop"\n' - ' is the upper bound; "step" is the step value; each is "None" ' - 'if\n' - ' omitted. These attributes can have any type.\n' + 'Special read-only attributes: "start" is the lower bound; "stop" ' + 'is\n' + 'the upper bound; "step" is the step value; each is "None" if ' + 'omitted.\n' + 'These attributes can have any type.\n' '\n' - ' Slice objects support one method:\n' + 'Slice objects support one method:\n' '\n' - ' slice.indices(self, length)\n' + 'slice.indices(self, length)\n' '\n' - ' This method takes a single integer argument *length* and\n' - ' computes information about the slice that the slice ' - 'object\n' - ' would describe if applied to a sequence of *length* ' - 'items.\n' - ' It returns a tuple of three integers; respectively these ' - 'are\n' - ' the *start* and *stop* indices and the *step* or stride\n' - ' length of the slice. Missing or out-of-bounds indices are\n' - ' handled in a manner consistent with regular slices.\n' - '\n' - ' Static method objects\n' - ' Static method objects provide a way of defeating the\n' - ' transformation of function objects to method objects ' - 'described\n' - ' above. A static method object is a wrapper around any other\n' - ' object, usually a user-defined method object. When a static\n' - ' method object is retrieved from a class or a class instance, ' - 'the\n' - ' object actually returned is the wrapped object, which is not\n' - ' subject to any further transformation. Static method objects ' - 'are\n' - ' also callable. Static method objects are created by the ' - 'built-in\n' - ' "staticmethod()" constructor.\n' + ' This method takes a single integer argument *length* and ' + 'computes\n' + ' information about the slice that the slice object would describe ' + 'if\n' + ' applied to a sequence of *length* items. It returns a tuple of\n' + ' three integers; respectively these are the *start* and *stop*\n' + ' indices and the *step* or stride length of the slice. Missing ' + 'or\n' + ' out-of-bounds indices are handled in a manner consistent with\n' + ' regular slices.\n' '\n' - ' Class method objects\n' - ' A class method object, like a static method object, is a ' - 'wrapper\n' - ' around another object that alters the way in which that ' - 'object\n' - ' is retrieved from classes and class instances. The behaviour ' + '\n' + 'Static method objects\n' + '---------------------\n' + '\n' + 'Static method objects provide a way of defeating the transformation ' 'of\n' - ' class method objects upon such retrieval is described above,\n' - ' under “User-defined methods”. Class method objects are ' - 'created\n' - ' by the built-in "classmethod()" constructor.\n', + 'function objects to method objects described above. A static ' + 'method\n' + 'object is a wrapper around any other object, usually a ' + 'user-defined\n' + 'method object. When a static method object is retrieved from a ' + 'class\n' + 'or a class instance, the object actually returned is the wrapped\n' + 'object, which is not subject to any further transformation. Static\n' + 'method objects are also callable. Static method objects are created ' + 'by\n' + 'the built-in "staticmethod()" constructor.\n' + '\n' + '\n' + 'Class method objects\n' + '--------------------\n' + '\n' + 'A class method object, like a static method object, is a wrapper\n' + 'around another object that alters the way in which that object is\n' + 'retrieved from classes and class instances. The behaviour of class\n' + 'method objects upon such retrieval is described above, under ' + '“User-\n' + 'defined methods”. Class method objects are created by the built-in\n' + '"classmethod()" constructor.\n', 'typesfunctions': 'Functions\n' '*********\n' '\n' diff --git a/Misc/NEWS.d/3.11.6.rst b/Misc/NEWS.d/3.11.6.rst new file mode 100644 index 00000000000000..f3fbe96eaefc55 --- /dev/null +++ b/Misc/NEWS.d/3.11.6.rst @@ -0,0 +1,607 @@ +.. date: 2023-09-12-16-00-42 +.. gh-issue: 109351 +.. nonce: kznGeR +.. release date: 2023-10-02 +.. section: Core and Builtins + +Fix crash when compiling an invalid AST involving a named (walrus) +expression. + +.. + +.. date: 2023-09-10-18-53-55 +.. gh-issue: 109207 +.. nonce: Fei8bY +.. section: Core and Builtins + +Fix a SystemError in ``__repr__`` of symtable entry object. + +.. + +.. date: 2023-09-09-21-17-18 +.. gh-issue: 109179 +.. nonce: ZR8qs2 +.. section: Core and Builtins + +Fix bug where the C traceback display drops notes from :exc:`SyntaxError`. + +.. + +.. date: 2023-09-07-16-05-36 +.. gh-issue: 88943 +.. nonce: rH_X3W +.. section: Core and Builtins + +Improve syntax error for non-ASCII character that follows a numerical +literal. It now points on the invalid non-ASCII character, not on the valid +numerical literal. + +.. + +.. date: 2023-09-05-20-52-17 +.. gh-issue: 108959 +.. nonce: 6z45Sy +.. section: Core and Builtins + +Fix caret placement for error locations for subscript and binary operations +that involve non-semantic parentheses and spaces. Patch by Pablo Galindo + +.. + +.. date: 2023-08-30-15-41-47 +.. gh-issue: 108520 +.. nonce: u0ZGP_ +.. section: Core and Builtins + +Fix :meth:`multiprocessing.synchronize.SemLock.__setstate__` to properly +initialize :attr:`multiprocessing.synchronize.SemLock._is_fork_ctx`. This +fixes a regression when passing a SemLock accross nested processes. + +Rename :attr:`multiprocessing.synchronize.SemLock.is_fork_ctx` to +:attr:`multiprocessing.synchronize.SemLock._is_fork_ctx` to avoid exposing +it as public API. + +.. + +.. date: 2023-09-28-18-53-11 +.. gh-issue: 110036 +.. nonce: fECxTj +.. section: Library + +On Windows, multiprocessing ``Popen.terminate()`` now catchs +:exc:`PermissionError` and get the process exit code. If the process is +still running, raise again the :exc:`PermissionError`. Otherwise, the +process terminated as expected: store its exit code. Patch by Victor +Stinner. + +.. + +.. date: 2023-09-28-18-50-33 +.. gh-issue: 110038 +.. nonce: nx_gCu +.. section: Library + +Fixed an issue that caused :meth:`KqueueSelector.select` to not return all +the ready events in some cases when a file descriptor is registered for both +read and write. + +.. + +.. date: 2023-09-25-23-00-37 +.. gh-issue: 109631 +.. nonce: eWSqpO +.. section: Library + +:mod:`re` functions such as :func:`re.findall`, :func:`re.split`, +:func:`re.search` and :func:`re.sub` which perform short repeated matches +can now be interrupted by user. + +.. + +.. date: 2023-09-22-20-16-44 +.. gh-issue: 109593 +.. nonce: LboaNM +.. section: Library + +Avoid deadlocking on a reentrant call to the multiprocessing resource +tracker. Such a reentrant call, though unlikely, can happen if a GC pass +invokes the finalizer for a multiprocessing object such as SemLock. + +.. + +.. date: 2023-09-20-17-45-46 +.. gh-issue: 109613 +.. nonce: P13ogN +.. section: Library + +Fix :func:`os.stat` and :meth:`os.DirEntry.stat`: check for exceptions. +Previously, on Python built in debug mode, these functions could trigger a +fatal Python error (and abort the process) when a function succeeded with an +exception set. Patch by Victor Stinner. + +.. + +.. date: 2023-09-13-17-22-44 +.. gh-issue: 109375 +.. nonce: ijJHZ9 +.. section: Library + +The :mod:`pdb` ``alias`` command now prevents registering aliases without +arguments. + +.. + +.. date: 2023-09-11-00-32-18 +.. gh-issue: 107219 +.. nonce: 3zqyFT +.. section: Library + +Fix a race condition in ``concurrent.futures``. When a process in the +process pool was terminated abruptly (while the future was running or +pending), close the connection write end. If the call queue is blocked on +sending bytes to a worker process, closing the connection write end +interrupts the send, so the queue can be closed. Patch by Victor Stinner. + +.. + +.. date: 2023-09-09-15-08-37 +.. gh-issue: 50644 +.. nonce: JUAZOh +.. section: Library + +Attempts to pickle or create a shallow or deep copy of :mod:`codecs` streams +now raise a TypeError. Previously, copying failed with a RecursionError, +while pickling produced wrong results that eventually caused unpickling to +fail with a RecursionError. + +.. + +.. date: 2023-09-08-12-09-55 +.. gh-issue: 108987 +.. nonce: x5AIG8 +.. section: Library + +Fix :func:`_thread.start_new_thread` race condition. If a thread is created +during Python finalization, the newly spawned thread now exits immediately +instead of trying to access freed memory and lead to a crash. Patch by +Victor Stinner. + +.. + +.. date: 2023-09-06-04-30-05 +.. gh-issue: 108843 +.. nonce: WJMhsS +.. section: Library + +Fix an issue in :func:`ast.unparse` when unparsing f-strings containing many +quote types. + +.. + +.. date: 2023-08-30-20-10-28 +.. gh-issue: 108682 +.. nonce: c2gzLQ +.. section: Library + +Enum: raise :exc:`TypeError` if ``super().__new__()`` is called from a +custom ``__new__``. + +.. + +.. date: 2023-08-26-12-35-39 +.. gh-issue: 105829 +.. nonce: kyYhWI +.. section: Library + +Fix concurrent.futures.ProcessPoolExecutor deadlock + +.. + +.. date: 2023-08-22-22-29-42 +.. gh-issue: 64662 +.. nonce: jHl_Bt +.. section: Library + +Fix support for virtual tables in :meth:`sqlite3.Connection.iterdump`. Patch +by Aviv Palivoda. + +.. + +.. date: 2023-08-14-11-18-13 +.. gh-issue: 107913 +.. nonce: 4ooY6i +.. section: Library + +Fix possible losses of ``errno`` and ``winerror`` values in :exc:`OSError` +exceptions if they were cleared or modified by the cleanup code before +creating the exception object. + +.. + +.. date: 2023-05-22-18-39-53 +.. gh-issue: 104372 +.. nonce: 7tDRaK +.. section: Library + +On Linux where :mod:`subprocess` can use the ``vfork()`` syscall for faster +spawning, prevent the parent process from blocking other threads by dropping +the GIL while it waits for the vfork'ed child process ``exec()`` outcome. +This prevents spawning a binary from a slow filesystem from blocking the +rest of the application. + +.. + +.. date: 2022-12-24-12-50-54 +.. gh-issue: 84867 +.. nonce: OhaLbU +.. section: Library + +:class:`unittest.TestLoader` no longer loads test cases from exact +:class:`unittest.TestCase` and :class:`unittest.FunctionTestCase` classes. + +.. + +.. date: 2023-09-10-02-39-06 +.. gh-issue: 109209 +.. nonce: 0LBewo +.. section: Documentation + +The minimum Sphinx version required for the documentation is now 4.2. + +.. + +.. date: 2023-05-29-14-10-24 +.. gh-issue: 105052 +.. nonce: MGFwbm +.. section: Documentation + +Update ``timeit`` doc to specify that time in seconds is just the default. + +.. + +.. date: 2023-03-19-09-39-31 +.. gh-issue: 102823 +.. nonce: OzsOz0 +.. section: Documentation + +Document the return type of ``x // y`` when ``x`` and ``y`` have type +:class:`float`. + +.. + +.. date: 2023-09-29-14-11-30 +.. gh-issue: 110031 +.. nonce: fQnFnc +.. section: Tests + +Skip test_threading tests using thread+fork if Python is built with Address +Sanitizer (ASAN). Patch by Victor Stinner. + +.. + +.. date: 2023-09-29-12-48-42 +.. gh-issue: 110088 +.. nonce: qUhRga +.. section: Tests + +Fix test_asyncio timeouts: don't measure the maximum duration, a test should +not measure a CI performance. Only measure the minimum duration when a task +has a timeout or delay. Add ``CLOCK_RES`` to ``test_asyncio.utils``. Patch +by Victor Stinner. + +.. + +.. date: 2023-09-28-18-14-52 +.. gh-issue: 110033 +.. nonce: 2yHMx0 +.. section: Tests + +Fix ``test_interprocess_signal()`` of ``test_signal``. Make sure that the +``subprocess.Popen`` object is deleted before the test raising an exception +in a signal handler. Otherwise, ``Popen.__del__()`` can get the exception +which is logged as ``Exception ignored in: ...`` and the test fails. Patch +by Victor Stinner. + +.. + +.. date: 2023-09-28-14-47-14 +.. gh-issue: 109594 +.. nonce: DB5KPP +.. section: Tests + +Fix test_timeout() of test_concurrent_futures.test_wait. Remove the future +which may or may not complete depending if it takes longer than the timeout +ot not. Keep the second future which does not complete before wait() +timeout. Patch by Victor Stinner. + +.. + +.. date: 2023-09-26-00-49-18 +.. gh-issue: 109748 +.. nonce: nxlT1i +.. section: Tests + +Fix ``test_zippath_from_non_installed_posix()`` of test_venv: don't copy +``__pycache__/`` sub-directories, because they can be modified by other +Python tests running in parallel. Patch by Victor Stinner. + +.. + +.. date: 2023-09-20-02-32-17 +.. gh-issue: 103053 +.. nonce: AoUJuK +.. section: Tests + +Skip test_freeze_simple_script() of test_tools.test_freeze if Python is +built with ``./configure --enable-optimizations``, which means with Profile +Guided Optimization (PGO): it just makes the test too slow. The freeze tool +is tested by many other CIs with other (faster) compiler flags. Patch by +Victor Stinner. + +.. + +.. date: 2023-09-14-22-58-47 +.. gh-issue: 109396 +.. nonce: J1a4jR +.. section: Tests + +Fix ``test_socket.test_hmac_sha1()`` in FIPS mode. Use a longer key: FIPS +mode requires at least of at least 112 bits. The previous key was only 32 +bits. Patch by Victor Stinner. + +.. + +.. date: 2023-09-13-05-58-09 +.. gh-issue: 104736 +.. nonce: lA25Fu +.. section: Tests + +Fix test_gdb on Python built with LLVM clang 16 on Linux ppc64le (ex: Fedora +38). Search patterns in gdb "bt" command output to detect when gdb fails to +retrieve the traceback. For example, skip a test if ``Backtrace stopped: +frame did not save the PC`` is found. Patch by Victor Stinner. + +.. + +.. date: 2023-09-10-22-32-20 +.. gh-issue: 109237 +.. nonce: SvgKwD +.. section: Tests + +Fix ``test_site.test_underpth_basic()`` when the working directory contains +at least one non-ASCII character: encode the ``._pth`` file to UTF-8 and +enable the UTF-8 Mode to use UTF-8 for the child process stdout. Patch by +Victor Stinner. + +.. + +.. date: 2023-09-10-19-59-57 +.. gh-issue: 109230 +.. nonce: SRNLFQ +.. section: Tests + +Fix ``test_pyexpat.test_exception()``: it can now be run from a directory +different than Python source code directory. Before, the test failed in this +case. Skip the test if Modules/pyexpat.c source is not available. Skip also +the test on Python implementations other than CPython. Patch by Victor +Stinner. + +.. + +.. date: 2023-09-06-18-27-53 +.. gh-issue: 109015 +.. nonce: 1dS1AQ +.. section: Tests + +Fix test_asyncio, test_imaplib and test_socket tests on FreeBSD if the TCP +blackhole is enabled (``sysctl net.inet.tcp.blackhole``). Skip the few tests +which failed with ``ETIMEDOUT`` which such non standard configuration. +Currently, the `FreeBSD GCP image enables TCP and UDP blackhole +`_ (``sysctl net.inet.tcp.blackhole=2`` +and ``sysctl net.inet.udp.blackhole=1``). Patch by Victor Stinner. + +.. + +.. date: 2023-09-06-15-36-51 +.. gh-issue: 91960 +.. nonce: P3nD5v +.. section: Tests + +Skip ``test_gdb`` if gdb is unable to retrieve Python frame objects: if a +frame is ````. When Python is built with "clang -Og", gdb can +fail to retrive the *frame* parameter of ``_PyEval_EvalFrameDefault()``. In +this case, tests like ``py_bt()`` are likely to fail. Without getting access +to Python frames, ``python-gdb.py`` is mostly clueless on retrieving the +Python traceback. Moreover, ``test_gdb`` is no longer skipped on macOS if +Python is built with Clang. Patch by Victor Stinner. + +.. + +.. date: 2023-09-05-23-00-09 +.. gh-issue: 108962 +.. nonce: R4NwuU +.. section: Tests + +Skip ``test_tempfile.test_flags()`` if ``chflags()`` fails with "OSError: +[Errno 45] Operation not supported" (ex: on FreeBSD 13). Patch by Victor +Stinner. + +.. + +.. date: 2023-09-04-15-18-14 +.. gh-issue: 89392 +.. nonce: 8A4T5p +.. section: Tests + +Removed support of ``test_main()`` function in tests. They now always use +normal unittest test runner. + +.. + +.. date: 2023-09-03-21-41-10 +.. gh-issue: 108851 +.. nonce: xFTYOE +.. section: Tests + +Fix ``test_tomllib`` recursion tests for WASI buildbots: reduce the +recursion limit and compute the maximum nested array/dict depending on the +current available recursion limit. Patch by Victor Stinner. + +.. + +.. date: 2023-09-03-21-18-35 +.. gh-issue: 108851 +.. nonce: CCuHyI +.. section: Tests + +Add ``get_recursion_available()`` and ``get_recursion_depth()`` functions to +the :mod:`test.support` module. Patch by Victor Stinner. + +.. + +.. date: 2023-09-02-19-06-52 +.. gh-issue: 108822 +.. nonce: arTbBI +.. section: Tests + +``regrtest`` now computes statistics on all tests: successes, failures and +skipped. ``test_netrc``, ``test_pep646_syntax`` and ``test_xml_etree`` now +return results in their ``test_main()`` function. Patch by Victor Stinner +and Alex Waygood. + +.. + +.. date: 2023-08-24-06-10-36 +.. gh-issue: 108388 +.. nonce: YCVB0D +.. section: Tests + +Convert test_concurrent_futures to a package of 7 sub-tests. Patch by Victor +Stinner. + +.. + +.. date: 2023-08-24-04-23-35 +.. gh-issue: 108388 +.. nonce: mr0MeE +.. section: Tests + +Split test_multiprocessing_fork, test_multiprocessing_forkserver and +test_multiprocessing_spawn into test packages. Each package is made of 4 +sub-tests: processes, threads, manager and misc. It allows running more +tests in parallel and so reduce the total test duration. Patch by Victor +Stinner. + +.. + +.. date: 2023-06-28-02-51-08 +.. gh-issue: 101634 +.. nonce: Rayczr +.. section: Tests + +When running the Python test suite with ``-jN`` option, if a worker stdout +cannot be decoded from the locale encoding report a failed testn so the +exitcode is non-zero. Patch by Victor Stinner. + +.. + +.. date: 2022-12-08-00-03-37 +.. gh-issue: 100086 +.. nonce: 1zYpto +.. section: Tests + +The Python test runner (libregrtest) now logs Python build information like +"debug" vs "release" build, or LTO and PGO optimizations. Patch by Victor +Stinner. + +.. + +.. date: 2022-10-31-14-47-49 +.. gh-issue: 98903 +.. nonce: 7KinCV +.. section: Tests + +The Python test suite now fails wit exit code 4 if no tests ran. It should +help detecting typos in test names and test methods. + +.. + +.. date: 2022-10-20-17-49-50 +.. gh-issue: 95027 +.. nonce: viRpJB +.. section: Tests + +On Windows, when the Python test suite is run with the ``-jN`` option, the +ANSI code page is now used as the encoding for the stdout temporary file, +rather than using UTF-8 which can lead to decoding errors. Patch by Victor +Stinner. + +.. + +.. date: 2022-06-16-17-50-58 +.. gh-issue: 93353 +.. nonce: JdpATx +.. section: Tests + +regrtest now checks if a test leaks temporary files or directories if run +with -jN option. Patch by Victor Stinner. + +.. + +.. date: 2023-09-02-18-04-15 +.. gh-issue: 63760 +.. nonce: r8hJ6q +.. section: Build + +Fix Solaris build: no longer redefine the ``gethostname()`` function. +Solaris defines the function since 2005. Patch by Victor Stinner, original +patch by Jakub Kulík. + +.. + +.. date: 2023-09-01-01-39-26 +.. gh-issue: 108740 +.. nonce: JHExAQ +.. section: Build + +Fix a race condition in ``make regen-all``. The ``deepfreeze.c`` source and +files generated by Argument Clinic are now generated or updated before +generating "global objects". Previously, some identifiers may miss depending +on the order in which these files were generated. Patch by Victor Stinner. + +.. + +.. date: 2023-09-28-17-09-23 +.. gh-issue: 109991 +.. nonce: CIMftz +.. section: Windows + +Update Windows build to use OpenSSL 3.0.11. + +.. + +.. date: 2023-09-05-10-08-47 +.. gh-issue: 107565 +.. nonce: CIMftz +.. section: Windows + +Update Windows build to use OpenSSL 3.0.10. + +.. + +.. date: 2023-09-27-22-35-22 +.. gh-issue: 109991 +.. nonce: -xJzaF +.. section: macOS + +Update macOS installer to use OpenSSL 3.0.11. + +.. + +.. date: 2023-09-27-23-31-54 +.. gh-issue: 109991 +.. nonce: sUUYY8 +.. section: Tools/Demos + +Update GitHub CI workflows to use OpenSSL 3.0.11 and multissltests to use +1.1.1w, 3.0.11, and 3.1.3. diff --git a/Misc/NEWS.d/next/Build/2023-09-01-01-39-26.gh-issue-108740.JHExAQ.rst b/Misc/NEWS.d/next/Build/2023-09-01-01-39-26.gh-issue-108740.JHExAQ.rst deleted file mode 100644 index 190d50387f339e..00000000000000 --- a/Misc/NEWS.d/next/Build/2023-09-01-01-39-26.gh-issue-108740.JHExAQ.rst +++ /dev/null @@ -1,4 +0,0 @@ -Fix a race condition in ``make regen-all``. The ``deepfreeze.c`` source and -files generated by Argument Clinic are now generated or updated before -generating "global objects". Previously, some identifiers may miss depending -on the order in which these files were generated. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Build/2023-09-02-18-04-15.gh-issue-63760.r8hJ6q.rst b/Misc/NEWS.d/next/Build/2023-09-02-18-04-15.gh-issue-63760.r8hJ6q.rst deleted file mode 100644 index 9a7249e923e0c7..00000000000000 --- a/Misc/NEWS.d/next/Build/2023-09-02-18-04-15.gh-issue-63760.r8hJ6q.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix Solaris build: no longer redefine the ``gethostname()`` function. Solaris -defines the function since 2005. Patch by Victor Stinner, original patch by -Jakub Kulík. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-08-30-15-41-47.gh-issue-108520.u0ZGP_.rst b/Misc/NEWS.d/next/Core and Builtins/2023-08-30-15-41-47.gh-issue-108520.u0ZGP_.rst deleted file mode 100644 index 44131fb11f068c..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-08-30-15-41-47.gh-issue-108520.u0ZGP_.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix :meth:`multiprocessing.synchronize.SemLock.__setstate__` to properly initialize :attr:`multiprocessing.synchronize.SemLock._is_fork_ctx`. This fixes a regression when passing a SemLock accross nested processes. - -Rename :attr:`multiprocessing.synchronize.SemLock.is_fork_ctx` to :attr:`multiprocessing.synchronize.SemLock._is_fork_ctx` to avoid exposing it as public API. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-09-05-20-52-17.gh-issue-108959.6z45Sy.rst b/Misc/NEWS.d/next/Core and Builtins/2023-09-05-20-52-17.gh-issue-108959.6z45Sy.rst deleted file mode 100644 index 792bbc454f2b27..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-09-05-20-52-17.gh-issue-108959.6z45Sy.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix caret placement for error locations for subscript and binary operations -that involve non-semantic parentheses and spaces. Patch by Pablo Galindo diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-09-07-16-05-36.gh-issue-88943.rH_X3W.rst b/Misc/NEWS.d/next/Core and Builtins/2023-09-07-16-05-36.gh-issue-88943.rH_X3W.rst deleted file mode 100644 index a99830fe4227c9..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-09-07-16-05-36.gh-issue-88943.rH_X3W.rst +++ /dev/null @@ -1,3 +0,0 @@ -Improve syntax error for non-ASCII character that follows a numerical -literal. It now points on the invalid non-ASCII character, not on the valid -numerical literal. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-09-09-21-17-18.gh-issue-109179.ZR8qs2.rst b/Misc/NEWS.d/next/Core and Builtins/2023-09-09-21-17-18.gh-issue-109179.ZR8qs2.rst deleted file mode 100644 index dd95a8ec7920aa..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-09-09-21-17-18.gh-issue-109179.ZR8qs2.rst +++ /dev/null @@ -1 +0,0 @@ -Fix bug where the C traceback display drops notes from :exc:`SyntaxError`. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-09-10-18-53-55.gh-issue-109207.Fei8bY.rst b/Misc/NEWS.d/next/Core and Builtins/2023-09-10-18-53-55.gh-issue-109207.Fei8bY.rst deleted file mode 100644 index f9da3ac4d1abbd..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-09-10-18-53-55.gh-issue-109207.Fei8bY.rst +++ /dev/null @@ -1 +0,0 @@ -Fix a SystemError in ``__repr__`` of symtable entry object. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-09-12-16-00-42.gh-issue-109351.kznGeR.rst b/Misc/NEWS.d/next/Core and Builtins/2023-09-12-16-00-42.gh-issue-109351.kznGeR.rst deleted file mode 100644 index 23b81c1c0a3baa..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-09-12-16-00-42.gh-issue-109351.kznGeR.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix crash when compiling an invalid AST involving a named (walrus) -expression. diff --git a/Misc/NEWS.d/next/Documentation/2023-03-19-09-39-31.gh-issue-102823.OzsOz0.rst b/Misc/NEWS.d/next/Documentation/2023-03-19-09-39-31.gh-issue-102823.OzsOz0.rst deleted file mode 100644 index 1e32f3c89231c8..00000000000000 --- a/Misc/NEWS.d/next/Documentation/2023-03-19-09-39-31.gh-issue-102823.OzsOz0.rst +++ /dev/null @@ -1,2 +0,0 @@ -Document the return type of ``x // y`` when ``x`` and ``y`` have type -:class:`float`. diff --git a/Misc/NEWS.d/next/Documentation/2023-05-29-14-10-24.gh-issue-105052.MGFwbm.rst b/Misc/NEWS.d/next/Documentation/2023-05-29-14-10-24.gh-issue-105052.MGFwbm.rst deleted file mode 100644 index 8fdc38d439f54f..00000000000000 --- a/Misc/NEWS.d/next/Documentation/2023-05-29-14-10-24.gh-issue-105052.MGFwbm.rst +++ /dev/null @@ -1 +0,0 @@ -Update ``timeit`` doc to specify that time in seconds is just the default. diff --git a/Misc/NEWS.d/next/Documentation/2023-09-10-02-39-06.gh-issue-109209.0LBewo.rst b/Misc/NEWS.d/next/Documentation/2023-09-10-02-39-06.gh-issue-109209.0LBewo.rst deleted file mode 100644 index 79cc0b72ec742f..00000000000000 --- a/Misc/NEWS.d/next/Documentation/2023-09-10-02-39-06.gh-issue-109209.0LBewo.rst +++ /dev/null @@ -1 +0,0 @@ -The minimum Sphinx version required for the documentation is now 4.2. diff --git a/Misc/NEWS.d/next/Library/2022-12-24-12-50-54.gh-issue-84867.OhaLbU.rst b/Misc/NEWS.d/next/Library/2022-12-24-12-50-54.gh-issue-84867.OhaLbU.rst deleted file mode 100644 index 8b45dcee481916..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-12-24-12-50-54.gh-issue-84867.OhaLbU.rst +++ /dev/null @@ -1,2 +0,0 @@ -:class:`unittest.TestLoader` no longer loads test cases from exact -:class:`unittest.TestCase` and :class:`unittest.FunctionTestCase` classes. diff --git a/Misc/NEWS.d/next/Library/2023-05-22-18-39-53.gh-issue-104372.7tDRaK.rst b/Misc/NEWS.d/next/Library/2023-05-22-18-39-53.gh-issue-104372.7tDRaK.rst deleted file mode 100644 index ea13ec85543ca2..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-05-22-18-39-53.gh-issue-104372.7tDRaK.rst +++ /dev/null @@ -1,5 +0,0 @@ -On Linux where :mod:`subprocess` can use the ``vfork()`` syscall for faster -spawning, prevent the parent process from blocking other threads by dropping -the GIL while it waits for the vfork'ed child process ``exec()`` outcome. -This prevents spawning a binary from a slow filesystem from blocking the -rest of the application. diff --git a/Misc/NEWS.d/next/Library/2023-08-14-11-18-13.gh-issue-107913.4ooY6i.rst b/Misc/NEWS.d/next/Library/2023-08-14-11-18-13.gh-issue-107913.4ooY6i.rst deleted file mode 100644 index de5e21abfc3ae7..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-08-14-11-18-13.gh-issue-107913.4ooY6i.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix possible losses of ``errno`` and ``winerror`` values in :exc:`OSError` -exceptions if they were cleared or modified by the cleanup code before -creating the exception object. diff --git a/Misc/NEWS.d/next/Library/2023-08-22-22-29-42.gh-issue-64662.jHl_Bt.rst b/Misc/NEWS.d/next/Library/2023-08-22-22-29-42.gh-issue-64662.jHl_Bt.rst deleted file mode 100644 index 1b4c33a82b0b86..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-08-22-22-29-42.gh-issue-64662.jHl_Bt.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix support for virtual tables in :meth:`sqlite3.Connection.iterdump`. Patch -by Aviv Palivoda. diff --git a/Misc/NEWS.d/next/Library/2023-08-26-12-35-39.gh-issue-105829.kyYhWI.rst b/Misc/NEWS.d/next/Library/2023-08-26-12-35-39.gh-issue-105829.kyYhWI.rst deleted file mode 100644 index eaa2a5a4330e28..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-08-26-12-35-39.gh-issue-105829.kyYhWI.rst +++ /dev/null @@ -1 +0,0 @@ -Fix concurrent.futures.ProcessPoolExecutor deadlock diff --git a/Misc/NEWS.d/next/Library/2023-08-30-20-10-28.gh-issue-108682.c2gzLQ.rst b/Misc/NEWS.d/next/Library/2023-08-30-20-10-28.gh-issue-108682.c2gzLQ.rst deleted file mode 100644 index 148d4329142740..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-08-30-20-10-28.gh-issue-108682.c2gzLQ.rst +++ /dev/null @@ -1,2 +0,0 @@ -Enum: raise :exc:`TypeError` if ``super().__new__()`` is called from a -custom ``__new__``. diff --git a/Misc/NEWS.d/next/Library/2023-09-06-04-30-05.gh-issue-108843.WJMhsS.rst b/Misc/NEWS.d/next/Library/2023-09-06-04-30-05.gh-issue-108843.WJMhsS.rst deleted file mode 100644 index 0f15761c14bb7d..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-09-06-04-30-05.gh-issue-108843.WJMhsS.rst +++ /dev/null @@ -1 +0,0 @@ -Fix an issue in :func:`ast.unparse` when unparsing f-strings containing many quote types. diff --git a/Misc/NEWS.d/next/Library/2023-09-08-12-09-55.gh-issue-108987.x5AIG8.rst b/Misc/NEWS.d/next/Library/2023-09-08-12-09-55.gh-issue-108987.x5AIG8.rst deleted file mode 100644 index 16526ee748d869..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-09-08-12-09-55.gh-issue-108987.x5AIG8.rst +++ /dev/null @@ -1,4 +0,0 @@ -Fix :func:`_thread.start_new_thread` race condition. If a thread is created -during Python finalization, the newly spawned thread now exits immediately -instead of trying to access freed memory and lead to a crash. Patch by -Victor Stinner. diff --git a/Misc/NEWS.d/next/Library/2023-09-09-15-08-37.gh-issue-50644.JUAZOh.rst b/Misc/NEWS.d/next/Library/2023-09-09-15-08-37.gh-issue-50644.JUAZOh.rst deleted file mode 100644 index a7a442e35289d3..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-09-09-15-08-37.gh-issue-50644.JUAZOh.rst +++ /dev/null @@ -1,4 +0,0 @@ -Attempts to pickle or create a shallow or deep copy of :mod:`codecs` streams -now raise a TypeError. Previously, copying failed with a RecursionError, -while pickling produced wrong results that eventually caused unpickling -to fail with a RecursionError. diff --git a/Misc/NEWS.d/next/Library/2023-09-11-00-32-18.gh-issue-107219.3zqyFT.rst b/Misc/NEWS.d/next/Library/2023-09-11-00-32-18.gh-issue-107219.3zqyFT.rst deleted file mode 100644 index 10afbcf823386a..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-09-11-00-32-18.gh-issue-107219.3zqyFT.rst +++ /dev/null @@ -1,5 +0,0 @@ -Fix a race condition in ``concurrent.futures``. When a process in the -process pool was terminated abruptly (while the future was running or -pending), close the connection write end. If the call queue is blocked on -sending bytes to a worker process, closing the connection write end interrupts -the send, so the queue can be closed. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Library/2023-09-13-17-22-44.gh-issue-109375.ijJHZ9.rst b/Misc/NEWS.d/next/Library/2023-09-13-17-22-44.gh-issue-109375.ijJHZ9.rst deleted file mode 100644 index 9b7a85d05f66ca..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-09-13-17-22-44.gh-issue-109375.ijJHZ9.rst +++ /dev/null @@ -1 +0,0 @@ -The :mod:`pdb` ``alias`` command now prevents registering aliases without arguments. diff --git a/Misc/NEWS.d/next/Library/2023-09-20-17-45-46.gh-issue-109613.P13ogN.rst b/Misc/NEWS.d/next/Library/2023-09-20-17-45-46.gh-issue-109613.P13ogN.rst deleted file mode 100644 index e21a758fc2eb05..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-09-20-17-45-46.gh-issue-109613.P13ogN.rst +++ /dev/null @@ -1,4 +0,0 @@ -Fix :func:`os.stat` and :meth:`os.DirEntry.stat`: check for exceptions. -Previously, on Python built in debug mode, these functions could trigger a -fatal Python error (and abort the process) when a function succeeded with an -exception set. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Library/2023-09-22-20-16-44.gh-issue-109593.LboaNM.rst b/Misc/NEWS.d/next/Library/2023-09-22-20-16-44.gh-issue-109593.LboaNM.rst deleted file mode 100644 index 292aea0be24dfb..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-09-22-20-16-44.gh-issue-109593.LboaNM.rst +++ /dev/null @@ -1 +0,0 @@ -Avoid deadlocking on a reentrant call to the multiprocessing resource tracker. Such a reentrant call, though unlikely, can happen if a GC pass invokes the finalizer for a multiprocessing object such as SemLock. diff --git a/Misc/NEWS.d/next/Library/2023-09-25-23-00-37.gh-issue-109631.eWSqpO.rst b/Misc/NEWS.d/next/Library/2023-09-25-23-00-37.gh-issue-109631.eWSqpO.rst deleted file mode 100644 index 58af2e57068267..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-09-25-23-00-37.gh-issue-109631.eWSqpO.rst +++ /dev/null @@ -1,3 +0,0 @@ -:mod:`re` functions such as :func:`re.findall`, :func:`re.split`, -:func:`re.search` and :func:`re.sub` which perform short repeated matches -can now be interrupted by user. diff --git a/Misc/NEWS.d/next/Library/2023-09-28-18-50-33.gh-issue-110038.nx_gCu.rst b/Misc/NEWS.d/next/Library/2023-09-28-18-50-33.gh-issue-110038.nx_gCu.rst deleted file mode 100644 index 6b2abd802fccdc..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-09-28-18-50-33.gh-issue-110038.nx_gCu.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fixed an issue that caused :meth:`KqueueSelector.select` to not return all -the ready events in some cases when a file descriptor is registered for both -read and write. diff --git a/Misc/NEWS.d/next/Library/2023-09-28-18-53-11.gh-issue-110036.fECxTj.rst b/Misc/NEWS.d/next/Library/2023-09-28-18-53-11.gh-issue-110036.fECxTj.rst deleted file mode 100644 index ddb11b5c3546a1..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-09-28-18-53-11.gh-issue-110036.fECxTj.rst +++ /dev/null @@ -1,5 +0,0 @@ -On Windows, multiprocessing ``Popen.terminate()`` now catchs -:exc:`PermissionError` and get the process exit code. If the process is -still running, raise again the :exc:`PermissionError`. Otherwise, the -process terminated as expected: store its exit code. Patch by Victor -Stinner. diff --git a/Misc/NEWS.d/next/Tests/2022-06-16-17-50-58.gh-issue-93353.JdpATx.rst b/Misc/NEWS.d/next/Tests/2022-06-16-17-50-58.gh-issue-93353.JdpATx.rst deleted file mode 100644 index 4e232948f49ee7..00000000000000 --- a/Misc/NEWS.d/next/Tests/2022-06-16-17-50-58.gh-issue-93353.JdpATx.rst +++ /dev/null @@ -1,2 +0,0 @@ -regrtest now checks if a test leaks temporary files or directories if run -with -jN option. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Tests/2022-10-20-17-49-50.gh-issue-95027.viRpJB.rst b/Misc/NEWS.d/next/Tests/2022-10-20-17-49-50.gh-issue-95027.viRpJB.rst deleted file mode 100644 index 8bf1a9d33573ab..00000000000000 --- a/Misc/NEWS.d/next/Tests/2022-10-20-17-49-50.gh-issue-95027.viRpJB.rst +++ /dev/null @@ -1,4 +0,0 @@ -On Windows, when the Python test suite is run with the ``-jN`` option, the -ANSI code page is now used as the encoding for the stdout temporary file, -rather than using UTF-8 which can lead to decoding errors. Patch by Victor -Stinner. diff --git a/Misc/NEWS.d/next/Tests/2022-10-31-14-47-49.gh-issue-98903.7KinCV.rst b/Misc/NEWS.d/next/Tests/2022-10-31-14-47-49.gh-issue-98903.7KinCV.rst deleted file mode 100644 index 65636ab0683f39..00000000000000 --- a/Misc/NEWS.d/next/Tests/2022-10-31-14-47-49.gh-issue-98903.7KinCV.rst +++ /dev/null @@ -1,2 +0,0 @@ -The Python test suite now fails wit exit code 4 if no tests ran. It should -help detecting typos in test names and test methods. diff --git a/Misc/NEWS.d/next/Tests/2022-12-08-00-03-37.gh-issue-100086.1zYpto.rst b/Misc/NEWS.d/next/Tests/2022-12-08-00-03-37.gh-issue-100086.1zYpto.rst deleted file mode 100644 index a5f1bb9f5a5e05..00000000000000 --- a/Misc/NEWS.d/next/Tests/2022-12-08-00-03-37.gh-issue-100086.1zYpto.rst +++ /dev/null @@ -1,3 +0,0 @@ -The Python test runner (libregrtest) now logs Python build information like -"debug" vs "release" build, or LTO and PGO optimizations. Patch by Victor -Stinner. diff --git a/Misc/NEWS.d/next/Tests/2023-06-28-02-51-08.gh-issue-101634.Rayczr.rst b/Misc/NEWS.d/next/Tests/2023-06-28-02-51-08.gh-issue-101634.Rayczr.rst deleted file mode 100644 index 6fbfc84c19e1b8..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-06-28-02-51-08.gh-issue-101634.Rayczr.rst +++ /dev/null @@ -1,3 +0,0 @@ -When running the Python test suite with ``-jN`` option, if a worker stdout -cannot be decoded from the locale encoding report a failed testn so the -exitcode is non-zero. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Tests/2023-08-24-04-23-35.gh-issue-108388.mr0MeE.rst b/Misc/NEWS.d/next/Tests/2023-08-24-04-23-35.gh-issue-108388.mr0MeE.rst deleted file mode 100644 index 8cf77b1cc187f1..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-08-24-04-23-35.gh-issue-108388.mr0MeE.rst +++ /dev/null @@ -1,4 +0,0 @@ -Split test_multiprocessing_fork, test_multiprocessing_forkserver and -test_multiprocessing_spawn into test packages. Each package is made of 4 -sub-tests: processes, threads, manager and misc. It allows running more tests -in parallel and so reduce the total test duration. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Tests/2023-08-24-06-10-36.gh-issue-108388.YCVB0D.rst b/Misc/NEWS.d/next/Tests/2023-08-24-06-10-36.gh-issue-108388.YCVB0D.rst deleted file mode 100644 index ddff07be024d47..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-08-24-06-10-36.gh-issue-108388.YCVB0D.rst +++ /dev/null @@ -1,2 +0,0 @@ -Convert test_concurrent_futures to a package of 7 sub-tests. Patch by Victor -Stinner. diff --git a/Misc/NEWS.d/next/Tests/2023-09-02-19-06-52.gh-issue-108822.arTbBI.rst b/Misc/NEWS.d/next/Tests/2023-09-02-19-06-52.gh-issue-108822.arTbBI.rst deleted file mode 100644 index e1c6df2adcb0ae..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-09-02-19-06-52.gh-issue-108822.arTbBI.rst +++ /dev/null @@ -1,4 +0,0 @@ -``regrtest`` now computes statistics on all tests: successes, failures and -skipped. ``test_netrc``, ``test_pep646_syntax`` and ``test_xml_etree`` now -return results in their ``test_main()`` function. Patch by Victor Stinner -and Alex Waygood. diff --git a/Misc/NEWS.d/next/Tests/2023-09-03-21-18-35.gh-issue-108851.CCuHyI.rst b/Misc/NEWS.d/next/Tests/2023-09-03-21-18-35.gh-issue-108851.CCuHyI.rst deleted file mode 100644 index 7a5b3052af22f2..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-09-03-21-18-35.gh-issue-108851.CCuHyI.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add ``get_recursion_available()`` and ``get_recursion_depth()`` functions to -the :mod:`test.support` module. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Tests/2023-09-03-21-41-10.gh-issue-108851.xFTYOE.rst b/Misc/NEWS.d/next/Tests/2023-09-03-21-41-10.gh-issue-108851.xFTYOE.rst deleted file mode 100644 index b35aaebb410afb..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-09-03-21-41-10.gh-issue-108851.xFTYOE.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix ``test_tomllib`` recursion tests for WASI buildbots: reduce the recursion -limit and compute the maximum nested array/dict depending on the current -available recursion limit. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Tests/2023-09-04-15-18-14.gh-issue-89392.8A4T5p.rst b/Misc/NEWS.d/next/Tests/2023-09-04-15-18-14.gh-issue-89392.8A4T5p.rst deleted file mode 100644 index e1dea8e78cdd4e..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-09-04-15-18-14.gh-issue-89392.8A4T5p.rst +++ /dev/null @@ -1,2 +0,0 @@ -Removed support of ``test_main()`` function in tests. They now always use -normal unittest test runner. diff --git a/Misc/NEWS.d/next/Tests/2023-09-05-23-00-09.gh-issue-108962.R4NwuU.rst b/Misc/NEWS.d/next/Tests/2023-09-05-23-00-09.gh-issue-108962.R4NwuU.rst deleted file mode 100644 index 380fb20b8881b2..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-09-05-23-00-09.gh-issue-108962.R4NwuU.rst +++ /dev/null @@ -1,3 +0,0 @@ -Skip ``test_tempfile.test_flags()`` if ``chflags()`` fails with "OSError: -[Errno 45] Operation not supported" (ex: on FreeBSD 13). Patch by Victor -Stinner. diff --git a/Misc/NEWS.d/next/Tests/2023-09-06-15-36-51.gh-issue-91960.P3nD5v.rst b/Misc/NEWS.d/next/Tests/2023-09-06-15-36-51.gh-issue-91960.P3nD5v.rst deleted file mode 100644 index 46472abf9802bc..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-09-06-15-36-51.gh-issue-91960.P3nD5v.rst +++ /dev/null @@ -1,7 +0,0 @@ -Skip ``test_gdb`` if gdb is unable to retrieve Python frame objects: if a -frame is ````. When Python is built with "clang -Og", gdb can -fail to retrive the *frame* parameter of ``_PyEval_EvalFrameDefault()``. In -this case, tests like ``py_bt()`` are likely to fail. Without getting access -to Python frames, ``python-gdb.py`` is mostly clueless on retrieving the -Python traceback. Moreover, ``test_gdb`` is no longer skipped on macOS if -Python is built with Clang. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Tests/2023-09-06-18-27-53.gh-issue-109015.1dS1AQ.rst b/Misc/NEWS.d/next/Tests/2023-09-06-18-27-53.gh-issue-109015.1dS1AQ.rst deleted file mode 100644 index cb641be9312e1a..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-09-06-18-27-53.gh-issue-109015.1dS1AQ.rst +++ /dev/null @@ -1,6 +0,0 @@ -Fix test_asyncio, test_imaplib and test_socket tests on FreeBSD if the TCP -blackhole is enabled (``sysctl net.inet.tcp.blackhole``). Skip the few tests -which failed with ``ETIMEDOUT`` which such non standard configuration. -Currently, the `FreeBSD GCP image enables TCP and UDP blackhole -`_ (``sysctl net.inet.tcp.blackhole=2`` -and ``sysctl net.inet.udp.blackhole=1``). Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Tests/2023-09-10-19-59-57.gh-issue-109230.SRNLFQ.rst b/Misc/NEWS.d/next/Tests/2023-09-10-19-59-57.gh-issue-109230.SRNLFQ.rst deleted file mode 100644 index 18e1e85242005a..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-09-10-19-59-57.gh-issue-109230.SRNLFQ.rst +++ /dev/null @@ -1,5 +0,0 @@ -Fix ``test_pyexpat.test_exception()``: it can now be run from a directory -different than Python source code directory. Before, the test failed in this -case. Skip the test if Modules/pyexpat.c source is not available. Skip also -the test on Python implementations other than CPython. Patch by Victor -Stinner. diff --git a/Misc/NEWS.d/next/Tests/2023-09-10-22-32-20.gh-issue-109237.SvgKwD.rst b/Misc/NEWS.d/next/Tests/2023-09-10-22-32-20.gh-issue-109237.SvgKwD.rst deleted file mode 100644 index 1d762bbe1d2592..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-09-10-22-32-20.gh-issue-109237.SvgKwD.rst +++ /dev/null @@ -1,4 +0,0 @@ -Fix ``test_site.test_underpth_basic()`` when the working directory contains -at least one non-ASCII character: encode the ``._pth`` file to UTF-8 and -enable the UTF-8 Mode to use UTF-8 for the child process stdout. Patch by -Victor Stinner. diff --git a/Misc/NEWS.d/next/Tests/2023-09-13-05-58-09.gh-issue-104736.lA25Fu.rst b/Misc/NEWS.d/next/Tests/2023-09-13-05-58-09.gh-issue-104736.lA25Fu.rst deleted file mode 100644 index 85c370fc87ac41..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-09-13-05-58-09.gh-issue-104736.lA25Fu.rst +++ /dev/null @@ -1,4 +0,0 @@ -Fix test_gdb on Python built with LLVM clang 16 on Linux ppc64le (ex: Fedora -38). Search patterns in gdb "bt" command output to detect when gdb fails to -retrieve the traceback. For example, skip a test if ``Backtrace stopped: frame -did not save the PC`` is found. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Tests/2023-09-14-22-58-47.gh-issue-109396.J1a4jR.rst b/Misc/NEWS.d/next/Tests/2023-09-14-22-58-47.gh-issue-109396.J1a4jR.rst deleted file mode 100644 index 71150ecae76434..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-09-14-22-58-47.gh-issue-109396.J1a4jR.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix ``test_socket.test_hmac_sha1()`` in FIPS mode. Use a longer key: FIPS -mode requires at least of at least 112 bits. The previous key was only 32 -bits. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Tests/2023-09-20-02-32-17.gh-issue-103053.AoUJuK.rst b/Misc/NEWS.d/next/Tests/2023-09-20-02-32-17.gh-issue-103053.AoUJuK.rst deleted file mode 100644 index 6d67bf237bdbb2..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-09-20-02-32-17.gh-issue-103053.AoUJuK.rst +++ /dev/null @@ -1,4 +0,0 @@ -Skip test_freeze_simple_script() of test_tools.test_freeze if Python is built -with ``./configure --enable-optimizations``, which means with Profile Guided -Optimization (PGO): it just makes the test too slow. The freeze tool is tested -by many other CIs with other (faster) compiler flags. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Tests/2023-09-26-00-49-18.gh-issue-109748.nxlT1i.rst b/Misc/NEWS.d/next/Tests/2023-09-26-00-49-18.gh-issue-109748.nxlT1i.rst deleted file mode 100644 index 840366ba8d1611..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-09-26-00-49-18.gh-issue-109748.nxlT1i.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix ``test_zippath_from_non_installed_posix()`` of test_venv: don't copy -``__pycache__/`` sub-directories, because they can be modified by other Python -tests running in parallel. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Tests/2023-09-28-14-47-14.gh-issue-109594.DB5KPP.rst b/Misc/NEWS.d/next/Tests/2023-09-28-14-47-14.gh-issue-109594.DB5KPP.rst deleted file mode 100644 index 5a4ae2b0837df6..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-09-28-14-47-14.gh-issue-109594.DB5KPP.rst +++ /dev/null @@ -1,4 +0,0 @@ -Fix test_timeout() of test_concurrent_futures.test_wait. Remove the future -which may or may not complete depending if it takes longer than the timeout -ot not. Keep the second future which does not complete before wait() -timeout. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Tests/2023-09-28-18-14-52.gh-issue-110033.2yHMx0.rst b/Misc/NEWS.d/next/Tests/2023-09-28-18-14-52.gh-issue-110033.2yHMx0.rst deleted file mode 100644 index fb6089377083bf..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-09-28-18-14-52.gh-issue-110033.2yHMx0.rst +++ /dev/null @@ -1,5 +0,0 @@ -Fix ``test_interprocess_signal()`` of ``test_signal``. Make sure that the -``subprocess.Popen`` object is deleted before the test raising an exception -in a signal handler. Otherwise, ``Popen.__del__()`` can get the exception -which is logged as ``Exception ignored in: ...`` and the test fails. Patch by -Victor Stinner. diff --git a/Misc/NEWS.d/next/Tests/2023-09-29-12-48-42.gh-issue-110088.qUhRga.rst b/Misc/NEWS.d/next/Tests/2023-09-29-12-48-42.gh-issue-110088.qUhRga.rst deleted file mode 100644 index cf44a123c2c925..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-09-29-12-48-42.gh-issue-110088.qUhRga.rst +++ /dev/null @@ -1,4 +0,0 @@ -Fix test_asyncio timeouts: don't measure the maximum duration, a test should -not measure a CI performance. Only measure the minimum duration when a task has -a timeout or delay. Add ``CLOCK_RES`` to ``test_asyncio.utils``. Patch by -Victor Stinner. diff --git a/Misc/NEWS.d/next/Tests/2023-09-29-14-11-30.gh-issue-110031.fQnFnc.rst b/Misc/NEWS.d/next/Tests/2023-09-29-14-11-30.gh-issue-110031.fQnFnc.rst deleted file mode 100644 index a8a163c567d2b3..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-09-29-14-11-30.gh-issue-110031.fQnFnc.rst +++ /dev/null @@ -1,2 +0,0 @@ -Skip test_threading tests using thread+fork if Python is built with Address -Sanitizer (ASAN). Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Tools-Demos/2023-09-27-23-31-54.gh-issue-109991.sUUYY8.rst b/Misc/NEWS.d/next/Tools-Demos/2023-09-27-23-31-54.gh-issue-109991.sUUYY8.rst deleted file mode 100644 index 13c1163ab53443..00000000000000 --- a/Misc/NEWS.d/next/Tools-Demos/2023-09-27-23-31-54.gh-issue-109991.sUUYY8.rst +++ /dev/null @@ -1,2 +0,0 @@ -Update GitHub CI workflows to use OpenSSL 3.0.11 and multissltests to use -1.1.1w, 3.0.11, and 3.1.3. diff --git a/Misc/NEWS.d/next/Windows/2023-09-05-10-08-47.gh-issue-107565.CIMftz.rst b/Misc/NEWS.d/next/Windows/2023-09-05-10-08-47.gh-issue-107565.CIMftz.rst deleted file mode 100644 index 024a58299caed9..00000000000000 --- a/Misc/NEWS.d/next/Windows/2023-09-05-10-08-47.gh-issue-107565.CIMftz.rst +++ /dev/null @@ -1 +0,0 @@ -Update Windows build to use OpenSSL 3.0.10. diff --git a/Misc/NEWS.d/next/Windows/2023-09-28-17-09-23.gh-issue-109991.CIMftz.rst b/Misc/NEWS.d/next/Windows/2023-09-28-17-09-23.gh-issue-109991.CIMftz.rst deleted file mode 100644 index ee988f90863426..00000000000000 --- a/Misc/NEWS.d/next/Windows/2023-09-28-17-09-23.gh-issue-109991.CIMftz.rst +++ /dev/null @@ -1 +0,0 @@ -Update Windows build to use OpenSSL 3.0.11. diff --git a/Misc/NEWS.d/next/macOS/2023-09-27-22-35-22.gh-issue-109991.-xJzaF.rst b/Misc/NEWS.d/next/macOS/2023-09-27-22-35-22.gh-issue-109991.-xJzaF.rst deleted file mode 100644 index 8d369988274f28..00000000000000 --- a/Misc/NEWS.d/next/macOS/2023-09-27-22-35-22.gh-issue-109991.-xJzaF.rst +++ /dev/null @@ -1 +0,0 @@ -Update macOS installer to use OpenSSL 3.0.11. diff --git a/README.rst b/README.rst index c26c85db42cc6c..1b82fa67d32da4 100644 --- a/README.rst +++ b/README.rst @@ -1,4 +1,4 @@ -This is Python version 3.11.5 +This is Python version 3.11.6 ============================= .. image:: https://github.com/python/cpython/workflows/Tests/badge.svg From 914ffb40d0d3ced2ed11e8f0edbc4dc4c569ccf6 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Mon, 2 Oct 2023 17:23:15 +0100 Subject: [PATCH 399/632] Post 3.11.6 --- Include/patchlevel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/patchlevel.h b/Include/patchlevel.h index 70d71c11377ede..58a3297d17b872 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -23,7 +23,7 @@ #define PY_RELEASE_SERIAL 0 /* Version as a string */ -#define PY_VERSION "3.11.6" +#define PY_VERSION "3.11.6+" /*--end constants--*/ /* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2. From 981696e342f41dc3f8c2ea94a79475c931d2c7cf Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 2 Oct 2023 21:43:05 +0200 Subject: [PATCH 400/632] [3.11] gh-108494: Document how to add a project in PCbuild/readme.txt (#110077) (#110232) gh-108494: Document how to add a project in PCbuild/readme.txt (#110077) (cherry picked from commit 6387b5313c60c1403785b2245db33372476ac304) --- PCbuild/readme.txt | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/PCbuild/readme.txt b/PCbuild/readme.txt index 092b2cc8c1a7f7..d6149a3cedeaa3 100644 --- a/PCbuild/readme.txt +++ b/PCbuild/readme.txt @@ -291,3 +291,31 @@ project, with some projects overriding certain specific values. The GUI doesn't always reflect the correct settings and may confuse the user with false information, especially for settings that automatically adapt for different configurations. + +Add a new project +----------------- + +For example, add a new _testclinic_limited project to build a new +_testclinic_limited extension, the file Modules/_testclinic_limited.c: + +* In PCbuild/, copy _testclinic.vcxproj to _testclinic_limited.vcxproj, + replace RootNamespace value with `_testclinic_limited`, replace + `_asyncio.c` with `_testclinic_limited.c`. +* Open Visual Studio, open PCbuild\pcbuild.sln solution, add the + PCbuild\_testclinic_limited.vcxproj project to the solution ("add existing + project). +* Add a dependency on the python project to the new _testclinic_limited + project. +* Save and exit Visual Studio. +* Add `;_testclinic_limited` to `` in + PCbuild\pcbuild.proj. +* Update "exts" in Tools\msi\lib\lib_files.wxs file or in + Tools\msi\test\test_files.wxs file (for tests). +* PC\layout\main.py needs updating if you add a test-only extension whose name + doesn't start with "_test". +* Add the extension to PCbuild\readme.txt (this file). +* Build Python from scratch (clean the solution) to check that the new project + is built successfully. +* Ensure the new .vcxproj and .vcxproj.filters files are added to your commit, + as well as the changes to pcbuild.sln, pcbuild.proj and any other modified + files. From bf6843e91f70a9956acb8a1cdd0564d3598b9a6e Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 2 Oct 2023 18:36:55 -0700 Subject: [PATCH 401/632] [3.11] [3.12] gh-109649: Enhance os.cpu_count() documentation (GH-110169) (#110226) [3.12] gh-109649: Enhance os.cpu_count() documentation (GH-110169) * gh-109649: Enhance os.cpu_count() documentation * Doc: Specify that os.cpu_count() counts *logicial* CPUs. * Doc: Specify that os.sched_getaffinity(0) is related to the calling thread. * Fix test_posix.test_sched_getaffinity(): restore the old CPU mask when the test completes! * Restore removed text (cherry picked from commit 5245b97e132ae071e2b574224e0788cab62fdcc9) Co-authored-by: Victor Stinner --- Doc/library/os.rst | 16 +++++++++------- Lib/test/test_posix.py | 1 + 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index df01963e54a5e6..087a01d2d9efc1 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -4872,8 +4872,10 @@ operating system. .. function:: sched_getaffinity(pid, /) - Return the set of CPUs the process with PID *pid* (or the current process - if zero) is restricted to. + Return the set of CPUs the process with PID *pid* is restricted to. + + If *pid* is zero, return the set of CPUs the calling thread of the current + process is restricted to. .. _os-path: @@ -4914,12 +4916,12 @@ Miscellaneous System Information .. function:: cpu_count() - Return the number of CPUs in the system. Returns ``None`` if undetermined. - - This number is not equivalent to the number of CPUs the current process can - use. The number of usable CPUs can be obtained with - ``len(os.sched_getaffinity(0))`` + Return the number of logical CPUs in the system. Returns ``None`` if + undetermined. + This number is not equivalent to the number of logical CPUs the current + process can use. ``len(os.sched_getaffinity(0))`` gets the number of logical + CPUs the calling thread of the current process is restricted to .. versionadded:: 3.4 diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index e643d8e5a4ce63..e4956b55a07e0a 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -1205,6 +1205,7 @@ def test_sched_getaffinity(self): @requires_sched_affinity def test_sched_setaffinity(self): mask = posix.sched_getaffinity(0) + self.addCleanup(posix.sched_setaffinity, 0, list(mask)) if len(mask) > 1: # Empty masks are forbidden mask.pop() From 0c6ae623e5c0f2afb28ccc6a1b39b70097d27553 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 2 Oct 2023 22:36:35 -0700 Subject: [PATCH 402/632] [3.11] Remove unused Misc/requirements-test.txt (GH-110240) (#110254) Co-authored-by: Hugo van Kemenade --- Misc/requirements-test.txt | 1 - 1 file changed, 1 deletion(-) delete mode 100644 Misc/requirements-test.txt diff --git a/Misc/requirements-test.txt b/Misc/requirements-test.txt deleted file mode 100644 index 60e7ed20a3d510..00000000000000 --- a/Misc/requirements-test.txt +++ /dev/null @@ -1 +0,0 @@ -tzdata==2020.3 From a151afe7199553415c8c4ea6406b0d393afb119b Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 3 Oct 2023 06:18:08 -0700 Subject: [PATCH 403/632] [3.11] Bump various dependencies in `Doc/requirements-oldest-sphinx.txt` (GH-110278) (#110281) Bump various dependencies in `Doc/requirements-oldest-sphinx.txt` (GH-110278) This resolves a Dependabot security alert on the repository for urllib3==2.0.4. (cherry picked from commit f1663a492e14c80c30cb9741fdc36fa221d5e30a) Co-authored-by: Alex Waygood --- Doc/requirements-oldest-sphinx.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Doc/requirements-oldest-sphinx.txt b/Doc/requirements-oldest-sphinx.txt index d3ef5bc17650ae..5de739fc10b085 100644 --- a/Doc/requirements-oldest-sphinx.txt +++ b/Doc/requirements-oldest-sphinx.txt @@ -13,16 +13,16 @@ python-docs-theme>=2022.1 # Sphinx 4.2 comes from ``needs_sphinx = '4.2'`` in ``Doc/conf.py``. alabaster==0.7.13 -Babel==2.12.1 +Babel==2.13.0 certifi==2023.7.22 -charset-normalizer==3.2.0 +charset-normalizer==3.3.0 colorama==0.4.6 -docutils==0.16 +docutils==0.17.1 idna==3.4 imagesize==1.4.1 -Jinja2==2.11.3 -MarkupSafe==1.1.1 -packaging==23.1 +Jinja2==3.1.2 +MarkupSafe==2.1.3 +packaging==23.2 Pygments==2.16.1 requests==2.31.0 snowballstemmer==2.2.0 @@ -33,4 +33,4 @@ sphinxcontrib-htmlhelp==2.0.1 sphinxcontrib-jsmath==1.0.1 sphinxcontrib-qthelp==1.0.3 sphinxcontrib-serializinghtml==1.1.5 -urllib3==2.0.4 +urllib3==2.0.6 From 3079aa3f5d4f54a05b9b67cb60625e45581f7ade Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Tue, 3 Oct 2023 16:09:06 +0100 Subject: [PATCH 404/632] [3.11] Enable ruff on `Lib/test/test_typing.py` (#110179) (#110290) --- .pre-commit-config.yaml | 2 +- Lib/test/.ruff.toml | 1 - Lib/test/test_typing.py | 80 ++++++++++++++++++++--------------------- 3 files changed, 41 insertions(+), 42 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 980b616559ff32..3cff5658ba4e81 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.288 + rev: v0.0.292 hooks: - id: ruff name: Run Ruff on Lib/test/ diff --git a/Lib/test/.ruff.toml b/Lib/test/.ruff.toml index 0afa0fe36b0ce4..bd46f98512d6e2 100644 --- a/Lib/test/.ruff.toml +++ b/Lib/test/.ruff.toml @@ -24,7 +24,6 @@ extend-exclude = [ "test_pkg.py", "test_subclassinit.py", "test_tokenize.py", - "test_typing.py", "test_yield_from.py", "time_hashlib.py", ] diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 149c29283d4bae..177155a6365662 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -183,7 +183,7 @@ def test_cannot_subclass(self): class A(self.bottom_type): pass with self.assertRaises(TypeError): - class A(type(self.bottom_type)): + class B(type(self.bottom_type)): pass def test_cannot_instantiate(self): @@ -279,7 +279,7 @@ def test_cannot_subclass(self): class C(type(Self)): pass with self.assertRaises(TypeError): - class C(Self): + class D(Self): pass def test_cannot_init(self): @@ -335,7 +335,7 @@ def test_cannot_subclass(self): class C(type(LiteralString)): pass with self.assertRaises(TypeError): - class C(LiteralString): + class D(LiteralString): pass def test_cannot_init(self): @@ -457,7 +457,7 @@ class V(TypeVar('T')): def test_cannot_subclass_var_itself(self): with self.assertRaises(TypeError): - class V(TypeVar): + class W(TypeVar): pass def test_cannot_instantiate_vars(self): @@ -1185,20 +1185,20 @@ class C(TypeVarTuple): pass def test_cannot_subclass_instance(self): Ts = TypeVarTuple('Ts') with self.assertRaises(TypeError): - class C(Ts): pass + class D(Ts): pass with self.assertRaisesRegex(TypeError, CANNOT_SUBCLASS_TYPE): - class C(type(Unpack)): pass + class E(type(Unpack)): pass with self.assertRaisesRegex(TypeError, CANNOT_SUBCLASS_TYPE): - class C(type(*Ts)): pass + class F(type(*Ts)): pass with self.assertRaisesRegex(TypeError, CANNOT_SUBCLASS_TYPE): - class C(type(Unpack[Ts])): pass + class G(type(Unpack[Ts])): pass with self.assertRaisesRegex(TypeError, r'Cannot subclass typing\.Unpack'): - class C(Unpack): pass + class H(Unpack): pass with self.assertRaisesRegex(TypeError, r'Cannot subclass \*Ts'): - class C(*Ts): pass + class I(*Ts): pass with self.assertRaisesRegex(TypeError, r'Cannot subclass \*Ts'): - class C(Unpack[Ts]): pass + class J(Unpack[Ts]): pass def test_variadic_class_args_are_correct(self): T = TypeVar('T') @@ -1372,12 +1372,12 @@ def test_variadic_class_with_duplicate_typevartuples_fails(self): with self.assertRaises(TypeError): class C(Generic[*Ts1, *Ts1]): pass with self.assertRaises(TypeError): - class C(Generic[Unpack[Ts1], Unpack[Ts1]]): pass + class D(Generic[Unpack[Ts1], Unpack[Ts1]]): pass with self.assertRaises(TypeError): - class C(Generic[*Ts1, *Ts2, *Ts1]): pass + class E(Generic[*Ts1, *Ts2, *Ts1]): pass with self.assertRaises(TypeError): - class C(Generic[Unpack[Ts1], Unpack[Ts2], Unpack[Ts1]]): pass + class F(Generic[Unpack[Ts1], Unpack[Ts2], Unpack[Ts1]]): pass def test_type_concatenation_in_variadic_class_argument_list_succeeds(self): Ts = TypeVarTuple('Ts') @@ -1744,10 +1744,10 @@ def test_cannot_subclass(self): class C(Union): pass with self.assertRaises(TypeError): - class C(type(Union)): + class D(type(Union)): pass with self.assertRaises(TypeError): - class C(Union[int, str]): + class E(Union[int, str]): pass def test_cannot_instantiate(self): @@ -2454,10 +2454,10 @@ class BP(Protocol): pass class P(C, Protocol): pass with self.assertRaises(TypeError): - class P(Protocol, C): + class Q(Protocol, C): pass with self.assertRaises(TypeError): - class P(BP, C, Protocol): + class R(BP, C, Protocol): pass class D(BP, C): pass @@ -2722,7 +2722,7 @@ class NotAProtocolButAnImplicitSubclass3: meth: Callable[[], None] meth2: Callable[[int, str], bool] def meth(self): pass - def meth(self, x, y): return True + def meth2(self, x, y): return True self.assertNotIsSubclass(AnnotatedButNotAProtocol, CallableMembersProto) self.assertIsSubclass(NotAProtocolButAnImplicitSubclass, CallableMembersProto) @@ -3208,11 +3208,11 @@ def test_protocols_bad_subscripts(self): with self.assertRaises(TypeError): class P(Protocol[T, T]): pass with self.assertRaises(TypeError): - class P(Protocol[int]): pass + class Q(Protocol[int]): pass with self.assertRaises(TypeError): - class P(Protocol[T], Protocol[S]): pass + class R(Protocol[T], Protocol[S]): pass with self.assertRaises(TypeError): - class P(typing.Mapping[T, S], Protocol[T]): pass + class S(typing.Mapping[T, S], Protocol[T]): pass def test_generic_protocols_repr(self): T = TypeVar('T') @@ -3553,12 +3553,12 @@ class NewGeneric(Generic): ... with self.assertRaises(TypeError): class MyGeneric(Generic[T], Generic[S]): ... with self.assertRaises(TypeError): - class MyGeneric(List[T], Generic[S]): ... + class MyGeneric2(List[T], Generic[S]): ... with self.assertRaises(TypeError): Generic[()] - class C(Generic[T]): pass + class D(Generic[T]): pass with self.assertRaises(TypeError): - C[()] + D[()] def test_init(self): T = TypeVar('T') @@ -4234,7 +4234,7 @@ class Test(Generic[T], Final): class Subclass(Test): pass with self.assertRaises(FinalException): - class Subclass(Test[int]): + class Subclass2(Test[int]): pass def test_nested(self): @@ -4472,7 +4472,7 @@ def test_cannot_subclass(self): class C(type(ClassVar)): pass with self.assertRaises(TypeError): - class C(type(ClassVar[int])): + class D(type(ClassVar[int])): pass def test_cannot_init(self): @@ -4514,7 +4514,7 @@ def test_cannot_subclass(self): class C(type(Final)): pass with self.assertRaises(TypeError): - class C(type(Final[int])): + class D(type(Final[int])): pass def test_cannot_init(self): @@ -6514,15 +6514,15 @@ class A: class X(NamedTuple, A): x: int with self.assertRaises(TypeError): - class X(NamedTuple, tuple): + class Y(NamedTuple, tuple): x: int with self.assertRaises(TypeError): - class X(NamedTuple, NamedTuple): + class Z(NamedTuple, NamedTuple): x: int - class A(NamedTuple): + class B(NamedTuple): x: int with self.assertRaises(TypeError): - class X(NamedTuple, A): + class C(NamedTuple, B): y: str def test_generic(self): @@ -7108,13 +7108,13 @@ def test_cannot_subclass(self): class C(type(Required)): pass with self.assertRaises(TypeError): - class C(type(Required[int])): + class D(type(Required[int])): pass with self.assertRaises(TypeError): - class C(Required): + class E(Required): pass with self.assertRaises(TypeError): - class C(Required[int]): + class F(Required[int]): pass def test_cannot_init(self): @@ -7154,13 +7154,13 @@ def test_cannot_subclass(self): class C(type(NotRequired)): pass with self.assertRaises(TypeError): - class C(type(NotRequired[int])): + class D(type(NotRequired[int])): pass with self.assertRaises(TypeError): - class C(NotRequired): + class E(NotRequired): pass with self.assertRaises(TypeError): - class C(NotRequired[int]): + class F(NotRequired[int]): pass def test_cannot_init(self): @@ -7622,7 +7622,7 @@ class C(TypeAlias): pass with self.assertRaises(TypeError): - class C(type(TypeAlias)): + class D(type(TypeAlias)): pass def test_repr(self): @@ -8078,7 +8078,7 @@ def test_cannot_subclass(self): class C(type(TypeGuard)): pass with self.assertRaises(TypeError): - class C(type(TypeGuard[int])): + class D(type(TypeGuard[int])): pass def test_cannot_init(self): From 6a6e8871ba0dcf4332093e198a876957651768ed Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 3 Oct 2023 09:02:04 -0700 Subject: [PATCH 405/632] [3.11] gh-109234: Hint to contextlib.closing in sqlite3 context manager docs (GH-109322) (#110294) (cherry picked from commit 4227bfa8b273207a2b882f7d69c8ac49c3d2b57d) Co-authored-by: Lincoln <71312724+Lincoln-developer@users.noreply.github.com> Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Erlend E. Aasland --- Doc/library/sqlite3.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index f3bcfb5a28f9a3..c85f4fc49c070e 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -2195,9 +2195,9 @@ If there is no open transaction upon leaving the body of the ``with`` statement, the context manager is a no-op. .. note:: - The context manager neither implicitly opens a new transaction - nor closes the connection. + nor closes the connection. If you need a closing context manager, consider + using :meth:`contextlib.closing`. .. testcode:: From 4e44927a0e80e85f68f26dcba06702022bd9f884 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 3 Oct 2023 15:27:56 -0700 Subject: [PATCH 406/632] [3.11] gh-109917: Fix test instability in test_concurrent_futures (GH-110306) (#110316) gh-109917: Fix test instability in test_concurrent_futures (GH-110306) The test had an instability issue due to the ordering of the dummy queue operation and the real wakeup pipe operations. Both primitives are thread safe but not done atomically as a single update and may interleave arbitrarily. With the old order of operations this can lead to an incorrect state where the dummy queue is full but the wakeup pipe is empty. By swapping the order in clear() I think this can no longer happen in any possible operation interleaving (famous last words). (cherry picked from commit a376a72bd92cd7c9930467dd1aba40045fb75e3b) Co-authored-by: elfstrom --- Lib/test/test_concurrent_futures/test_deadlock.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_concurrent_futures/test_deadlock.py b/Lib/test/test_concurrent_futures/test_deadlock.py index 7ede95d5ed3422..1db4cd009926b9 100644 --- a/Lib/test/test_concurrent_futures/test_deadlock.py +++ b/Lib/test/test_concurrent_futures/test_deadlock.py @@ -284,11 +284,12 @@ def wakeup(self): super().wakeup() def clear(self): + super().clear() try: while True: self._dummy_queue.get_nowait() except queue.Empty: - super().clear() + pass with (unittest.mock.patch.object(futures.process._ExecutorManagerThread, 'run', mock_run), From 5039db7f9b2213634cd053a7f4a1e6000edf8e2f Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 4 Oct 2023 02:01:19 -0700 Subject: [PATCH 407/632] [3.11] gh-110267: Add tests for pickling and copying PyStructSequence objects (GH-110272) (GH-110284) (cherry picked from commit 2d4865d775123e8889c7a79fc49b4bf627176c4b) Co-authored-by: Xuehai Pan --- Lib/test/test_structseq.py | 75 ++++++++++++++++++- ...-10-03-10-54-09.gh-issue-110267.O-c47G.rst | 2 + 2 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2023-10-03-10-54-09.gh-issue-110267.O-c47G.rst diff --git a/Lib/test/test_structseq.py b/Lib/test/test_structseq.py index a9fe193028ebe4..c6c0afaf077acc 100644 --- a/Lib/test/test_structseq.py +++ b/Lib/test/test_structseq.py @@ -1,4 +1,6 @@ +import copy import os +import pickle import time import unittest @@ -106,9 +108,78 @@ def __len__(self): self.assertRaises(Exc, time.struct_time, C()) - def test_reduce(self): + def test_pickling(self): t = time.gmtime() - x = t.__reduce__() + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + p = pickle.dumps(t, proto) + t2 = pickle.loads(p) + self.assertEqual(t2.__class__, t.__class__) + self.assertEqual(t2, t) + self.assertEqual(t2.tm_year, t.tm_year) + self.assertEqual(t2.tm_zone, t.tm_zone) + + def test_pickling_with_unnamed_fields(self): + assert os.stat_result.n_unnamed_fields > 0 + + r = os.stat_result(range(os.stat_result.n_sequence_fields), + {'st_atime': 1.0, 'st_atime_ns': 2.0}) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + p = pickle.dumps(r, proto) + r2 = pickle.loads(p) + self.assertEqual(r2.__class__, r.__class__) + self.assertEqual(r2, r) + self.assertEqual(r2.st_mode, r.st_mode) + self.assertEqual(r2.st_atime, r.st_atime) + self.assertEqual(r2.st_atime_ns, r.st_atime_ns) + + def test_copying(self): + n_fields = time.struct_time.n_fields + t = time.struct_time([[i] for i in range(n_fields)]) + + t2 = copy.copy(t) + self.assertEqual(t2.__class__, t.__class__) + self.assertEqual(t2, t) + self.assertEqual(t2.tm_year, t.tm_year) + self.assertEqual(t2.tm_zone, t.tm_zone) + self.assertIs(t2[0], t[0]) + self.assertIs(t2.tm_year, t.tm_year) + + t3 = copy.deepcopy(t) + self.assertEqual(t3.__class__, t.__class__) + self.assertEqual(t3, t) + self.assertEqual(t3.tm_year, t.tm_year) + self.assertEqual(t3.tm_zone, t.tm_zone) + self.assertIsNot(t3[0], t[0]) + self.assertIsNot(t3.tm_year, t.tm_year) + + def test_copying_with_unnamed_fields(self): + assert os.stat_result.n_unnamed_fields > 0 + + n_sequence_fields = os.stat_result.n_sequence_fields + r = os.stat_result([[i] for i in range(n_sequence_fields)], + {'st_atime': [1.0], 'st_atime_ns': [2.0]}) + + r2 = copy.copy(r) + self.assertEqual(r2.__class__, r.__class__) + self.assertEqual(r2, r) + self.assertEqual(r2.st_mode, r.st_mode) + self.assertEqual(r2.st_atime, r.st_atime) + self.assertEqual(r2.st_atime_ns, r.st_atime_ns) + self.assertIs(r2[0], r[0]) + self.assertIs(r2.st_mode, r.st_mode) + self.assertIs(r2.st_atime, r.st_atime) + self.assertIs(r2.st_atime_ns, r.st_atime_ns) + + r3 = copy.deepcopy(r) + self.assertEqual(r3.__class__, r.__class__) + self.assertEqual(r3, r) + self.assertEqual(r3.st_mode, r.st_mode) + self.assertEqual(r3.st_atime, r.st_atime) + self.assertEqual(r3.st_atime_ns, r.st_atime_ns) + self.assertIsNot(r3[0], r[0]) + self.assertIsNot(r3.st_mode, r.st_mode) + self.assertIsNot(r3.st_atime, r.st_atime) + self.assertIsNot(r3.st_atime_ns, r.st_atime_ns) def test_extended_getslice(self): # Test extended slicing by comparing with list slicing. diff --git a/Misc/NEWS.d/next/Tests/2023-10-03-10-54-09.gh-issue-110267.O-c47G.rst b/Misc/NEWS.d/next/Tests/2023-10-03-10-54-09.gh-issue-110267.O-c47G.rst new file mode 100644 index 00000000000000..2bae7715cc3d5b --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-10-03-10-54-09.gh-issue-110267.O-c47G.rst @@ -0,0 +1,2 @@ +Add tests for pickling and copying PyStructSequence objects. +Patched by Xuehai Pan. From 6c98c734c7bcef2eee870bf4e5b4326bb81b320c Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 4 Oct 2023 12:53:28 +0200 Subject: [PATCH 408/632] =?UTF-8?q?[3.11]=20gh-109972:=20Split=20test=5Fgd?= =?UTF-8?q?b.py=20into=20test=5Fgdb=20package=20(#109977)=20(=E2=80=A6=20(?= =?UTF-8?q?#110343)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [3.12] gh-109972: Split test_gdb.py into test_gdb package (#109977) (#110339) gh-109972: Split test_gdb.py into test_gdb package (#109977) Split test_gdb.py file into a test_gdb package made of multiple tests, so tests can now be run in parallel. * Create Lib/test/test_gdb/ directory. * Split test_gdb.py into multiple files in Lib/test/test_gdb/ directory. * Move Lib/test/gdb_sample.py to Lib/test/test_gdb/ directory. Update get_sample_script(): use __file__ to locate gdb_sample.py. * Move gdb_has_frame_select() and HAS_PYUP_PYDOWN to test_misc.py. * Explicitly skip test_gdb on Windows. Previously, test_gdb was skipped even if gdb was available because of gdb_has_frame_select(). (cherry picked from commit 8f324b7ecd2df3036fab098c4c8ac185ac07b277) (cherry picked from commit e7a61d34b7897ac6cff53add2ec03309a5ff8350) --- Lib/test/libregrtest/runtest.py | 1 + Lib/test/test_gdb.py | 1066 ----------------- Lib/test/test_gdb/__init__.py | 10 + Lib/test/{ => test_gdb}/gdb_sample.py | 2 +- Lib/test/test_gdb/test_backtrace.py | 134 +++ Lib/test/test_gdb/test_cfunction.py | 83 ++ Lib/test/test_gdb/test_misc.py | 188 +++ Lib/test/test_gdb/test_pretty_print.py | 400 +++++++ Lib/test/test_gdb/util.py | 304 +++++ Makefile.pre.in | 1 + ...-09-28-12-25-19.gh-issue-109972.GYnwIP.rst | 2 + 11 files changed, 1124 insertions(+), 1067 deletions(-) delete mode 100644 Lib/test/test_gdb.py create mode 100644 Lib/test/test_gdb/__init__.py rename Lib/test/{ => test_gdb}/gdb_sample.py (75%) create mode 100644 Lib/test/test_gdb/test_backtrace.py create mode 100644 Lib/test/test_gdb/test_cfunction.py create mode 100644 Lib/test/test_gdb/test_misc.py create mode 100644 Lib/test/test_gdb/test_pretty_print.py create mode 100644 Lib/test/test_gdb/util.py create mode 100644 Misc/NEWS.d/next/Tests/2023-09-28-12-25-19.gh-issue-109972.GYnwIP.rst diff --git a/Lib/test/libregrtest/runtest.py b/Lib/test/libregrtest/runtest.py index 000a4aa75c44a0..f37093b81cf284 100644 --- a/Lib/test/libregrtest/runtest.py +++ b/Lib/test/libregrtest/runtest.py @@ -143,6 +143,7 @@ def set_env_changed(self): "test_asyncio", "test_concurrent_futures", "test_future_stmt", + "test_gdb", "test_multiprocessing_fork", "test_multiprocessing_forkserver", "test_multiprocessing_spawn", diff --git a/Lib/test/test_gdb.py b/Lib/test/test_gdb.py deleted file mode 100644 index 5d042a45587a58..00000000000000 --- a/Lib/test/test_gdb.py +++ /dev/null @@ -1,1066 +0,0 @@ -# Verify that gdb can pretty-print the various PyObject* types -# -# The code for testing gdb was adapted from similar work in Unladen Swallow's -# Lib/test/test_jit_gdb.py - -import os -import platform -import re -import subprocess -import sys -import sysconfig -import textwrap -import unittest - -from test import support -from test.support import findfile, python_is_optimized - -def get_gdb_version(): - try: - cmd = ["gdb", "-nx", "--version"] - proc = subprocess.Popen(cmd, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True) - with proc: - version, stderr = proc.communicate() - - if proc.returncode: - raise Exception(f"Command {' '.join(cmd)!r} failed " - f"with exit code {proc.returncode}: " - f"stdout={version!r} stderr={stderr!r}") - except OSError: - # This is what "no gdb" looks like. There may, however, be other - # errors that manifest this way too. - raise unittest.SkipTest("Couldn't find gdb on the path") - - # Regex to parse: - # 'GNU gdb (GDB; SUSE Linux Enterprise 12) 7.7\n' -> 7.7 - # 'GNU gdb (GDB) Fedora 7.9.1-17.fc22\n' -> 7.9 - # 'GNU gdb 6.1.1 [FreeBSD]\n' -> 6.1 - # 'GNU gdb (GDB) Fedora (7.5.1-37.fc18)\n' -> 7.5 - # 'HP gdb 6.7 for HP Itanium (32 or 64 bit) and target HP-UX 11iv2 and 11iv3.\n' -> 6.7 - match = re.search(r"^(?:GNU|HP) gdb.*?\b(\d+)\.(\d+)", version) - if match is None: - raise Exception("unable to parse GDB version: %r" % version) - return (version, int(match.group(1)), int(match.group(2))) - -gdb_version, gdb_major_version, gdb_minor_version = get_gdb_version() -if gdb_major_version < 7: - raise unittest.SkipTest("gdb versions before 7.0 didn't support python " - "embedding. Saw %s.%s:\n%s" - % (gdb_major_version, gdb_minor_version, - gdb_version)) - -if not sysconfig.is_python_build(): - raise unittest.SkipTest("test_gdb only works on source builds at the moment.") - -if ((sysconfig.get_config_var('PGO_PROF_USE_FLAG') or 'xxx') in - (sysconfig.get_config_var('PY_CORE_CFLAGS') or '')): - raise unittest.SkipTest("test_gdb is not reliable on PGO builds") - -# Location of custom hooks file in a repository checkout. -checkout_hook_path = os.path.join(os.path.dirname(sys.executable), - 'python-gdb.py') - -PYTHONHASHSEED = '123' - - -def cet_protection(): - cflags = sysconfig.get_config_var('CFLAGS') - if not cflags: - return False - flags = cflags.split() - # True if "-mcet -fcf-protection" options are found, but false - # if "-fcf-protection=none" or "-fcf-protection=return" is found. - return (('-mcet' in flags) - and any((flag.startswith('-fcf-protection') - and not flag.endswith(("=none", "=return"))) - for flag in flags)) - -# Control-flow enforcement technology -CET_PROTECTION = cet_protection() - - -def run_gdb(*args, **env_vars): - """Runs gdb in --batch mode with the additional arguments given by *args. - - Returns its (stdout, stderr) decoded from utf-8 using the replace handler. - """ - if env_vars: - env = os.environ.copy() - env.update(env_vars) - else: - env = None - # -nx: Do not execute commands from any .gdbinit initialization files - # (issue #22188) - base_cmd = ('gdb', '--batch', '-nx') - if (gdb_major_version, gdb_minor_version) >= (7, 4): - base_cmd += ('-iex', 'add-auto-load-safe-path ' + checkout_hook_path) - proc = subprocess.Popen(base_cmd + args, - # Redirect stdin to prevent GDB from messing with - # the terminal settings - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - env=env) - with proc: - out, err = proc.communicate() - return out.decode('utf-8', 'replace'), err.decode('utf-8', 'replace') - -# Verify that "gdb" was built with the embedded python support enabled: -gdbpy_version, _ = run_gdb("--eval-command=python import sys; print(sys.version_info)") -if not gdbpy_version: - raise unittest.SkipTest("gdb not built with embedded python support") - -if "major=2" in gdbpy_version: - raise unittest.SkipTest("gdb built with Python 2") - -# Verify that "gdb" can load our custom hooks, as OS security settings may -# disallow this without a customized .gdbinit. -_, gdbpy_errors = run_gdb('--args', sys.executable) -if "auto-loading has been declined" in gdbpy_errors: - msg = "gdb security settings prevent use of custom hooks: " - raise unittest.SkipTest(msg + gdbpy_errors.rstrip()) - -def gdb_has_frame_select(): - # Does this build of gdb have gdb.Frame.select ? - stdout, _ = run_gdb("--eval-command=python print(dir(gdb.Frame))") - m = re.match(r'.*\[(.*)\].*', stdout) - if not m: - raise unittest.SkipTest("Unable to parse output from gdb.Frame.select test") - gdb_frame_dir = m.group(1).split(', ') - return "'select'" in gdb_frame_dir - -HAS_PYUP_PYDOWN = gdb_has_frame_select() - -BREAKPOINT_FN='builtin_id' - -@unittest.skipIf(support.PGO, "not useful for PGO") -class DebuggerTests(unittest.TestCase): - - """Test that the debugger can debug Python.""" - - def get_stack_trace(self, source=None, script=None, - breakpoint=BREAKPOINT_FN, - cmds_after_breakpoint=None, - import_site=False, - ignore_stderr=False): - ''' - Run 'python -c SOURCE' under gdb with a breakpoint. - - Support injecting commands after the breakpoint is reached - - Returns the stdout from gdb - - cmds_after_breakpoint: if provided, a list of strings: gdb commands - ''' - # We use "set breakpoint pending yes" to avoid blocking with a: - # Function "foo" not defined. - # Make breakpoint pending on future shared library load? (y or [n]) - # error, which typically happens python is dynamically linked (the - # breakpoints of interest are to be found in the shared library) - # When this happens, we still get: - # Function "textiowrapper_write" not defined. - # emitted to stderr each time, alas. - - # Initially I had "--eval-command=continue" here, but removed it to - # avoid repeated print breakpoints when traversing hierarchical data - # structures - - # Generate a list of commands in gdb's language: - commands = ['set breakpoint pending yes', - 'break %s' % breakpoint, - - # The tests assume that the first frame of printed - # backtrace will not contain program counter, - # that is however not guaranteed by gdb - # therefore we need to use 'set print address off' to - # make sure the counter is not there. For example: - # #0 in PyObject_Print ... - # is assumed, but sometimes this can be e.g. - # #0 0x00003fffb7dd1798 in PyObject_Print ... - 'set print address off', - - 'run'] - - # GDB as of 7.4 onwards can distinguish between the - # value of a variable at entry vs current value: - # http://sourceware.org/gdb/onlinedocs/gdb/Variables.html - # which leads to the selftests failing with errors like this: - # AssertionError: 'v@entry=()' != '()' - # Disable this: - if (gdb_major_version, gdb_minor_version) >= (7, 4): - commands += ['set print entry-values no'] - - if cmds_after_breakpoint: - if CET_PROTECTION: - # bpo-32962: When Python is compiled with -mcet - # -fcf-protection, function arguments are unusable before - # running the first instruction of the function entry point. - # The 'next' command makes the required first step. - commands += ['next'] - commands += cmds_after_breakpoint - else: - commands += ['backtrace'] - - # print commands - - # Use "commands" to generate the arguments with which to invoke "gdb": - args = ['--eval-command=%s' % cmd for cmd in commands] - args += ["--args", - sys.executable] - args.extend(subprocess._args_from_interpreter_flags()) - - if not import_site: - # -S suppresses the default 'import site' - args += ["-S"] - - if source: - args += ["-c", source] - elif script: - args += [script] - - # Use "args" to invoke gdb, capturing stdout, stderr: - out, err = run_gdb(*args, PYTHONHASHSEED=PYTHONHASHSEED) - - if not ignore_stderr: - for line in err.splitlines(): - print(line, file=sys.stderr) - - # bpo-34007: Sometimes some versions of the shared libraries that - # are part of the traceback are compiled in optimised mode and the - # Program Counter (PC) is not present, not allowing gdb to walk the - # frames back. When this happens, the Python bindings of gdb raise - # an exception, making the test impossible to succeed. - if "PC not saved" in err: - raise unittest.SkipTest("gdb cannot walk the frame object" - " because the Program Counter is" - " not present") - - # bpo-40019: Skip the test if gdb failed to read debug information - # because the Python binary is optimized. - for pattern in ( - '(frame information optimized out)', - 'Unable to read information on python frame', - # gh-91960: On Python built with "clang -Og", gdb gets - # "frame=" for _PyEval_EvalFrameDefault() parameter - '(unable to read python frame information)', - # gh-104736: On Python built with "clang -Og" on ppc64le, - # "py-bt" displays a truncated or not traceback, but "where" - # logs this error message: - 'Backtrace stopped: frame did not save the PC', - # gh-104736: When "bt" command displays something like: - # "#1 0x0000000000000000 in ?? ()", the traceback is likely - # truncated or wrong. - ' ?? ()', - ): - if pattern in out: - raise unittest.SkipTest(f"{pattern!r} found in gdb output") - - return out - - def get_gdb_repr(self, source, - cmds_after_breakpoint=None, - import_site=False): - # Given an input python source representation of data, - # run "python -c'id(DATA)'" under gdb with a breakpoint on - # builtin_id and scrape out gdb's representation of the "op" - # parameter, and verify that the gdb displays the same string - # - # Verify that the gdb displays the expected string - # - # For a nested structure, the first time we hit the breakpoint will - # give us the top-level structure - - # NOTE: avoid decoding too much of the traceback as some - # undecodable characters may lurk there in optimized mode - # (issue #19743). - cmds_after_breakpoint = cmds_after_breakpoint or ["backtrace 1"] - gdb_output = self.get_stack_trace(source, breakpoint=BREAKPOINT_FN, - cmds_after_breakpoint=cmds_after_breakpoint, - import_site=import_site) - # gdb can insert additional '\n' and space characters in various places - # in its output, depending on the width of the terminal it's connected - # to (using its "wrap_here" function) - m = re.search( - # Match '#0 builtin_id(self=..., v=...)' - r'#0\s+builtin_id\s+\(self\=.*,\s+v=\s*(.*?)?\)' - # Match ' at Python/bltinmodule.c'. - # bpo-38239: builtin_id() is defined in Python/bltinmodule.c, - # but accept any "Directory\file.c" to support Link Time - # Optimization (LTO). - r'\s+at\s+\S*[A-Za-z]+/[A-Za-z0-9_-]+\.c', - gdb_output, re.DOTALL) - if not m: - self.fail('Unexpected gdb output: %r\n%s' % (gdb_output, gdb_output)) - return m.group(1), gdb_output - - def assertEndsWith(self, actual, exp_end): - '''Ensure that the given "actual" string ends with "exp_end"''' - self.assertTrue(actual.endswith(exp_end), - msg='%r did not end with %r' % (actual, exp_end)) - - def assertMultilineMatches(self, actual, pattern): - m = re.match(pattern, actual, re.DOTALL) - if not m: - self.fail(msg='%r did not match %r' % (actual, pattern)) - - def get_sample_script(self): - return findfile('gdb_sample.py') - -class PrettyPrintTests(DebuggerTests): - def test_getting_backtrace(self): - gdb_output = self.get_stack_trace('id(42)') - self.assertTrue(BREAKPOINT_FN in gdb_output) - - def assertGdbRepr(self, val, exp_repr=None): - # Ensure that gdb's rendering of the value in a debugged process - # matches repr(value) in this process: - gdb_repr, gdb_output = self.get_gdb_repr('id(' + ascii(val) + ')') - if not exp_repr: - exp_repr = repr(val) - self.assertEqual(gdb_repr, exp_repr, - ('%r did not equal expected %r; full output was:\n%s' - % (gdb_repr, exp_repr, gdb_output))) - - @support.requires_resource('cpu') - def test_int(self): - 'Verify the pretty-printing of various int values' - self.assertGdbRepr(42) - self.assertGdbRepr(0) - self.assertGdbRepr(-7) - self.assertGdbRepr(1000000000000) - self.assertGdbRepr(-1000000000000000) - - def test_singletons(self): - 'Verify the pretty-printing of True, False and None' - self.assertGdbRepr(True) - self.assertGdbRepr(False) - self.assertGdbRepr(None) - - def test_dicts(self): - 'Verify the pretty-printing of dictionaries' - self.assertGdbRepr({}) - self.assertGdbRepr({'foo': 'bar'}, "{'foo': 'bar'}") - # Python preserves insertion order since 3.6 - self.assertGdbRepr({'foo': 'bar', 'douglas': 42}, "{'foo': 'bar', 'douglas': 42}") - - def test_lists(self): - 'Verify the pretty-printing of lists' - self.assertGdbRepr([]) - self.assertGdbRepr(list(range(5))) - - @support.requires_resource('cpu') - def test_bytes(self): - 'Verify the pretty-printing of bytes' - self.assertGdbRepr(b'') - self.assertGdbRepr(b'And now for something hopefully the same') - self.assertGdbRepr(b'string with embedded NUL here \0 and then some more text') - self.assertGdbRepr(b'this is a tab:\t' - b' this is a slash-N:\n' - b' this is a slash-R:\r' - ) - - self.assertGdbRepr(b'this is byte 255:\xff and byte 128:\x80') - - self.assertGdbRepr(bytes([b for b in range(255)])) - - @support.requires_resource('cpu') - def test_strings(self): - 'Verify the pretty-printing of unicode strings' - # We cannot simply call locale.getpreferredencoding() here, - # as GDB might have been linked against a different version - # of Python with a different encoding and coercion policy - # with respect to PEP 538 and PEP 540. - out, err = run_gdb( - '--eval-command', - 'python import locale; print(locale.getpreferredencoding())') - - encoding = out.rstrip() - if err or not encoding: - raise RuntimeError( - f'unable to determine the preferred encoding ' - f'of embedded Python in GDB: {err}') - - def check_repr(text): - try: - text.encode(encoding) - except UnicodeEncodeError: - self.assertGdbRepr(text, ascii(text)) - else: - self.assertGdbRepr(text) - - self.assertGdbRepr('') - self.assertGdbRepr('And now for something hopefully the same') - self.assertGdbRepr('string with embedded NUL here \0 and then some more text') - - # Test printing a single character: - # U+2620 SKULL AND CROSSBONES - check_repr('\u2620') - - # Test printing a Japanese unicode string - # (I believe this reads "mojibake", using 3 characters from the CJK - # Unified Ideographs area, followed by U+3051 HIRAGANA LETTER KE) - check_repr('\u6587\u5b57\u5316\u3051') - - # Test a character outside the BMP: - # U+1D121 MUSICAL SYMBOL C CLEF - # This is: - # UTF-8: 0xF0 0x9D 0x84 0xA1 - # UTF-16: 0xD834 0xDD21 - check_repr(chr(0x1D121)) - - def test_tuples(self): - 'Verify the pretty-printing of tuples' - self.assertGdbRepr(tuple(), '()') - self.assertGdbRepr((1,), '(1,)') - self.assertGdbRepr(('foo', 'bar', 'baz')) - - @support.requires_resource('cpu') - def test_sets(self): - 'Verify the pretty-printing of sets' - if (gdb_major_version, gdb_minor_version) < (7, 3): - self.skipTest("pretty-printing of sets needs gdb 7.3 or later") - self.assertGdbRepr(set(), "set()") - self.assertGdbRepr(set(['a']), "{'a'}") - # PYTHONHASHSEED is need to get the exact frozenset item order - if not sys.flags.ignore_environment: - self.assertGdbRepr(set(['a', 'b']), "{'a', 'b'}") - self.assertGdbRepr(set([4, 5, 6]), "{4, 5, 6}") - - # Ensure that we handle sets containing the "dummy" key value, - # which happens on deletion: - gdb_repr, gdb_output = self.get_gdb_repr('''s = set(['a','b']) -s.remove('a') -id(s)''') - self.assertEqual(gdb_repr, "{'b'}") - - @support.requires_resource('cpu') - def test_frozensets(self): - 'Verify the pretty-printing of frozensets' - if (gdb_major_version, gdb_minor_version) < (7, 3): - self.skipTest("pretty-printing of frozensets needs gdb 7.3 or later") - self.assertGdbRepr(frozenset(), "frozenset()") - self.assertGdbRepr(frozenset(['a']), "frozenset({'a'})") - # PYTHONHASHSEED is need to get the exact frozenset item order - if not sys.flags.ignore_environment: - self.assertGdbRepr(frozenset(['a', 'b']), "frozenset({'a', 'b'})") - self.assertGdbRepr(frozenset([4, 5, 6]), "frozenset({4, 5, 6})") - - def test_exceptions(self): - # Test a RuntimeError - gdb_repr, gdb_output = self.get_gdb_repr(''' -try: - raise RuntimeError("I am an error") -except RuntimeError as e: - id(e) -''') - self.assertEqual(gdb_repr, - "RuntimeError('I am an error',)") - - - # Test division by zero: - gdb_repr, gdb_output = self.get_gdb_repr(''' -try: - a = 1 / 0 -except ZeroDivisionError as e: - id(e) -''') - self.assertEqual(gdb_repr, - "ZeroDivisionError('division by zero',)") - - def test_modern_class(self): - 'Verify the pretty-printing of new-style class instances' - gdb_repr, gdb_output = self.get_gdb_repr(''' -class Foo: - pass -foo = Foo() -foo.an_int = 42 -id(foo)''') - m = re.match(r'', gdb_repr) - self.assertTrue(m, - msg='Unexpected new-style class rendering %r' % gdb_repr) - - def test_subclassing_list(self): - 'Verify the pretty-printing of an instance of a list subclass' - gdb_repr, gdb_output = self.get_gdb_repr(''' -class Foo(list): - pass -foo = Foo() -foo += [1, 2, 3] -foo.an_int = 42 -id(foo)''') - m = re.match(r'', gdb_repr) - - self.assertTrue(m, - msg='Unexpected new-style class rendering %r' % gdb_repr) - - def test_subclassing_tuple(self): - 'Verify the pretty-printing of an instance of a tuple subclass' - # This should exercise the negative tp_dictoffset code in the - # new-style class support - gdb_repr, gdb_output = self.get_gdb_repr(''' -class Foo(tuple): - pass -foo = Foo((1, 2, 3)) -foo.an_int = 42 -id(foo)''') - m = re.match(r'', gdb_repr) - - self.assertTrue(m, - msg='Unexpected new-style class rendering %r' % gdb_repr) - - def assertSane(self, source, corruption, exprepr=None): - '''Run Python under gdb, corrupting variables in the inferior process - immediately before taking a backtrace. - - Verify that the variable's representation is the expected failsafe - representation''' - if corruption: - cmds_after_breakpoint=[corruption, 'backtrace'] - else: - cmds_after_breakpoint=['backtrace'] - - gdb_repr, gdb_output = \ - self.get_gdb_repr(source, - cmds_after_breakpoint=cmds_after_breakpoint) - if exprepr: - if gdb_repr == exprepr: - # gdb managed to print the value in spite of the corruption; - # this is good (see http://bugs.python.org/issue8330) - return - - # Match anything for the type name; 0xDEADBEEF could point to - # something arbitrary (see http://bugs.python.org/issue8330) - pattern = '<.* at remote 0x-?[0-9a-f]+>' - - m = re.match(pattern, gdb_repr) - if not m: - self.fail('Unexpected gdb representation: %r\n%s' % \ - (gdb_repr, gdb_output)) - - def test_NULL_ptr(self): - 'Ensure that a NULL PyObject* is handled gracefully' - gdb_repr, gdb_output = ( - self.get_gdb_repr('id(42)', - cmds_after_breakpoint=['set variable v=0', - 'backtrace']) - ) - - self.assertEqual(gdb_repr, '0x0') - - def test_NULL_ob_type(self): - 'Ensure that a PyObject* with NULL ob_type is handled gracefully' - self.assertSane('id(42)', - 'set v->ob_type=0') - - def test_corrupt_ob_type(self): - 'Ensure that a PyObject* with a corrupt ob_type is handled gracefully' - self.assertSane('id(42)', - 'set v->ob_type=0xDEADBEEF', - exprepr='42') - - def test_corrupt_tp_flags(self): - 'Ensure that a PyObject* with a type with corrupt tp_flags is handled' - self.assertSane('id(42)', - 'set v->ob_type->tp_flags=0x0', - exprepr='42') - - def test_corrupt_tp_name(self): - 'Ensure that a PyObject* with a type with corrupt tp_name is handled' - self.assertSane('id(42)', - 'set v->ob_type->tp_name=0xDEADBEEF', - exprepr='42') - - def test_builtins_help(self): - 'Ensure that the new-style class _Helper in site.py can be handled' - - if sys.flags.no_site: - self.skipTest("need site module, but -S option was used") - - # (this was the issue causing tracebacks in - # http://bugs.python.org/issue8032#msg100537 ) - gdb_repr, gdb_output = self.get_gdb_repr('id(__builtins__.help)', import_site=True) - - m = re.match(r'<_Helper\(\) at remote 0x-?[0-9a-f]+>', gdb_repr) - self.assertTrue(m, - msg='Unexpected rendering %r' % gdb_repr) - - def test_selfreferential_list(self): - '''Ensure that a reference loop involving a list doesn't lead proxyval - into an infinite loop:''' - gdb_repr, gdb_output = \ - self.get_gdb_repr("a = [3, 4, 5] ; a.append(a) ; id(a)") - self.assertEqual(gdb_repr, '[3, 4, 5, [...]]') - - gdb_repr, gdb_output = \ - self.get_gdb_repr("a = [3, 4, 5] ; b = [a] ; a.append(b) ; id(a)") - self.assertEqual(gdb_repr, '[3, 4, 5, [[...]]]') - - def test_selfreferential_dict(self): - '''Ensure that a reference loop involving a dict doesn't lead proxyval - into an infinite loop:''' - gdb_repr, gdb_output = \ - self.get_gdb_repr("a = {} ; b = {'bar':a} ; a['foo'] = b ; id(a)") - - self.assertEqual(gdb_repr, "{'foo': {'bar': {...}}}") - - def test_selfreferential_old_style_instance(self): - gdb_repr, gdb_output = \ - self.get_gdb_repr(''' -class Foo: - pass -foo = Foo() -foo.an_attr = foo -id(foo)''') - self.assertTrue(re.match(r'\) at remote 0x-?[0-9a-f]+>', - gdb_repr), - 'Unexpected gdb representation: %r\n%s' % \ - (gdb_repr, gdb_output)) - - def test_selfreferential_new_style_instance(self): - gdb_repr, gdb_output = \ - self.get_gdb_repr(''' -class Foo(object): - pass -foo = Foo() -foo.an_attr = foo -id(foo)''') - self.assertTrue(re.match(r'\) at remote 0x-?[0-9a-f]+>', - gdb_repr), - 'Unexpected gdb representation: %r\n%s' % \ - (gdb_repr, gdb_output)) - - gdb_repr, gdb_output = \ - self.get_gdb_repr(''' -class Foo(object): - pass -a = Foo() -b = Foo() -a.an_attr = b -b.an_attr = a -id(a)''') - self.assertTrue(re.match(r'\) at remote 0x-?[0-9a-f]+>\) at remote 0x-?[0-9a-f]+>', - gdb_repr), - 'Unexpected gdb representation: %r\n%s' % \ - (gdb_repr, gdb_output)) - - def test_truncation(self): - 'Verify that very long output is truncated' - gdb_repr, gdb_output = self.get_gdb_repr('id(list(range(1000)))') - self.assertEqual(gdb_repr, - "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, " - "14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, " - "27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, " - "40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, " - "53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, " - "66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, " - "79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, " - "92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, " - "104, 105, 106, 107, 108, 109, 110, 111, 112, 113, " - "114, 115, 116, 117, 118, 119, 120, 121, 122, 123, " - "124, 125, 126, 127, 128, 129, 130, 131, 132, 133, " - "134, 135, 136, 137, 138, 139, 140, 141, 142, 143, " - "144, 145, 146, 147, 148, 149, 150, 151, 152, 153, " - "154, 155, 156, 157, 158, 159, 160, 161, 162, 163, " - "164, 165, 166, 167, 168, 169, 170, 171, 172, 173, " - "174, 175, 176, 177, 178, 179, 180, 181, 182, 183, " - "184, 185, 186, 187, 188, 189, 190, 191, 192, 193, " - "194, 195, 196, 197, 198, 199, 200, 201, 202, 203, " - "204, 205, 206, 207, 208, 209, 210, 211, 212, 213, " - "214, 215, 216, 217, 218, 219, 220, 221, 222, 223, " - "224, 225, 226...(truncated)") - self.assertEqual(len(gdb_repr), - 1024 + len('...(truncated)')) - - def test_builtin_method(self): - gdb_repr, gdb_output = self.get_gdb_repr('import sys; id(sys.stdout.readlines)') - self.assertTrue(re.match(r'', - gdb_repr), - 'Unexpected gdb representation: %r\n%s' % \ - (gdb_repr, gdb_output)) - - def test_frames(self): - gdb_output = self.get_stack_trace(''' -import sys -def foo(a, b, c): - return sys._getframe(0) - -f = foo(3, 4, 5) -id(f)''', - breakpoint='builtin_id', - cmds_after_breakpoint=['print (PyFrameObject*)v'] - ) - self.assertTrue(re.match(r'.*\s+\$1 =\s+Frame 0x-?[0-9a-f]+, for file , line 4, in foo \(a=3.*', - gdb_output, - re.DOTALL), - 'Unexpected gdb representation: %r\n%s' % (gdb_output, gdb_output)) - -@unittest.skipIf(python_is_optimized(), - "Python was compiled with optimizations") -class PyListTests(DebuggerTests): - def assertListing(self, expected, actual): - self.assertEndsWith(actual, expected) - - def test_basic_command(self): - 'Verify that the "py-list" command works' - bt = self.get_stack_trace(script=self.get_sample_script(), - cmds_after_breakpoint=['py-list']) - - self.assertListing(' 5 \n' - ' 6 def bar(a, b, c):\n' - ' 7 baz(a, b, c)\n' - ' 8 \n' - ' 9 def baz(*args):\n' - ' >10 id(42)\n' - ' 11 \n' - ' 12 foo(1, 2, 3)\n', - bt) - - def test_one_abs_arg(self): - 'Verify the "py-list" command with one absolute argument' - bt = self.get_stack_trace(script=self.get_sample_script(), - cmds_after_breakpoint=['py-list 9']) - - self.assertListing(' 9 def baz(*args):\n' - ' >10 id(42)\n' - ' 11 \n' - ' 12 foo(1, 2, 3)\n', - bt) - - def test_two_abs_args(self): - 'Verify the "py-list" command with two absolute arguments' - bt = self.get_stack_trace(script=self.get_sample_script(), - cmds_after_breakpoint=['py-list 1,3']) - - self.assertListing(' 1 # Sample script for use by test_gdb.py\n' - ' 2 \n' - ' 3 def foo(a, b, c):\n', - bt) - -SAMPLE_WITH_C_CALL = """ - -from _testcapi import pyobject_fastcall - -def foo(a, b, c): - bar(a, b, c) - -def bar(a, b, c): - pyobject_fastcall(baz, (a, b, c)) - -def baz(*args): - id(42) - -foo(1, 2, 3) - -""" - - -class StackNavigationTests(DebuggerTests): - @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") - @unittest.skipIf(python_is_optimized(), - "Python was compiled with optimizations") - def test_pyup_command(self): - 'Verify that the "py-up" command works' - bt = self.get_stack_trace(source=SAMPLE_WITH_C_CALL, - cmds_after_breakpoint=['py-up', 'py-up']) - self.assertMultilineMatches(bt, - r'''^.* -#[0-9]+ Frame 0x-?[0-9a-f]+, for file , line 12, in baz \(args=\(1, 2, 3\)\) -#[0-9]+ -$''') - - @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") - def test_down_at_bottom(self): - 'Verify handling of "py-down" at the bottom of the stack' - bt = self.get_stack_trace(script=self.get_sample_script(), - cmds_after_breakpoint=['py-down']) - self.assertEndsWith(bt, - 'Unable to find a newer python frame\n') - - @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") - def test_up_at_top(self): - 'Verify handling of "py-up" at the top of the stack' - bt = self.get_stack_trace(script=self.get_sample_script(), - cmds_after_breakpoint=['py-up'] * 5) - self.assertEndsWith(bt, - 'Unable to find an older python frame\n') - - @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") - @unittest.skipIf(python_is_optimized(), - "Python was compiled with optimizations") - def test_up_then_down(self): - 'Verify "py-up" followed by "py-down"' - bt = self.get_stack_trace(source=SAMPLE_WITH_C_CALL, - cmds_after_breakpoint=['py-up', 'py-up', 'py-down']) - self.assertMultilineMatches(bt, - r'''^.* -#[0-9]+ Frame 0x-?[0-9a-f]+, for file , line 12, in baz \(args=\(1, 2, 3\)\) -#[0-9]+ -#[0-9]+ Frame 0x-?[0-9a-f]+, for file , line 12, in baz \(args=\(1, 2, 3\)\) -$''') - -class PyBtTests(DebuggerTests): - @unittest.skipIf(python_is_optimized(), - "Python was compiled with optimizations") - def test_bt(self): - 'Verify that the "py-bt" command works' - bt = self.get_stack_trace(script=self.get_sample_script(), - cmds_after_breakpoint=['py-bt']) - self.assertMultilineMatches(bt, - r'''^.* -Traceback \(most recent call first\): - - File ".*gdb_sample.py", line 10, in baz - id\(42\) - File ".*gdb_sample.py", line 7, in bar - baz\(a, b, c\) - File ".*gdb_sample.py", line 4, in foo - bar\(a=a, b=b, c=c\) - File ".*gdb_sample.py", line 12, in - foo\(1, 2, 3\) -''') - - @unittest.skipIf(python_is_optimized(), - "Python was compiled with optimizations") - def test_bt_full(self): - 'Verify that the "py-bt-full" command works' - bt = self.get_stack_trace(script=self.get_sample_script(), - cmds_after_breakpoint=['py-bt-full']) - self.assertMultilineMatches(bt, - r'''^.* -#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\) - baz\(a, b, c\) -#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 4, in foo \(a=1, b=2, c=3\) - bar\(a=a, b=b, c=c\) -#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 12, in \(\) - foo\(1, 2, 3\) -''') - - @unittest.skipIf(python_is_optimized(), - "Python was compiled with optimizations") - @support.requires_resource('cpu') - def test_threads(self): - 'Verify that "py-bt" indicates threads that are waiting for the GIL' - cmd = ''' -from threading import Thread - -class TestThread(Thread): - # These threads would run forever, but we'll interrupt things with the - # debugger - def run(self): - i = 0 - while 1: - i += 1 - -t = {} -for i in range(4): - t[i] = TestThread() - t[i].start() - -# Trigger a breakpoint on the main thread -id(42) - -''' - # Verify with "py-bt": - gdb_output = self.get_stack_trace(cmd, - cmds_after_breakpoint=['thread apply all py-bt']) - self.assertIn('Waiting for the GIL', gdb_output) - - # Verify with "py-bt-full": - gdb_output = self.get_stack_trace(cmd, - cmds_after_breakpoint=['thread apply all py-bt-full']) - self.assertIn('Waiting for the GIL', gdb_output) - - @unittest.skipIf(python_is_optimized(), - "Python was compiled with optimizations") - # Some older versions of gdb will fail with - # "Cannot find new threads: generic error" - # unless we add LD_PRELOAD=PATH-TO-libpthread.so.1 as a workaround - def test_gc(self): - 'Verify that "py-bt" indicates if a thread is garbage-collecting' - cmd = ('from gc import collect\n' - 'id(42)\n' - 'def foo():\n' - ' collect()\n' - 'def bar():\n' - ' foo()\n' - 'bar()\n') - # Verify with "py-bt": - gdb_output = self.get_stack_trace(cmd, - cmds_after_breakpoint=['break update_refs', 'continue', 'py-bt'], - ) - self.assertIn('Garbage-collecting', gdb_output) - - # Verify with "py-bt-full": - gdb_output = self.get_stack_trace(cmd, - cmds_after_breakpoint=['break update_refs', 'continue', 'py-bt-full'], - ) - self.assertIn('Garbage-collecting', gdb_output) - - - @unittest.skipIf(python_is_optimized(), - "Python was compiled with optimizations") - @support.requires_resource('cpu') - # Some older versions of gdb will fail with - # "Cannot find new threads: generic error" - # unless we add LD_PRELOAD=PATH-TO-libpthread.so.1 as a workaround - # - # gdb will also generate many erroneous errors such as: - # Function "meth_varargs" not defined. - # This is because we are calling functions from an "external" module - # (_testcapimodule) rather than compiled-in functions. It seems difficult - # to suppress these. See also the comment in DebuggerTests.get_stack_trace - def test_pycfunction(self): - 'Verify that "py-bt" displays invocations of PyCFunction instances' - # bpo-46600: If the compiler inlines _null_to_none() in meth_varargs() - # (ex: clang -Og), _null_to_none() is the frame #1. Otherwise, - # meth_varargs() is the frame #1. - expected_frame = r'#(1|2)' - # Various optimizations multiply the code paths by which these are - # called, so test a variety of calling conventions. - for func_name, args in ( - ('meth_varargs', ''), - ('meth_varargs_keywords', ''), - ('meth_o', '[]'), - ('meth_noargs', ''), - ('meth_fastcall', ''), - ('meth_fastcall_keywords', ''), - ): - for obj in ( - '_testcapi', - '_testcapi.MethClass', - '_testcapi.MethClass()', - '_testcapi.MethStatic()', - - # XXX: bound methods don't yet give nice tracebacks - # '_testcapi.MethInstance()', - ): - with self.subTest(f'{obj}.{func_name}'): - cmd = textwrap.dedent(f''' - import _testcapi - def foo(): - {obj}.{func_name}({args}) - def bar(): - foo() - bar() - ''') - # Verify with "py-bt": - gdb_output = self.get_stack_trace( - cmd, - breakpoint=func_name, - cmds_after_breakpoint=['bt', 'py-bt'], - # bpo-45207: Ignore 'Function "meth_varargs" not - # defined.' message in stderr. - ignore_stderr=True, - ) - self.assertIn(f'\n.*") - -class PyLocalsTests(DebuggerTests): - @unittest.skipIf(python_is_optimized(), - "Python was compiled with optimizations") - def test_basic_command(self): - bt = self.get_stack_trace(script=self.get_sample_script(), - cmds_after_breakpoint=['py-up', 'py-locals']) - self.assertMultilineMatches(bt, - r".*\nargs = \(1, 2, 3\)\n.*") - - @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") - @unittest.skipIf(python_is_optimized(), - "Python was compiled with optimizations") - def test_locals_after_up(self): - bt = self.get_stack_trace(script=self.get_sample_script(), - cmds_after_breakpoint=['py-up', 'py-up', 'py-locals']) - self.assertMultilineMatches(bt, - r'''^.* -Locals for foo -a = 1 -b = 2 -c = 3 -Locals for -.*$''') - - -def setUpModule(): - if support.verbose: - print("GDB version %s.%s:" % (gdb_major_version, gdb_minor_version)) - for line in gdb_version.splitlines(): - print(" " * 4 + line) - - -if __name__ == "__main__": - unittest.main() diff --git a/Lib/test/test_gdb/__init__.py b/Lib/test/test_gdb/__init__.py new file mode 100644 index 00000000000000..0261f59adf54bd --- /dev/null +++ b/Lib/test/test_gdb/__init__.py @@ -0,0 +1,10 @@ +# Verify that gdb can pretty-print the various PyObject* types +# +# The code for testing gdb was adapted from similar work in Unladen Swallow's +# Lib/test/test_jit_gdb.py + +import os +from test.support import load_package_tests + +def load_tests(*args): + return load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/gdb_sample.py b/Lib/test/test_gdb/gdb_sample.py similarity index 75% rename from Lib/test/gdb_sample.py rename to Lib/test/test_gdb/gdb_sample.py index 4188f50136fb97..a7f23db73ea6e6 100644 --- a/Lib/test/gdb_sample.py +++ b/Lib/test/test_gdb/gdb_sample.py @@ -1,4 +1,4 @@ -# Sample script for use by test_gdb.py +# Sample script for use by test_gdb def foo(a, b, c): bar(a=a, b=b, c=c) diff --git a/Lib/test/test_gdb/test_backtrace.py b/Lib/test/test_gdb/test_backtrace.py new file mode 100644 index 00000000000000..15cbcf169ab9e3 --- /dev/null +++ b/Lib/test/test_gdb/test_backtrace.py @@ -0,0 +1,134 @@ +import textwrap +import unittest +from test import support +from test.support import python_is_optimized + +from .util import setup_module, DebuggerTests, CET_PROTECTION + + +def setUpModule(): + setup_module() + + +class PyBtTests(DebuggerTests): + @unittest.skipIf(python_is_optimized(), + "Python was compiled with optimizations") + def test_bt(self): + 'Verify that the "py-bt" command works' + bt = self.get_stack_trace(script=self.get_sample_script(), + cmds_after_breakpoint=['py-bt']) + self.assertMultilineMatches(bt, + r'''^.* +Traceback \(most recent call first\): + + File ".*gdb_sample.py", line 10, in baz + id\(42\) + File ".*gdb_sample.py", line 7, in bar + baz\(a, b, c\) + File ".*gdb_sample.py", line 4, in foo + bar\(a=a, b=b, c=c\) + File ".*gdb_sample.py", line 12, in + foo\(1, 2, 3\) +''') + + @unittest.skipIf(python_is_optimized(), + "Python was compiled with optimizations") + def test_bt_full(self): + 'Verify that the "py-bt-full" command works' + bt = self.get_stack_trace(script=self.get_sample_script(), + cmds_after_breakpoint=['py-bt-full']) + self.assertMultilineMatches(bt, + r'''^.* +#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\) + baz\(a, b, c\) +#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 4, in foo \(a=1, b=2, c=3\) + bar\(a=a, b=b, c=c\) +#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 12, in \(\) + foo\(1, 2, 3\) +''') + + @unittest.skipIf(python_is_optimized(), + "Python was compiled with optimizations") + @support.requires_resource('cpu') + def test_threads(self): + 'Verify that "py-bt" indicates threads that are waiting for the GIL' + cmd = ''' +from threading import Thread + +class TestThread(Thread): + # These threads would run forever, but we'll interrupt things with the + # debugger + def run(self): + i = 0 + while 1: + i += 1 + +t = {} +for i in range(4): + t[i] = TestThread() + t[i].start() + +# Trigger a breakpoint on the main thread +id(42) + +''' + # Verify with "py-bt": + gdb_output = self.get_stack_trace(cmd, + cmds_after_breakpoint=['thread apply all py-bt']) + self.assertIn('Waiting for the GIL', gdb_output) + + # Verify with "py-bt-full": + gdb_output = self.get_stack_trace(cmd, + cmds_after_breakpoint=['thread apply all py-bt-full']) + self.assertIn('Waiting for the GIL', gdb_output) + + @unittest.skipIf(python_is_optimized(), + "Python was compiled with optimizations") + # Some older versions of gdb will fail with + # "Cannot find new threads: generic error" + # unless we add LD_PRELOAD=PATH-TO-libpthread.so.1 as a workaround + def test_gc(self): + 'Verify that "py-bt" indicates if a thread is garbage-collecting' + cmd = ('from gc import collect\n' + 'id(42)\n' + 'def foo():\n' + ' collect()\n' + 'def bar():\n' + ' foo()\n' + 'bar()\n') + # Verify with "py-bt": + gdb_output = self.get_stack_trace(cmd, + cmds_after_breakpoint=['break update_refs', 'continue', 'py-bt'], + ) + self.assertIn('Garbage-collecting', gdb_output) + + # Verify with "py-bt-full": + gdb_output = self.get_stack_trace(cmd, + cmds_after_breakpoint=['break update_refs', 'continue', 'py-bt-full'], + ) + self.assertIn('Garbage-collecting', gdb_output) + + @unittest.skipIf(python_is_optimized(), + "Python was compiled with optimizations") + def test_wrapper_call(self): + cmd = textwrap.dedent(''' + class MyList(list): + def __init__(self): + super(*[]).__init__() # wrapper_call() + + id("first break point") + l = MyList() + ''') + cmds_after_breakpoint = ['break wrapper_call', 'continue'] + if CET_PROTECTION: + # bpo-32962: same case as in get_stack_trace(): + # we need an additional 'next' command in order to read + # arguments of the innermost function of the call stack. + cmds_after_breakpoint.append('next') + cmds_after_breakpoint.append('py-bt') + + # Verify with "py-bt": + gdb_output = self.get_stack_trace(cmd, + cmds_after_breakpoint=cmds_after_breakpoint) + self.assertRegex(gdb_output, + r"10 id(42)\n' + ' 11 \n' + ' 12 foo(1, 2, 3)\n', + bt) + + def test_one_abs_arg(self): + 'Verify the "py-list" command with one absolute argument' + bt = self.get_stack_trace(script=self.get_sample_script(), + cmds_after_breakpoint=['py-list 9']) + + self.assertListing(' 9 def baz(*args):\n' + ' >10 id(42)\n' + ' 11 \n' + ' 12 foo(1, 2, 3)\n', + bt) + + def test_two_abs_args(self): + 'Verify the "py-list" command with two absolute arguments' + bt = self.get_stack_trace(script=self.get_sample_script(), + cmds_after_breakpoint=['py-list 1,3']) + + self.assertListing(' 1 # Sample script for use by test_gdb\n' + ' 2 \n' + ' 3 def foo(a, b, c):\n', + bt) + +SAMPLE_WITH_C_CALL = """ + +from _testcapi import pyobject_vectorcall + +def foo(a, b, c): + bar(a, b, c) + +def bar(a, b, c): + pyobject_vectorcall(baz, (a, b, c), None) + +def baz(*args): + id(42) + +foo(1, 2, 3) + +""" + + +class StackNavigationTests(DebuggerTests): + @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") + @unittest.skipIf(python_is_optimized(), + "Python was compiled with optimizations") + def test_pyup_command(self): + 'Verify that the "py-up" command works' + bt = self.get_stack_trace(source=SAMPLE_WITH_C_CALL, + cmds_after_breakpoint=['py-up', 'py-up']) + self.assertMultilineMatches(bt, + r'''^.* +#[0-9]+ Frame 0x-?[0-9a-f]+, for file , line 12, in baz \(args=\(1, 2, 3\)\) +#[0-9]+ +$''') + + @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") + def test_down_at_bottom(self): + 'Verify handling of "py-down" at the bottom of the stack' + bt = self.get_stack_trace(script=self.get_sample_script(), + cmds_after_breakpoint=['py-down']) + self.assertEndsWith(bt, + 'Unable to find a newer python frame\n') + + @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") + def test_up_at_top(self): + 'Verify handling of "py-up" at the top of the stack' + bt = self.get_stack_trace(script=self.get_sample_script(), + cmds_after_breakpoint=['py-up'] * 5) + self.assertEndsWith(bt, + 'Unable to find an older python frame\n') + + @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") + @unittest.skipIf(python_is_optimized(), + "Python was compiled with optimizations") + def test_up_then_down(self): + 'Verify "py-up" followed by "py-down"' + bt = self.get_stack_trace(source=SAMPLE_WITH_C_CALL, + cmds_after_breakpoint=['py-up', 'py-up', 'py-down']) + self.assertMultilineMatches(bt, + r'''^.* +#[0-9]+ Frame 0x-?[0-9a-f]+, for file , line 12, in baz \(args=\(1, 2, 3\)\) +#[0-9]+ +#[0-9]+ Frame 0x-?[0-9a-f]+, for file , line 12, in baz \(args=\(1, 2, 3\)\) +$''') + +class PyPrintTests(DebuggerTests): + @unittest.skipIf(python_is_optimized(), + "Python was compiled with optimizations") + def test_basic_command(self): + 'Verify that the "py-print" command works' + bt = self.get_stack_trace(source=SAMPLE_WITH_C_CALL, + cmds_after_breakpoint=['py-up', 'py-print args']) + self.assertMultilineMatches(bt, + r".*\nlocal 'args' = \(1, 2, 3\)\n.*") + + @unittest.skipIf(python_is_optimized(), + "Python was compiled with optimizations") + @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") + def test_print_after_up(self): + bt = self.get_stack_trace(source=SAMPLE_WITH_C_CALL, + cmds_after_breakpoint=['py-up', 'py-up', 'py-print c', 'py-print b', 'py-print a']) + self.assertMultilineMatches(bt, + r".*\nlocal 'c' = 3\nlocal 'b' = 2\nlocal 'a' = 1\n.*") + + @unittest.skipIf(python_is_optimized(), + "Python was compiled with optimizations") + def test_printing_global(self): + bt = self.get_stack_trace(script=self.get_sample_script(), + cmds_after_breakpoint=['py-up', 'py-print __name__']) + self.assertMultilineMatches(bt, + r".*\nglobal '__name__' = '__main__'\n.*") + + @unittest.skipIf(python_is_optimized(), + "Python was compiled with optimizations") + def test_printing_builtin(self): + bt = self.get_stack_trace(script=self.get_sample_script(), + cmds_after_breakpoint=['py-up', 'py-print len']) + self.assertMultilineMatches(bt, + r".*\nbuiltin 'len' = \n.*") + +class PyLocalsTests(DebuggerTests): + @unittest.skipIf(python_is_optimized(), + "Python was compiled with optimizations") + def test_basic_command(self): + bt = self.get_stack_trace(script=self.get_sample_script(), + cmds_after_breakpoint=['py-up', 'py-locals']) + self.assertMultilineMatches(bt, + r".*\nargs = \(1, 2, 3\)\n.*") + + @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") + @unittest.skipIf(python_is_optimized(), + "Python was compiled with optimizations") + def test_locals_after_up(self): + bt = self.get_stack_trace(script=self.get_sample_script(), + cmds_after_breakpoint=['py-up', 'py-up', 'py-locals']) + self.assertMultilineMatches(bt, + r'''^.* +Locals for foo +a = 1 +b = 2 +c = 3 +Locals for +.*$''') diff --git a/Lib/test/test_gdb/test_pretty_print.py b/Lib/test/test_gdb/test_pretty_print.py new file mode 100644 index 00000000000000..e31dc66f29684a --- /dev/null +++ b/Lib/test/test_gdb/test_pretty_print.py @@ -0,0 +1,400 @@ +import re +import sys +from test import support + +from .util import ( + BREAKPOINT_FN, gdb_major_version, gdb_minor_version, + run_gdb, setup_module, DebuggerTests) + + +def setUpModule(): + setup_module() + + +class PrettyPrintTests(DebuggerTests): + def test_getting_backtrace(self): + gdb_output = self.get_stack_trace('id(42)') + self.assertTrue(BREAKPOINT_FN in gdb_output) + + def assertGdbRepr(self, val, exp_repr=None): + # Ensure that gdb's rendering of the value in a debugged process + # matches repr(value) in this process: + gdb_repr, gdb_output = self.get_gdb_repr('id(' + ascii(val) + ')') + if not exp_repr: + exp_repr = repr(val) + self.assertEqual(gdb_repr, exp_repr, + ('%r did not equal expected %r; full output was:\n%s' + % (gdb_repr, exp_repr, gdb_output))) + + @support.requires_resource('cpu') + def test_int(self): + 'Verify the pretty-printing of various int values' + self.assertGdbRepr(42) + self.assertGdbRepr(0) + self.assertGdbRepr(-7) + self.assertGdbRepr(1000000000000) + self.assertGdbRepr(-1000000000000000) + + def test_singletons(self): + 'Verify the pretty-printing of True, False and None' + self.assertGdbRepr(True) + self.assertGdbRepr(False) + self.assertGdbRepr(None) + + def test_dicts(self): + 'Verify the pretty-printing of dictionaries' + self.assertGdbRepr({}) + self.assertGdbRepr({'foo': 'bar'}, "{'foo': 'bar'}") + # Python preserves insertion order since 3.6 + self.assertGdbRepr({'foo': 'bar', 'douglas': 42}, "{'foo': 'bar', 'douglas': 42}") + + def test_lists(self): + 'Verify the pretty-printing of lists' + self.assertGdbRepr([]) + self.assertGdbRepr(list(range(5))) + + @support.requires_resource('cpu') + def test_bytes(self): + 'Verify the pretty-printing of bytes' + self.assertGdbRepr(b'') + self.assertGdbRepr(b'And now for something hopefully the same') + self.assertGdbRepr(b'string with embedded NUL here \0 and then some more text') + self.assertGdbRepr(b'this is a tab:\t' + b' this is a slash-N:\n' + b' this is a slash-R:\r' + ) + + self.assertGdbRepr(b'this is byte 255:\xff and byte 128:\x80') + + self.assertGdbRepr(bytes([b for b in range(255)])) + + @support.requires_resource('cpu') + def test_strings(self): + 'Verify the pretty-printing of unicode strings' + # We cannot simply call locale.getpreferredencoding() here, + # as GDB might have been linked against a different version + # of Python with a different encoding and coercion policy + # with respect to PEP 538 and PEP 540. + out, err = run_gdb( + '--eval-command', + 'python import locale; print(locale.getpreferredencoding())') + + encoding = out.rstrip() + if err or not encoding: + raise RuntimeError( + f'unable to determine the preferred encoding ' + f'of embedded Python in GDB: {err}') + + def check_repr(text): + try: + text.encode(encoding) + except UnicodeEncodeError: + self.assertGdbRepr(text, ascii(text)) + else: + self.assertGdbRepr(text) + + self.assertGdbRepr('') + self.assertGdbRepr('And now for something hopefully the same') + self.assertGdbRepr('string with embedded NUL here \0 and then some more text') + + # Test printing a single character: + # U+2620 SKULL AND CROSSBONES + check_repr('\u2620') + + # Test printing a Japanese unicode string + # (I believe this reads "mojibake", using 3 characters from the CJK + # Unified Ideographs area, followed by U+3051 HIRAGANA LETTER KE) + check_repr('\u6587\u5b57\u5316\u3051') + + # Test a character outside the BMP: + # U+1D121 MUSICAL SYMBOL C CLEF + # This is: + # UTF-8: 0xF0 0x9D 0x84 0xA1 + # UTF-16: 0xD834 0xDD21 + check_repr(chr(0x1D121)) + + def test_tuples(self): + 'Verify the pretty-printing of tuples' + self.assertGdbRepr(tuple(), '()') + self.assertGdbRepr((1,), '(1,)') + self.assertGdbRepr(('foo', 'bar', 'baz')) + + @support.requires_resource('cpu') + def test_sets(self): + 'Verify the pretty-printing of sets' + if (gdb_major_version, gdb_minor_version) < (7, 3): + self.skipTest("pretty-printing of sets needs gdb 7.3 or later") + self.assertGdbRepr(set(), "set()") + self.assertGdbRepr(set(['a']), "{'a'}") + # PYTHONHASHSEED is need to get the exact frozenset item order + if not sys.flags.ignore_environment: + self.assertGdbRepr(set(['a', 'b']), "{'a', 'b'}") + self.assertGdbRepr(set([4, 5, 6]), "{4, 5, 6}") + + # Ensure that we handle sets containing the "dummy" key value, + # which happens on deletion: + gdb_repr, gdb_output = self.get_gdb_repr('''s = set(['a','b']) +s.remove('a') +id(s)''') + self.assertEqual(gdb_repr, "{'b'}") + + @support.requires_resource('cpu') + def test_frozensets(self): + 'Verify the pretty-printing of frozensets' + if (gdb_major_version, gdb_minor_version) < (7, 3): + self.skipTest("pretty-printing of frozensets needs gdb 7.3 or later") + self.assertGdbRepr(frozenset(), "frozenset()") + self.assertGdbRepr(frozenset(['a']), "frozenset({'a'})") + # PYTHONHASHSEED is need to get the exact frozenset item order + if not sys.flags.ignore_environment: + self.assertGdbRepr(frozenset(['a', 'b']), "frozenset({'a', 'b'})") + self.assertGdbRepr(frozenset([4, 5, 6]), "frozenset({4, 5, 6})") + + def test_exceptions(self): + # Test a RuntimeError + gdb_repr, gdb_output = self.get_gdb_repr(''' +try: + raise RuntimeError("I am an error") +except RuntimeError as e: + id(e) +''') + self.assertEqual(gdb_repr, + "RuntimeError('I am an error',)") + + + # Test division by zero: + gdb_repr, gdb_output = self.get_gdb_repr(''' +try: + a = 1 / 0 +except ZeroDivisionError as e: + id(e) +''') + self.assertEqual(gdb_repr, + "ZeroDivisionError('division by zero',)") + + def test_modern_class(self): + 'Verify the pretty-printing of new-style class instances' + gdb_repr, gdb_output = self.get_gdb_repr(''' +class Foo: + pass +foo = Foo() +foo.an_int = 42 +id(foo)''') + m = re.match(r'', gdb_repr) + self.assertTrue(m, + msg='Unexpected new-style class rendering %r' % gdb_repr) + + def test_subclassing_list(self): + 'Verify the pretty-printing of an instance of a list subclass' + gdb_repr, gdb_output = self.get_gdb_repr(''' +class Foo(list): + pass +foo = Foo() +foo += [1, 2, 3] +foo.an_int = 42 +id(foo)''') + m = re.match(r'', gdb_repr) + + self.assertTrue(m, + msg='Unexpected new-style class rendering %r' % gdb_repr) + + def test_subclassing_tuple(self): + 'Verify the pretty-printing of an instance of a tuple subclass' + # This should exercise the negative tp_dictoffset code in the + # new-style class support + gdb_repr, gdb_output = self.get_gdb_repr(''' +class Foo(tuple): + pass +foo = Foo((1, 2, 3)) +foo.an_int = 42 +id(foo)''') + m = re.match(r'', gdb_repr) + + self.assertTrue(m, + msg='Unexpected new-style class rendering %r' % gdb_repr) + + def assertSane(self, source, corruption, exprepr=None): + '''Run Python under gdb, corrupting variables in the inferior process + immediately before taking a backtrace. + + Verify that the variable's representation is the expected failsafe + representation''' + if corruption: + cmds_after_breakpoint=[corruption, 'backtrace'] + else: + cmds_after_breakpoint=['backtrace'] + + gdb_repr, gdb_output = \ + self.get_gdb_repr(source, + cmds_after_breakpoint=cmds_after_breakpoint) + if exprepr: + if gdb_repr == exprepr: + # gdb managed to print the value in spite of the corruption; + # this is good (see http://bugs.python.org/issue8330) + return + + # Match anything for the type name; 0xDEADBEEF could point to + # something arbitrary (see http://bugs.python.org/issue8330) + pattern = '<.* at remote 0x-?[0-9a-f]+>' + + m = re.match(pattern, gdb_repr) + if not m: + self.fail('Unexpected gdb representation: %r\n%s' % \ + (gdb_repr, gdb_output)) + + def test_NULL_ptr(self): + 'Ensure that a NULL PyObject* is handled gracefully' + gdb_repr, gdb_output = ( + self.get_gdb_repr('id(42)', + cmds_after_breakpoint=['set variable v=0', + 'backtrace']) + ) + + self.assertEqual(gdb_repr, '0x0') + + def test_NULL_ob_type(self): + 'Ensure that a PyObject* with NULL ob_type is handled gracefully' + self.assertSane('id(42)', + 'set v->ob_type=0') + + def test_corrupt_ob_type(self): + 'Ensure that a PyObject* with a corrupt ob_type is handled gracefully' + self.assertSane('id(42)', + 'set v->ob_type=0xDEADBEEF', + exprepr='42') + + def test_corrupt_tp_flags(self): + 'Ensure that a PyObject* with a type with corrupt tp_flags is handled' + self.assertSane('id(42)', + 'set v->ob_type->tp_flags=0x0', + exprepr='42') + + def test_corrupt_tp_name(self): + 'Ensure that a PyObject* with a type with corrupt tp_name is handled' + self.assertSane('id(42)', + 'set v->ob_type->tp_name=0xDEADBEEF', + exprepr='42') + + def test_builtins_help(self): + 'Ensure that the new-style class _Helper in site.py can be handled' + + if sys.flags.no_site: + self.skipTest("need site module, but -S option was used") + + # (this was the issue causing tracebacks in + # http://bugs.python.org/issue8032#msg100537 ) + gdb_repr, gdb_output = self.get_gdb_repr('id(__builtins__.help)', import_site=True) + + m = re.match(r'<_Helper\(\) at remote 0x-?[0-9a-f]+>', gdb_repr) + self.assertTrue(m, + msg='Unexpected rendering %r' % gdb_repr) + + def test_selfreferential_list(self): + '''Ensure that a reference loop involving a list doesn't lead proxyval + into an infinite loop:''' + gdb_repr, gdb_output = \ + self.get_gdb_repr("a = [3, 4, 5] ; a.append(a) ; id(a)") + self.assertEqual(gdb_repr, '[3, 4, 5, [...]]') + + gdb_repr, gdb_output = \ + self.get_gdb_repr("a = [3, 4, 5] ; b = [a] ; a.append(b) ; id(a)") + self.assertEqual(gdb_repr, '[3, 4, 5, [[...]]]') + + def test_selfreferential_dict(self): + '''Ensure that a reference loop involving a dict doesn't lead proxyval + into an infinite loop:''' + gdb_repr, gdb_output = \ + self.get_gdb_repr("a = {} ; b = {'bar':a} ; a['foo'] = b ; id(a)") + + self.assertEqual(gdb_repr, "{'foo': {'bar': {...}}}") + + def test_selfreferential_old_style_instance(self): + gdb_repr, gdb_output = \ + self.get_gdb_repr(''' +class Foo: + pass +foo = Foo() +foo.an_attr = foo +id(foo)''') + self.assertTrue(re.match(r'\) at remote 0x-?[0-9a-f]+>', + gdb_repr), + 'Unexpected gdb representation: %r\n%s' % \ + (gdb_repr, gdb_output)) + + def test_selfreferential_new_style_instance(self): + gdb_repr, gdb_output = \ + self.get_gdb_repr(''' +class Foo(object): + pass +foo = Foo() +foo.an_attr = foo +id(foo)''') + self.assertTrue(re.match(r'\) at remote 0x-?[0-9a-f]+>', + gdb_repr), + 'Unexpected gdb representation: %r\n%s' % \ + (gdb_repr, gdb_output)) + + gdb_repr, gdb_output = \ + self.get_gdb_repr(''' +class Foo(object): + pass +a = Foo() +b = Foo() +a.an_attr = b +b.an_attr = a +id(a)''') + self.assertTrue(re.match(r'\) at remote 0x-?[0-9a-f]+>\) at remote 0x-?[0-9a-f]+>', + gdb_repr), + 'Unexpected gdb representation: %r\n%s' % \ + (gdb_repr, gdb_output)) + + def test_truncation(self): + 'Verify that very long output is truncated' + gdb_repr, gdb_output = self.get_gdb_repr('id(list(range(1000)))') + self.assertEqual(gdb_repr, + "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, " + "14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, " + "27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, " + "40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, " + "53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, " + "66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, " + "79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, " + "92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, " + "104, 105, 106, 107, 108, 109, 110, 111, 112, 113, " + "114, 115, 116, 117, 118, 119, 120, 121, 122, 123, " + "124, 125, 126, 127, 128, 129, 130, 131, 132, 133, " + "134, 135, 136, 137, 138, 139, 140, 141, 142, 143, " + "144, 145, 146, 147, 148, 149, 150, 151, 152, 153, " + "154, 155, 156, 157, 158, 159, 160, 161, 162, 163, " + "164, 165, 166, 167, 168, 169, 170, 171, 172, 173, " + "174, 175, 176, 177, 178, 179, 180, 181, 182, 183, " + "184, 185, 186, 187, 188, 189, 190, 191, 192, 193, " + "194, 195, 196, 197, 198, 199, 200, 201, 202, 203, " + "204, 205, 206, 207, 208, 209, 210, 211, 212, 213, " + "214, 215, 216, 217, 218, 219, 220, 221, 222, 223, " + "224, 225, 226...(truncated)") + self.assertEqual(len(gdb_repr), + 1024 + len('...(truncated)')) + + def test_builtin_method(self): + gdb_repr, gdb_output = self.get_gdb_repr('import sys; id(sys.stdout.readlines)') + self.assertTrue(re.match(r'', + gdb_repr), + 'Unexpected gdb representation: %r\n%s' % \ + (gdb_repr, gdb_output)) + + def test_frames(self): + gdb_output = self.get_stack_trace(''' +import sys +def foo(a, b, c): + return sys._getframe(0) + +f = foo(3, 4, 5) +id(f)''', + breakpoint='builtin_id', + cmds_after_breakpoint=['print (PyFrameObject*)v'] + ) + self.assertTrue(re.match(r'.*\s+\$1 =\s+Frame 0x-?[0-9a-f]+, for file , line 4, in foo \(a=3.*', + gdb_output, + re.DOTALL), + 'Unexpected gdb representation: %r\n%s' % (gdb_output, gdb_output)) diff --git a/Lib/test/test_gdb/util.py b/Lib/test/test_gdb/util.py new file mode 100644 index 00000000000000..30beb4e14285c7 --- /dev/null +++ b/Lib/test/test_gdb/util.py @@ -0,0 +1,304 @@ +import os +import re +import subprocess +import sys +import sysconfig +import unittest +from test import support + + +MS_WINDOWS = (sys.platform == 'win32') +if MS_WINDOWS: + raise unittest.SkipTest("test_gdb doesn't work on Windows") + + +def get_gdb_version(): + try: + cmd = ["gdb", "-nx", "--version"] + proc = subprocess.Popen(cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True) + with proc: + version, stderr = proc.communicate() + + if proc.returncode: + raise Exception(f"Command {' '.join(cmd)!r} failed " + f"with exit code {proc.returncode}: " + f"stdout={version!r} stderr={stderr!r}") + except OSError: + # This is what "no gdb" looks like. There may, however, be other + # errors that manifest this way too. + raise unittest.SkipTest("Couldn't find gdb on the path") + + # Regex to parse: + # 'GNU gdb (GDB; SUSE Linux Enterprise 12) 7.7\n' -> 7.7 + # 'GNU gdb (GDB) Fedora 7.9.1-17.fc22\n' -> 7.9 + # 'GNU gdb 6.1.1 [FreeBSD]\n' -> 6.1 + # 'GNU gdb (GDB) Fedora (7.5.1-37.fc18)\n' -> 7.5 + # 'HP gdb 6.7 for HP Itanium (32 or 64 bit) and target HP-UX 11iv2 and 11iv3.\n' -> 6.7 + match = re.search(r"^(?:GNU|HP) gdb.*?\b(\d+)\.(\d+)", version) + if match is None: + raise Exception("unable to parse GDB version: %r" % version) + return (version, int(match.group(1)), int(match.group(2))) + +gdb_version, gdb_major_version, gdb_minor_version = get_gdb_version() +if gdb_major_version < 7: + raise unittest.SkipTest("gdb versions before 7.0 didn't support python " + "embedding. Saw %s.%s:\n%s" + % (gdb_major_version, gdb_minor_version, + gdb_version)) + +if not sysconfig.is_python_build(): + raise unittest.SkipTest("test_gdb only works on source builds at the moment.") + +if ((sysconfig.get_config_var('PGO_PROF_USE_FLAG') or 'xxx') in + (sysconfig.get_config_var('PY_CORE_CFLAGS') or '')): + raise unittest.SkipTest("test_gdb is not reliable on PGO builds") + +# Location of custom hooks file in a repository checkout. +checkout_hook_path = os.path.join(os.path.dirname(sys.executable), + 'python-gdb.py') + +PYTHONHASHSEED = '123' + + +def cet_protection(): + cflags = sysconfig.get_config_var('CFLAGS') + if not cflags: + return False + flags = cflags.split() + # True if "-mcet -fcf-protection" options are found, but false + # if "-fcf-protection=none" or "-fcf-protection=return" is found. + return (('-mcet' in flags) + and any((flag.startswith('-fcf-protection') + and not flag.endswith(("=none", "=return"))) + for flag in flags)) + +# Control-flow enforcement technology +CET_PROTECTION = cet_protection() + + +def run_gdb(*args, **env_vars): + """Runs gdb in --batch mode with the additional arguments given by *args. + + Returns its (stdout, stderr) decoded from utf-8 using the replace handler. + """ + if env_vars: + env = os.environ.copy() + env.update(env_vars) + else: + env = None + # -nx: Do not execute commands from any .gdbinit initialization files + # (issue #22188) + base_cmd = ('gdb', '--batch', '-nx') + if (gdb_major_version, gdb_minor_version) >= (7, 4): + base_cmd += ('-iex', 'add-auto-load-safe-path ' + checkout_hook_path) + proc = subprocess.Popen(base_cmd + args, + # Redirect stdin to prevent GDB from messing with + # the terminal settings + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=env) + with proc: + out, err = proc.communicate() + return out.decode('utf-8', 'replace'), err.decode('utf-8', 'replace') + +# Verify that "gdb" was built with the embedded python support enabled: +gdbpy_version, _ = run_gdb("--eval-command=python import sys; print(sys.version_info)") +if not gdbpy_version: + raise unittest.SkipTest("gdb not built with embedded python support") + +if "major=2" in gdbpy_version: + raise unittest.SkipTest("gdb built with Python 2") + +# Verify that "gdb" can load our custom hooks, as OS security settings may +# disallow this without a customized .gdbinit. +_, gdbpy_errors = run_gdb('--args', sys.executable) +if "auto-loading has been declined" in gdbpy_errors: + msg = "gdb security settings prevent use of custom hooks: " + raise unittest.SkipTest(msg + gdbpy_errors.rstrip()) + +BREAKPOINT_FN='builtin_id' + + +def setup_module(): + if support.verbose: + print("GDB version %s.%s:" % (gdb_major_version, gdb_minor_version)) + for line in gdb_version.splitlines(): + print(" " * 4 + line) + + +@unittest.skipIf(support.PGO, "not useful for PGO") +class DebuggerTests(unittest.TestCase): + + """Test that the debugger can debug Python.""" + + def get_stack_trace(self, source=None, script=None, + breakpoint=BREAKPOINT_FN, + cmds_after_breakpoint=None, + import_site=False, + ignore_stderr=False): + ''' + Run 'python -c SOURCE' under gdb with a breakpoint. + + Support injecting commands after the breakpoint is reached + + Returns the stdout from gdb + + cmds_after_breakpoint: if provided, a list of strings: gdb commands + ''' + # We use "set breakpoint pending yes" to avoid blocking with a: + # Function "foo" not defined. + # Make breakpoint pending on future shared library load? (y or [n]) + # error, which typically happens python is dynamically linked (the + # breakpoints of interest are to be found in the shared library) + # When this happens, we still get: + # Function "textiowrapper_write" not defined. + # emitted to stderr each time, alas. + + # Initially I had "--eval-command=continue" here, but removed it to + # avoid repeated print breakpoints when traversing hierarchical data + # structures + + # Generate a list of commands in gdb's language: + commands = ['set breakpoint pending yes', + 'break %s' % breakpoint, + + # The tests assume that the first frame of printed + # backtrace will not contain program counter, + # that is however not guaranteed by gdb + # therefore we need to use 'set print address off' to + # make sure the counter is not there. For example: + # #0 in PyObject_Print ... + # is assumed, but sometimes this can be e.g. + # #0 0x00003fffb7dd1798 in PyObject_Print ... + 'set print address off', + + 'run'] + + # GDB as of 7.4 onwards can distinguish between the + # value of a variable at entry vs current value: + # http://sourceware.org/gdb/onlinedocs/gdb/Variables.html + # which leads to the selftests failing with errors like this: + # AssertionError: 'v@entry=()' != '()' + # Disable this: + if (gdb_major_version, gdb_minor_version) >= (7, 4): + commands += ['set print entry-values no'] + + if cmds_after_breakpoint: + if CET_PROTECTION: + # bpo-32962: When Python is compiled with -mcet + # -fcf-protection, function arguments are unusable before + # running the first instruction of the function entry point. + # The 'next' command makes the required first step. + commands += ['next'] + commands += cmds_after_breakpoint + else: + commands += ['backtrace'] + + # print commands + + # Use "commands" to generate the arguments with which to invoke "gdb": + args = ['--eval-command=%s' % cmd for cmd in commands] + args += ["--args", + sys.executable] + args.extend(subprocess._args_from_interpreter_flags()) + + if not import_site: + # -S suppresses the default 'import site' + args += ["-S"] + + if source: + args += ["-c", source] + elif script: + args += [script] + + # Use "args" to invoke gdb, capturing stdout, stderr: + out, err = run_gdb(*args, PYTHONHASHSEED=PYTHONHASHSEED) + + if not ignore_stderr: + for line in err.splitlines(): + print(line, file=sys.stderr) + + # bpo-34007: Sometimes some versions of the shared libraries that + # are part of the traceback are compiled in optimised mode and the + # Program Counter (PC) is not present, not allowing gdb to walk the + # frames back. When this happens, the Python bindings of gdb raise + # an exception, making the test impossible to succeed. + if "PC not saved" in err: + raise unittest.SkipTest("gdb cannot walk the frame object" + " because the Program Counter is" + " not present") + + # bpo-40019: Skip the test if gdb failed to read debug information + # because the Python binary is optimized. + for pattern in ( + '(frame information optimized out)', + 'Unable to read information on python frame', + # gh-91960: On Python built with "clang -Og", gdb gets + # "frame=" for _PyEval_EvalFrameDefault() parameter + '(unable to read python frame information)', + # gh-104736: On Python built with "clang -Og" on ppc64le, + # "py-bt" displays a truncated or not traceback, but "where" + # logs this error message: + 'Backtrace stopped: frame did not save the PC', + # gh-104736: When "bt" command displays something like: + # "#1 0x0000000000000000 in ?? ()", the traceback is likely + # truncated or wrong. + ' ?? ()', + ): + if pattern in out: + raise unittest.SkipTest(f"{pattern!r} found in gdb output") + + return out + + def get_gdb_repr(self, source, + cmds_after_breakpoint=None, + import_site=False): + # Given an input python source representation of data, + # run "python -c'id(DATA)'" under gdb with a breakpoint on + # builtin_id and scrape out gdb's representation of the "op" + # parameter, and verify that the gdb displays the same string + # + # Verify that the gdb displays the expected string + # + # For a nested structure, the first time we hit the breakpoint will + # give us the top-level structure + + # NOTE: avoid decoding too much of the traceback as some + # undecodable characters may lurk there in optimized mode + # (issue #19743). + cmds_after_breakpoint = cmds_after_breakpoint or ["backtrace 1"] + gdb_output = self.get_stack_trace(source, breakpoint=BREAKPOINT_FN, + cmds_after_breakpoint=cmds_after_breakpoint, + import_site=import_site) + # gdb can insert additional '\n' and space characters in various places + # in its output, depending on the width of the terminal it's connected + # to (using its "wrap_here" function) + m = re.search( + # Match '#0 builtin_id(self=..., v=...)' + r'#0\s+builtin_id\s+\(self\=.*,\s+v=\s*(.*?)?\)' + # Match ' at Python/bltinmodule.c'. + # bpo-38239: builtin_id() is defined in Python/bltinmodule.c, + # but accept any "Directory\file.c" to support Link Time + # Optimization (LTO). + r'\s+at\s+\S*[A-Za-z]+/[A-Za-z0-9_-]+\.c', + gdb_output, re.DOTALL) + if not m: + self.fail('Unexpected gdb output: %r\n%s' % (gdb_output, gdb_output)) + return m.group(1), gdb_output + + def assertEndsWith(self, actual, exp_end): + '''Ensure that the given "actual" string ends with "exp_end"''' + self.assertTrue(actual.endswith(exp_end), + msg='%r did not end with %r' % (actual, exp_end)) + + def assertMultilineMatches(self, actual, pattern): + m = re.match(pattern, actual, re.DOTALL) + if not m: + self.fail(msg='%r did not match %r' % (actual, pattern)) + + def get_sample_script(self): + return os.path.join(os.path.dirname(__file__), 'gdb_sample.py') diff --git a/Makefile.pre.in b/Makefile.pre.in index 0d0ac1315bdc7d..ba8e6349e815de 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1966,6 +1966,7 @@ TESTSUBDIRS= ctypes/test \ test/test_email \ test/test_email/data \ test/test_future_stmt \ + test/test_gdb \ test/test_import \ test/test_import/data \ test/test_import/data/circular_imports \ diff --git a/Misc/NEWS.d/next/Tests/2023-09-28-12-25-19.gh-issue-109972.GYnwIP.rst b/Misc/NEWS.d/next/Tests/2023-09-28-12-25-19.gh-issue-109972.GYnwIP.rst new file mode 100644 index 00000000000000..7b6007678388b1 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-09-28-12-25-19.gh-issue-109972.GYnwIP.rst @@ -0,0 +1,2 @@ +Split test_gdb.py file into a test_gdb package made of multiple tests, so tests +can now be run in parallel. Patch by Victor Stinner. From f9ac377626ff993569b532e693acf3f08a6c1f43 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 4 Oct 2023 12:58:49 +0200 Subject: [PATCH 409/632] [3.11] Add test.support.busy_retry() (#93770) (#110341) Add test.support.busy_retry() (#93770) Add busy_retry() and sleeping_retry() functions to test.support. (cherry picked from commit 7e9eaad864349d2cfd4c9ffc4453aba03b2cbc16) --- Doc/library/test.rst | 45 +++++++++++ Lib/test/_test_multiprocessing.py | 60 ++++++--------- Lib/test/fork_wait.py | 6 +- Lib/test/support/__init__.py | 76 +++++++++++++++++++ Lib/test/test__xxsubinterpreters.py | 11 ++- Lib/test/test_concurrent_futures/test_init.py | 9 +-- .../test_multiprocessing_main_handling.py | 25 +++--- Lib/test/test_signal.py | 25 +++--- Lib/test/test_ssl.py | 5 +- Lib/test/test_support.py | 12 +-- Lib/test/test_wait3.py | 5 +- Lib/test/test_wait4.py | 5 +- 12 files changed, 185 insertions(+), 99 deletions(-) diff --git a/Doc/library/test.rst b/Doc/library/test.rst index 427953e0077aab..d3eb0ae00a08dc 100644 --- a/Doc/library/test.rst +++ b/Doc/library/test.rst @@ -404,6 +404,51 @@ The :mod:`test.support` module defines the following constants: The :mod:`test.support` module defines the following functions: +.. function:: busy_retry(timeout, err_msg=None, /, *, error=True) + + Run the loop body until ``break`` stops the loop. + + After *timeout* seconds, raise an :exc:`AssertionError` if *error* is true, + or just stop the loop if *error* is false. + + Example:: + + for _ in support.busy_retry(support.SHORT_TIMEOUT): + if check(): + break + + Example of error=False usage:: + + for _ in support.busy_retry(support.SHORT_TIMEOUT, error=False): + if check(): + break + else: + raise RuntimeError('my custom error') + +.. function:: sleeping_retry(timeout, err_msg=None, /, *, init_delay=0.010, max_delay=1.0, error=True) + + Wait strategy that applies exponential backoff. + + Run the loop body until ``break`` stops the loop. Sleep at each loop + iteration, but not at the first iteration. The sleep delay is doubled at + each iteration (up to *max_delay* seconds). + + See :func:`busy_retry` documentation for the parameters usage. + + Example raising an exception after SHORT_TIMEOUT seconds:: + + for _ in support.sleeping_retry(support.SHORT_TIMEOUT): + if check(): + break + + Example of error=False usage:: + + for _ in support.sleeping_retry(support.SHORT_TIMEOUT, error=False): + if check(): + break + else: + raise RuntimeError('my custom error') + .. function:: is_resource_enabled(resource) Return ``True`` if *resource* is enabled and available. The list of diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 6249062639b8d4..6459757eb93d51 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -4376,18 +4376,13 @@ def test_shared_memory_cleaned_after_process_termination(self): p.terminate() p.wait() - deadline = time.monotonic() + support.LONG_TIMEOUT - t = 0.1 - while time.monotonic() < deadline: - time.sleep(t) - t = min(t*2, 5) + err_msg = ("A SharedMemory segment was leaked after " + "a process was abruptly terminated") + for _ in support.sleeping_retry(support.LONG_TIMEOUT, err_msg): try: smm = shared_memory.SharedMemory(name, create=False) except FileNotFoundError: break - else: - raise AssertionError("A SharedMemory segment was leaked after" - " a process was abruptly terminated.") if os.name == 'posix': # Without this line it was raising warnings like: @@ -5458,9 +5453,10 @@ def create_and_register_resource(rtype): p.terminate() p.wait() - deadline = time.monotonic() + support.LONG_TIMEOUT - while time.monotonic() < deadline: - time.sleep(.5) + err_msg = (f"A {rtype} resource was leaked after a process was " + f"abruptly terminated") + for _ in support.sleeping_retry(support.SHORT_TIMEOUT, + err_msg): try: _resource_unlink(name2, rtype) except OSError as e: @@ -5468,10 +5464,7 @@ def create_and_register_resource(rtype): # EINVAL self.assertIn(e.errno, (errno.ENOENT, errno.EINVAL)) break - else: - raise AssertionError( - f"A {rtype} resource was leaked after a process was " - f"abruptly terminated.") + err = p.stderr.read().decode('utf-8') p.stderr.close() expected = ('resource_tracker: There appear to be 2 leaked {} ' @@ -5707,18 +5700,17 @@ def wait_proc_exit(self): # but this can take a bit on slow machines, so wait a few seconds # if there are other children too (see #17395). join_process(self.proc) + start_time = time.monotonic() - t = 0.01 - while len(multiprocessing.active_children()) > 1: - time.sleep(t) - t *= 2 - dt = time.monotonic() - start_time - if dt >= 5.0: - test.support.environment_altered = True - support.print_warning(f"multiprocessing.Manager still has " - f"{multiprocessing.active_children()} " - f"active children after {dt} seconds") + for _ in support.sleeping_retry(5.0, error=False): + if len(multiprocessing.active_children()) <= 1: break + else: + dt = time.monotonic() - start_time + support.environment_altered = True + support.print_warning(f"multiprocessing.Manager still has " + f"{multiprocessing.active_children()} " + f"active children after {dt:.1f} seconds") def run_worker(self, worker, obj): self.proc = multiprocessing.Process(target=worker, args=(obj, )) @@ -6031,17 +6023,15 @@ def tearDownClass(cls): # but this can take a bit on slow machines, so wait a few seconds # if there are other children too (see #17395) start_time = time.monotonic() - t = 0.01 - while len(multiprocessing.active_children()) > 1: - time.sleep(t) - t *= 2 - dt = time.monotonic() - start_time - if dt >= 5.0: - test.support.environment_altered = True - support.print_warning(f"multiprocessing.Manager still has " - f"{multiprocessing.active_children()} " - f"active children after {dt} seconds") + for _ in support.sleeping_retry(5.0, error=False): + if len(multiprocessing.active_children()) <= 1: break + else: + dt = time.monotonic() - start_time + support.environment_altered = True + support.print_warning(f"multiprocessing.Manager still has " + f"{multiprocessing.active_children()} " + f"active children after {dt:.1f} seconds") gc.collect() # do garbage collection if cls.manager._number_of_objects() != 0: diff --git a/Lib/test/fork_wait.py b/Lib/test/fork_wait.py index 4d3dbd8e83f5a9..c565f593559483 100644 --- a/Lib/test/fork_wait.py +++ b/Lib/test/fork_wait.py @@ -54,10 +54,8 @@ def test_wait(self): self.threads.append(thread) # busy-loop to wait for threads - deadline = time.monotonic() + support.SHORT_TIMEOUT - while len(self.alive) < NUM_THREADS: - time.sleep(0.1) - if deadline < time.monotonic(): + for _ in support.sleeping_retry(support.SHORT_TIMEOUT, error=False): + if len(self.alive) >= NUM_THREADS: break a = sorted(self.alive.keys()) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 2e6518cf241f14..6ee525c061f624 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -2324,6 +2324,82 @@ def requires_venv_with_pip(): return unittest.skipUnless(ctypes, 'venv: pip requires ctypes') +def busy_retry(timeout, err_msg=None, /, *, error=True): + """ + Run the loop body until "break" stops the loop. + + After *timeout* seconds, raise an AssertionError if *error* is true, + or just stop if *error is false. + + Example: + + for _ in support.busy_retry(support.SHORT_TIMEOUT): + if check(): + break + + Example of error=False usage: + + for _ in support.busy_retry(support.SHORT_TIMEOUT, error=False): + if check(): + break + else: + raise RuntimeError('my custom error') + + """ + if timeout <= 0: + raise ValueError("timeout must be greater than zero") + + start_time = time.monotonic() + deadline = start_time + timeout + + while True: + yield + + if time.monotonic() >= deadline: + break + + if error: + dt = time.monotonic() - start_time + msg = f"timeout ({dt:.1f} seconds)" + if err_msg: + msg = f"{msg}: {err_msg}" + raise AssertionError(msg) + + +def sleeping_retry(timeout, err_msg=None, /, + *, init_delay=0.010, max_delay=1.0, error=True): + """ + Wait strategy that applies exponential backoff. + + Run the loop body until "break" stops the loop. Sleep at each loop + iteration, but not at the first iteration. The sleep delay is doubled at + each iteration (up to *max_delay* seconds). + + See busy_retry() documentation for the parameters usage. + + Example raising an exception after SHORT_TIMEOUT seconds: + + for _ in support.sleeping_retry(support.SHORT_TIMEOUT): + if check(): + break + + Example of error=False usage: + + for _ in support.sleeping_retry(support.SHORT_TIMEOUT, error=False): + if check(): + break + else: + raise RuntimeError('my custom error') + """ + + delay = init_delay + for _ in busy_retry(timeout, err_msg, error=error): + yield + + time.sleep(delay) + delay = min(delay * 2, max_delay) + + @contextlib.contextmanager def adjust_int_max_str_digits(max_digits): """Temporarily change the integer string conversion length limit.""" diff --git a/Lib/test/test__xxsubinterpreters.py b/Lib/test/test__xxsubinterpreters.py index 5d0ed9ea14ac7f..f20aae8e21c66f 100644 --- a/Lib/test/test__xxsubinterpreters.py +++ b/Lib/test/test__xxsubinterpreters.py @@ -45,12 +45,11 @@ def _wait_for_interp_to_run(interp, timeout=None): # run subinterpreter eariler than the main thread in multiprocess. if timeout is None: timeout = support.SHORT_TIMEOUT - start_time = time.monotonic() - deadline = start_time + timeout - while not interpreters.is_running(interp): - if time.monotonic() > deadline: - raise RuntimeError('interp is not running') - time.sleep(0.010) + for _ in support.sleeping_retry(timeout, error=False): + if interpreters.is_running(interp): + break + else: + raise RuntimeError('interp is not running') @contextlib.contextmanager diff --git a/Lib/test/test_concurrent_futures/test_init.py b/Lib/test/test_concurrent_futures/test_init.py index 138d6ee545fd92..ebab9437b9c4e1 100644 --- a/Lib/test/test_concurrent_futures/test_init.py +++ b/Lib/test/test_concurrent_futures/test_init.py @@ -78,11 +78,10 @@ def test_initializer(self): future.result() # At some point, the executor should break - t1 = time.monotonic() - while not self.executor._broken: - if time.monotonic() - t1 > 5: - self.fail("executor not broken after 5 s.") - time.sleep(0.01) + for _ in support.sleeping_retry(5, "executor not broken"): + if self.executor._broken: + break + # ... and from this point submit() is guaranteed to fail with self.assertRaises(BrokenExecutor): self.executor.submit(get_init_status) diff --git a/Lib/test/test_multiprocessing_main_handling.py b/Lib/test/test_multiprocessing_main_handling.py index 510d8d3a7597e1..35e9cd64fa6c01 100644 --- a/Lib/test/test_multiprocessing_main_handling.py +++ b/Lib/test/test_multiprocessing_main_handling.py @@ -40,6 +40,7 @@ import sys import time from multiprocessing import Pool, set_start_method +from test import support # We use this __main__ defined function in the map call below in order to # check that multiprocessing in correctly running the unguarded @@ -59,13 +60,11 @@ def f(x): results = [] with Pool(5) as pool: pool.map_async(f, [1, 2, 3], callback=results.extend) - start_time = time.monotonic() - while not results: - time.sleep(0.05) - # up to 1 min to report the results - dt = time.monotonic() - start_time - if dt > 60.0: - raise RuntimeError("Timed out waiting for results (%.1f sec)" % dt) + + # up to 1 min to report the results + for _ in support.sleeping_retry(60, "Timed out waiting for results"): + if results: + break results.sort() print(start_method, "->", results) @@ -86,19 +85,17 @@ def f(x): import sys import time from multiprocessing import Pool, set_start_method +from test import support start_method = sys.argv[1] set_start_method(start_method) results = [] with Pool(5) as pool: pool.map_async(int, [1, 4, 9], callback=results.extend) - start_time = time.monotonic() - while not results: - time.sleep(0.05) - # up to 1 min to report the results - dt = time.monotonic() - start_time - if dt > 60.0: - raise RuntimeError("Timed out waiting for results (%.1f sec)" % dt) + # up to 1 min to report the results + for _ in support.sleeping_retry(60, "Timed out waiting for results"): + if results: + break results.sort() print(start_method, "->", results) diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py index f6632896100a66..a00c7e539400d9 100644 --- a/Lib/test/test_signal.py +++ b/Lib/test/test_signal.py @@ -813,13 +813,14 @@ def test_itimer_virtual(self): signal.signal(signal.SIGVTALRM, self.sig_vtalrm) signal.setitimer(self.itimer, 0.3, 0.2) - start_time = time.monotonic() - while time.monotonic() - start_time < 60.0: + for _ in support.busy_retry(60.0, error=False): # use up some virtual time by doing real work _ = pow(12345, 67890, 10000019) if signal.getitimer(self.itimer) == (0.0, 0.0): - break # sig_vtalrm handler stopped this itimer - else: # Issue 8424 + # sig_vtalrm handler stopped this itimer + break + else: + # bpo-8424 self.skipTest("timeout: likely cause: machine too slow or load too " "high") @@ -833,13 +834,14 @@ def test_itimer_prof(self): signal.signal(signal.SIGPROF, self.sig_prof) signal.setitimer(self.itimer, 0.2, 0.2) - start_time = time.monotonic() - while time.monotonic() - start_time < 60.0: + for _ in support.busy_retry(60.0, error=False): # do some work _ = pow(12345, 67890, 10000019) if signal.getitimer(self.itimer) == (0.0, 0.0): - break # sig_prof handler stopped this itimer - else: # Issue 8424 + # sig_prof handler stopped this itimer + break + else: + # bpo-8424 self.skipTest("timeout: likely cause: machine too slow or load too " "high") @@ -1308,8 +1310,6 @@ def handler(signum, frame): self.setsig(signal.SIGALRM, handler) # for ITIMER_REAL expected_sigs = 0 - deadline = time.monotonic() + support.SHORT_TIMEOUT - while expected_sigs < N: # Hopefully the SIGALRM will be received somewhere during # initial processing of SIGUSR1. @@ -1318,8 +1318,9 @@ def handler(signum, frame): expected_sigs += 2 # Wait for handlers to run to avoid signal coalescing - while len(sigs) < expected_sigs and time.monotonic() < deadline: - time.sleep(1e-5) + for _ in support.sleeping_retry(support.SHORT_TIMEOUT, error=False): + if len(sigs) >= expected_sigs: + break # All ITIMER_REAL signals should have been delivered to the # Python handler diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 965c2728914b50..1bfec237484124 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -2279,11 +2279,8 @@ def ssl_io_loop(self, sock, incoming, outgoing, func, *args, **kwargs): # A simple IO loop. Call func(*args) depending on the error we get # (WANT_READ or WANT_WRITE) move data between the socket and the BIOs. timeout = kwargs.get('timeout', support.SHORT_TIMEOUT) - deadline = time.monotonic() + timeout count = 0 - while True: - if time.monotonic() > deadline: - self.fail("timeout") + for _ in support.busy_retry(timeout): errno = None count += 1 try: diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index 01ba88ce42c5f6..fec96bef4bbd22 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -10,7 +10,6 @@ import sysconfig import tempfile import textwrap -import time import unittest import warnings @@ -462,18 +461,12 @@ def test_reap_children(self): # child process: do nothing, just exit os._exit(0) - t0 = time.monotonic() - deadline = time.monotonic() + support.SHORT_TIMEOUT - was_altered = support.environment_altered try: support.environment_altered = False stderr = io.StringIO() - while True: - if time.monotonic() > deadline: - self.fail("timeout") - + for _ in support.sleeping_retry(support.SHORT_TIMEOUT): with support.swap_attr(support.print_warning, 'orig_stderr', stderr): support.reap_children() @@ -482,9 +475,6 @@ def test_reap_children(self): if support.environment_altered: break - # loop until the child process completed - time.sleep(0.100) - msg = "Warning -- reap_children() reaped child process %s" % pid self.assertIn(msg, stderr.getvalue()) self.assertTrue(support.environment_altered) diff --git a/Lib/test/test_wait3.py b/Lib/test/test_wait3.py index 4ec7690ac19bbf..15d66ae825abf5 100644 --- a/Lib/test/test_wait3.py +++ b/Lib/test/test_wait3.py @@ -4,7 +4,6 @@ import os import subprocess import sys -import time import unittest from test.fork_wait import ForkWait from test import support @@ -20,14 +19,12 @@ def wait_impl(self, cpid, *, exitcode): # This many iterations can be required, since some previously run # tests (e.g. test_ctypes) could have spawned a lot of children # very quickly. - deadline = time.monotonic() + support.SHORT_TIMEOUT - while time.monotonic() <= deadline: + for _ in support.sleeping_retry(support.SHORT_TIMEOUT, error=False): # wait3() shouldn't hang, but some of the buildbots seem to hang # in the forking tests. This is an attempt to fix the problem. spid, status, rusage = os.wait3(os.WNOHANG) if spid == cpid: break - time.sleep(0.1) self.assertEqual(spid, cpid) self.assertEqual(os.waitstatus_to_exitcode(status), exitcode) diff --git a/Lib/test/test_wait4.py b/Lib/test/test_wait4.py index 24f1aaec60c56b..f66c0db1c20e6e 100644 --- a/Lib/test/test_wait4.py +++ b/Lib/test/test_wait4.py @@ -2,7 +2,6 @@ """ import os -import time import sys import unittest from test.fork_wait import ForkWait @@ -22,14 +21,12 @@ def wait_impl(self, cpid, *, exitcode): # Issue #11185: wait4 is broken on AIX and will always return 0 # with WNOHANG. option = 0 - deadline = time.monotonic() + support.SHORT_TIMEOUT - while time.monotonic() <= deadline: + for _ in support.sleeping_retry(support.SHORT_TIMEOUT, error=False): # wait4() shouldn't hang, but some of the buildbots seem to hang # in the forking tests. This is an attempt to fix the problem. spid, status, rusage = os.wait4(cpid, option) if spid == cpid: break - time.sleep(0.1) self.assertEqual(spid, cpid) self.assertEqual(os.waitstatus_to_exitcode(status), exitcode) self.assertTrue(rusage) From 497c8c4520e563e6b21f6707adb617542798e125 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 4 Oct 2023 04:09:43 -0700 Subject: [PATCH 410/632] [3.11] gh-108927: Fix test_import + test_importlib + test_unittest problem (GH-108929) (#110347) gh-108927: Fix test_import + test_importlib + test_unittest problem (GH-108929) (cherry picked from commit 3f89b257639dd817a32079da2ae2c4436b8e82eb) Co-authored-by: Nikita Sobolev --- Lib/unittest/test/test_discovery.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/unittest/test/test_discovery.py b/Lib/unittest/test/test_discovery.py index 3b58786ec16a10..c39ad2e3acfb29 100644 --- a/Lib/unittest/test/test_discovery.py +++ b/Lib/unittest/test/test_discovery.py @@ -6,7 +6,6 @@ import pickle from test import support from test.support import import_helper -import test.test_importlib.util import unittest import unittest.mock @@ -826,6 +825,8 @@ def restore(): 'as dotted module names') def test_discovery_failed_discovery(self): + from test.test_importlib import util + loader = unittest.TestLoader() package = types.ModuleType('package') @@ -837,7 +838,7 @@ def _import(packagename, *args, **kwargs): # Since loader.discover() can modify sys.path, restore it when done. with import_helper.DirsOnSysPath(): # Make sure to remove 'package' from sys.modules when done. - with test.test_importlib.util.uncache('package'): + with util.uncache('package'): with self.assertRaises(TypeError) as cm: loader.discover('package') self.assertEqual(str(cm.exception), From d507493078ccbc9bf75d0643716cf7b355e375f0 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 4 Oct 2023 04:17:38 -0700 Subject: [PATCH 411/632] [3.11] gh-110332: Remove mentions of `random.WichmannHill` from `test_zlib` (GH-110334) (#110348) gh-110332: Remove mentions of `random.WichmannHill` from `test_zlib` (GH-110334) (cherry picked from commit e9f2352b7b7503519790ee6f51c2e298cf390e75) Co-authored-by: Nikita Sobolev --- Lib/test/test_zlib.py | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/Lib/test/test_zlib.py b/Lib/test/test_zlib.py index f20aad051da960..ff5ce11b965b7c 100644 --- a/Lib/test/test_zlib.py +++ b/Lib/test/test_zlib.py @@ -516,18 +516,7 @@ def test_odd_flush(self): # Try 17K of data # generate random data stream - try: - # In 2.3 and later, WichmannHill is the RNG of the bug report - gen = random.WichmannHill() - except AttributeError: - try: - # 2.2 called it Random - gen = random.Random() - except AttributeError: - # others might simply have a single RNG - gen = random - gen.seed(1) - data = gen.randbytes(17 * 1024) + data = random.randbytes(17 * 1024) # compress, sync-flush, and decompress first = co.compress(data) From aa8d3db6f201470b55debfe8a301317f39268ff1 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 4 Oct 2023 05:03:28 -0700 Subject: [PATCH 412/632] [3.11] [3.12] gh-109972: Enhance test_gdb (GH-110026) (GH-110351) (#110354) [3.12] gh-109972: Enhance test_gdb (GH-110026) (GH-110351) * gh-109972: Enhance test_gdb (GH-110026) * Split test_pycfunction.py: add test_cfunction_full.py. Split the function into the following 6 functions. In verbose mode, these "pycfunction" tests now log each tested call. * test_pycfunction_noargs() * test_pycfunction_o() * test_pycfunction_varargs() * test_pycfunction_varargs_keywords() * test_pycfunction_fastcall() * test_pycfunction_fastcall_keywords() * Move get_gdb_repr() to PrettyPrintTests. * Replace DebuggerTests.get_sample_script() with SAMPLE_SCRIPT. * Rename checkout_hook_path to CHECKOUT_HOOK_PATH. * Rename gdb_version to GDB_VERSION_TEXT. * Replace (gdb_major_version, gdb_minor_version) with GDB_VERSION. * run_gdb() uses "backslashreplace" error handler instead of "replace". * Add check_gdb() function to util.py. * Enhance support.check_cflags_pgo(): check also for sysconfig PGO_PROF_USE_FLAG (if available) in compiler flags. * Move some SkipTest checks to test_gdb/__init__.py. * Elaborate why gdb cannot be tested on Windows: gdb doesn't support PDB debug symbol files. (cherry picked from commit 757cbd4f29c9e89b38b975e0463dc8ed331b2515) * gh-104736: Fix test_gdb tests on ppc64le with clang (GH-109360) Fix test_gdb on Python built with LLVM clang 16 on Linux ppc64le (ex: Fedora 38). Search patterns in gdb "bt" command output to detect when gdb fails to retrieve the traceback. For example, skip a test if "Backtrace stopped: frame did not save the PC" is found. (cherry picked from commit 44d9a71ea246e7c3fb478d9be62c16914be6c545) * gh-110166: Fix gdb CFunctionFullTests on ppc64le clang build (GH-110331) CFunctionFullTests now also runs "bt" command before "py-bt-full", similar to CFunctionTests which also runs "bt" command before "py-bt". So test_gdb can skip the test if patterns like "?? ()" are found in the gdb output. (cherry picked from commit bbce8bd05dd25c6e74487940fa1977485b52baf4) Co-authored-by: Victor Stinner (cherry picked from commit 1de9406f9136e3952b849487f0151be3c669a3ea) Co-authored-by: Victor Stinner --- Lib/test/support/__init__.py | 7 +- Lib/test/test_gdb/__init__.py | 24 +- Lib/test/test_gdb/test_backtrace.py | 6 +- Lib/test/test_gdb/test_cfunction.py | 114 ++++---- Lib/test/test_gdb/test_cfunction_full.py | 36 +++ Lib/test/test_gdb/test_misc.py | 20 +- Lib/test/test_gdb/test_pretty_print.py | 54 +++- Lib/test/test_gdb/util.py | 256 ++++++++---------- ...-09-13-05-58-09.gh-issue-104736.lA25Fu.rst | 4 + 9 files changed, 303 insertions(+), 218 deletions(-) create mode 100644 Lib/test/test_gdb/test_cfunction_full.py create mode 100644 Misc/NEWS.d/next/Tests/2023-09-13-05-58-09.gh-issue-104736.lA25Fu.rst diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 6ee525c061f624..fbbd59d719d193 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -769,14 +769,17 @@ def check_cflags_pgo(): # Check if Python was built with ./configure --enable-optimizations: # with Profile Guided Optimization (PGO). cflags_nodist = sysconfig.get_config_var('PY_CFLAGS_NODIST') or '' - pgo_options = ( + pgo_options = [ # GCC '-fprofile-use', # clang: -fprofile-instr-use=code.profclangd '-fprofile-instr-use', # ICC "-prof-use", - ) + ] + PGO_PROF_USE_FLAG = sysconfig.get_config_var('PGO_PROF_USE_FLAG') + if PGO_PROF_USE_FLAG: + pgo_options.append(PGO_PROF_USE_FLAG) return any(option in cflags_nodist for option in pgo_options) diff --git a/Lib/test/test_gdb/__init__.py b/Lib/test/test_gdb/__init__.py index 0261f59adf54bd..d74075e456792d 100644 --- a/Lib/test/test_gdb/__init__.py +++ b/Lib/test/test_gdb/__init__.py @@ -4,7 +4,27 @@ # Lib/test/test_jit_gdb.py import os -from test.support import load_package_tests +import sysconfig +import unittest +from test import support + + +MS_WINDOWS = (os.name == 'nt') +if MS_WINDOWS: + # On Windows, Python is usually built by MSVC. Passing /p:DebugSymbols=true + # option to MSBuild produces PDB debug symbols, but gdb doesn't support PDB + # debug symbol files. + raise unittest.SkipTest("test_gdb doesn't work on Windows") + +if support.PGO: + raise unittest.SkipTest("test_gdb is not useful for PGO") + +if not sysconfig.is_python_build(): + raise unittest.SkipTest("test_gdb only works on source builds at the moment.") + +if support.check_cflags_pgo(): + raise unittest.SkipTest("test_gdb is not reliable on PGO builds") + def load_tests(*args): - return load_package_tests(os.path.dirname(__file__), *args) + return support.load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/test_gdb/test_backtrace.py b/Lib/test/test_gdb/test_backtrace.py index 15cbcf169ab9e3..c41e7cb7c210de 100644 --- a/Lib/test/test_gdb/test_backtrace.py +++ b/Lib/test/test_gdb/test_backtrace.py @@ -3,7 +3,7 @@ from test import support from test.support import python_is_optimized -from .util import setup_module, DebuggerTests, CET_PROTECTION +from .util import setup_module, DebuggerTests, CET_PROTECTION, SAMPLE_SCRIPT def setUpModule(): @@ -15,7 +15,7 @@ class PyBtTests(DebuggerTests): "Python was compiled with optimizations") def test_bt(self): 'Verify that the "py-bt" command works' - bt = self.get_stack_trace(script=self.get_sample_script(), + bt = self.get_stack_trace(script=SAMPLE_SCRIPT, cmds_after_breakpoint=['py-bt']) self.assertMultilineMatches(bt, r'''^.* @@ -35,7 +35,7 @@ def test_bt(self): "Python was compiled with optimizations") def test_bt_full(self): 'Verify that the "py-bt-full" command works' - bt = self.get_stack_trace(script=self.get_sample_script(), + bt = self.get_stack_trace(script=SAMPLE_SCRIPT, cmds_after_breakpoint=['py-bt-full']) self.assertMultilineMatches(bt, r'''^.* diff --git a/Lib/test/test_gdb/test_cfunction.py b/Lib/test/test_gdb/test_cfunction.py index 55796d021511e1..0a62014923e61f 100644 --- a/Lib/test/test_gdb/test_cfunction.py +++ b/Lib/test/test_gdb/test_cfunction.py @@ -1,8 +1,6 @@ -import re import textwrap import unittest from test import support -from test.support import python_is_optimized from .util import setup_module, DebuggerTests @@ -11,10 +9,22 @@ def setUpModule(): setup_module() -@unittest.skipIf(python_is_optimized(), +@unittest.skipIf(support.python_is_optimized(), "Python was compiled with optimizations") @support.requires_resource('cpu') class CFunctionTests(DebuggerTests): + def check(self, func_name, cmd): + # Verify with "py-bt": + gdb_output = self.get_stack_trace( + cmd, + breakpoint=func_name, + cmds_after_breakpoint=['bt', 'py-bt'], + # bpo-45207: Ignore 'Function "meth_varargs" not + # defined.' message in stderr. + ignore_stderr=True, + ) + self.assertIn(f'\n.*") @@ -167,7 +167,7 @@ class PyLocalsTests(DebuggerTests): @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") def test_basic_command(self): - bt = self.get_stack_trace(script=self.get_sample_script(), + bt = self.get_stack_trace(script=SAMPLE_SCRIPT, cmds_after_breakpoint=['py-up', 'py-locals']) self.assertMultilineMatches(bt, r".*\nargs = \(1, 2, 3\)\n.*") @@ -176,7 +176,7 @@ def test_basic_command(self): @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") def test_locals_after_up(self): - bt = self.get_stack_trace(script=self.get_sample_script(), + bt = self.get_stack_trace(script=SAMPLE_SCRIPT, cmds_after_breakpoint=['py-up', 'py-up', 'py-locals']) self.assertMultilineMatches(bt, r'''^.* diff --git a/Lib/test/test_gdb/test_pretty_print.py b/Lib/test/test_gdb/test_pretty_print.py index e31dc66f29684a..dfc77d65ab16a4 100644 --- a/Lib/test/test_gdb/test_pretty_print.py +++ b/Lib/test/test_gdb/test_pretty_print.py @@ -3,7 +3,7 @@ from test import support from .util import ( - BREAKPOINT_FN, gdb_major_version, gdb_minor_version, + BREAKPOINT_FN, GDB_VERSION, run_gdb, setup_module, DebuggerTests) @@ -12,6 +12,42 @@ def setUpModule(): class PrettyPrintTests(DebuggerTests): + def get_gdb_repr(self, source, + cmds_after_breakpoint=None, + import_site=False): + # Given an input python source representation of data, + # run "python -c'id(DATA)'" under gdb with a breakpoint on + # builtin_id and scrape out gdb's representation of the "op" + # parameter, and verify that the gdb displays the same string + # + # Verify that the gdb displays the expected string + # + # For a nested structure, the first time we hit the breakpoint will + # give us the top-level structure + + # NOTE: avoid decoding too much of the traceback as some + # undecodable characters may lurk there in optimized mode + # (issue #19743). + cmds_after_breakpoint = cmds_after_breakpoint or ["backtrace 1"] + gdb_output = self.get_stack_trace(source, breakpoint=BREAKPOINT_FN, + cmds_after_breakpoint=cmds_after_breakpoint, + import_site=import_site) + # gdb can insert additional '\n' and space characters in various places + # in its output, depending on the width of the terminal it's connected + # to (using its "wrap_here" function) + m = re.search( + # Match '#0 builtin_id(self=..., v=...)' + r'#0\s+builtin_id\s+\(self\=.*,\s+v=\s*(.*?)?\)' + # Match ' at Python/bltinmodule.c'. + # bpo-38239: builtin_id() is defined in Python/bltinmodule.c, + # but accept any "Directory\file.c" to support Link Time + # Optimization (LTO). + r'\s+at\s+\S*[A-Za-z]+/[A-Za-z0-9_-]+\.c', + gdb_output, re.DOTALL) + if not m: + self.fail('Unexpected gdb output: %r\n%s' % (gdb_output, gdb_output)) + return m.group(1), gdb_output + def test_getting_backtrace(self): gdb_output = self.get_stack_trace('id(42)') self.assertTrue(BREAKPOINT_FN in gdb_output) @@ -75,15 +111,17 @@ def test_strings(self): # as GDB might have been linked against a different version # of Python with a different encoding and coercion policy # with respect to PEP 538 and PEP 540. - out, err = run_gdb( + stdout, stderr = run_gdb( '--eval-command', 'python import locale; print(locale.getpreferredencoding())') - encoding = out.rstrip() - if err or not encoding: + encoding = stdout + if stderr or not encoding: raise RuntimeError( - f'unable to determine the preferred encoding ' - f'of embedded Python in GDB: {err}') + f'unable to determine the Python locale preferred encoding ' + f'of embedded Python in GDB\n' + f'stdout={stdout!r}\n' + f'stderr={stderr!r}') def check_repr(text): try: @@ -122,7 +160,7 @@ def test_tuples(self): @support.requires_resource('cpu') def test_sets(self): 'Verify the pretty-printing of sets' - if (gdb_major_version, gdb_minor_version) < (7, 3): + if GDB_VERSION < (7, 3): self.skipTest("pretty-printing of sets needs gdb 7.3 or later") self.assertGdbRepr(set(), "set()") self.assertGdbRepr(set(['a']), "{'a'}") @@ -141,7 +179,7 @@ def test_sets(self): @support.requires_resource('cpu') def test_frozensets(self): 'Verify the pretty-printing of frozensets' - if (gdb_major_version, gdb_minor_version) < (7, 3): + if GDB_VERSION < (7, 3): self.skipTest("pretty-printing of frozensets needs gdb 7.3 or later") self.assertGdbRepr(frozenset(), "frozenset()") self.assertGdbRepr(frozenset(['a']), "frozenset({'a'})") diff --git a/Lib/test/test_gdb/util.py b/Lib/test/test_gdb/util.py index 30beb4e14285c7..7f4e3cba3534bd 100644 --- a/Lib/test/test_gdb/util.py +++ b/Lib/test/test_gdb/util.py @@ -1,5 +1,6 @@ import os import re +import shlex import subprocess import sys import sysconfig @@ -7,29 +8,74 @@ from test import support -MS_WINDOWS = (sys.platform == 'win32') -if MS_WINDOWS: - raise unittest.SkipTest("test_gdb doesn't work on Windows") +# Location of custom hooks file in a repository checkout. +CHECKOUT_HOOK_PATH = os.path.join(os.path.dirname(sys.executable), + 'python-gdb.py') + +SAMPLE_SCRIPT = os.path.join(os.path.dirname(__file__), 'gdb_sample.py') +BREAKPOINT_FN = 'builtin_id' + +PYTHONHASHSEED = '123' + + +def clean_environment(): + # Remove PYTHON* environment variables such as PYTHONHOME + return {name: value for name, value in os.environ.items() + if not name.startswith('PYTHON')} + + +# Temporary value until it's initialized by get_gdb_version() below +GDB_VERSION = (0, 0) + +def run_gdb(*args, exitcode=0, **env_vars): + """Runs gdb in --batch mode with the additional arguments given by *args. + + Returns its (stdout, stderr) decoded from utf-8 using the replace handler. + """ + env = clean_environment() + if env_vars: + env.update(env_vars) + + cmd = ['gdb', + # Batch mode: Exit after processing all the command files + # specified with -x/--command + '--batch', + # -nx: Do not execute commands from any .gdbinit initialization + # files (gh-66384) + '-nx'] + if GDB_VERSION >= (7, 4): + cmd.extend(('--init-eval-command', + f'add-auto-load-safe-path {CHECKOUT_HOOK_PATH}')) + cmd.extend(args) + + proc = subprocess.run( + cmd, + # Redirect stdin to prevent gdb from messing with the terminal settings + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + encoding="utf8", errors="backslashreplace", + env=env) + + stdout = proc.stdout + stderr = proc.stderr + if proc.returncode != exitcode: + cmd_text = shlex.join(cmd) + raise Exception(f"{cmd_text} failed with exit code {proc.returncode}, " + f"expected exit code {exitcode}:\n" + f"stdout={stdout!r}\n" + f"stderr={stderr!r}") + + return (stdout, stderr) def get_gdb_version(): try: - cmd = ["gdb", "-nx", "--version"] - proc = subprocess.Popen(cmd, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True) - with proc: - version, stderr = proc.communicate() - - if proc.returncode: - raise Exception(f"Command {' '.join(cmd)!r} failed " - f"with exit code {proc.returncode}: " - f"stdout={version!r} stderr={stderr!r}") + stdout, stderr = run_gdb('--version') except OSError: # This is what "no gdb" looks like. There may, however, be other # errors that manifest this way too. - raise unittest.SkipTest("Couldn't find gdb on the path") + raise unittest.SkipTest("Couldn't find gdb program on the path") # Regex to parse: # 'GNU gdb (GDB; SUSE Linux Enterprise 12) 7.7\n' -> 7.7 @@ -37,32 +83,48 @@ def get_gdb_version(): # 'GNU gdb 6.1.1 [FreeBSD]\n' -> 6.1 # 'GNU gdb (GDB) Fedora (7.5.1-37.fc18)\n' -> 7.5 # 'HP gdb 6.7 for HP Itanium (32 or 64 bit) and target HP-UX 11iv2 and 11iv3.\n' -> 6.7 - match = re.search(r"^(?:GNU|HP) gdb.*?\b(\d+)\.(\d+)", version) + match = re.search(r"^(?:GNU|HP) gdb.*?\b(\d+)\.(\d+)", stdout) if match is None: - raise Exception("unable to parse GDB version: %r" % version) - return (version, int(match.group(1)), int(match.group(2))) + raise Exception("unable to parse gdb version: %r" % stdout) + version_text = stdout + major = int(match.group(1)) + minor = int(match.group(2)) + version = (major, minor) + return (version_text, version) -gdb_version, gdb_major_version, gdb_minor_version = get_gdb_version() -if gdb_major_version < 7: - raise unittest.SkipTest("gdb versions before 7.0 didn't support python " - "embedding. Saw %s.%s:\n%s" - % (gdb_major_version, gdb_minor_version, - gdb_version)) +GDB_VERSION_TEXT, GDB_VERSION = get_gdb_version() +if GDB_VERSION < (7, 0): + raise unittest.SkipTest( + f"gdb versions before 7.0 didn't support python embedding. " + f"Saw gdb version {GDB_VERSION[0]}.{GDB_VERSION[1]}:\n" + f"{GDB_VERSION_TEXT}") -if not sysconfig.is_python_build(): - raise unittest.SkipTest("test_gdb only works on source builds at the moment.") -if ((sysconfig.get_config_var('PGO_PROF_USE_FLAG') or 'xxx') in - (sysconfig.get_config_var('PY_CORE_CFLAGS') or '')): - raise unittest.SkipTest("test_gdb is not reliable on PGO builds") +def check_usable_gdb(): + # Verify that "gdb" was built with the embedded Python support enabled and + # verify that "gdb" can load our custom hooks, as OS security settings may + # disallow this without a customized .gdbinit. + stdout, stderr = run_gdb( + '--eval-command=python import sys; print(sys.version_info)', + '--args', sys.executable) -# Location of custom hooks file in a repository checkout. -checkout_hook_path = os.path.join(os.path.dirname(sys.executable), - 'python-gdb.py') + if "auto-loading has been declined" in stderr: + raise unittest.SkipTest( + f"gdb security settings prevent use of custom hooks; " + f"stderr: {stderr!r}") -PYTHONHASHSEED = '123' + if not stdout: + raise unittest.SkipTest( + f"gdb not built with embedded python support; " + f"stderr: {stderr!r}") + + if "major=2" in stdout: + raise unittest.SkipTest("gdb built with Python 2") +check_usable_gdb() + +# Control-flow enforcement technology def cet_protection(): cflags = sysconfig.get_config_var('CFLAGS') if not cflags: @@ -74,63 +136,17 @@ def cet_protection(): and any((flag.startswith('-fcf-protection') and not flag.endswith(("=none", "=return"))) for flag in flags)) - -# Control-flow enforcement technology CET_PROTECTION = cet_protection() -def run_gdb(*args, **env_vars): - """Runs gdb in --batch mode with the additional arguments given by *args. - - Returns its (stdout, stderr) decoded from utf-8 using the replace handler. - """ - if env_vars: - env = os.environ.copy() - env.update(env_vars) - else: - env = None - # -nx: Do not execute commands from any .gdbinit initialization files - # (issue #22188) - base_cmd = ('gdb', '--batch', '-nx') - if (gdb_major_version, gdb_minor_version) >= (7, 4): - base_cmd += ('-iex', 'add-auto-load-safe-path ' + checkout_hook_path) - proc = subprocess.Popen(base_cmd + args, - # Redirect stdin to prevent GDB from messing with - # the terminal settings - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - env=env) - with proc: - out, err = proc.communicate() - return out.decode('utf-8', 'replace'), err.decode('utf-8', 'replace') - -# Verify that "gdb" was built with the embedded python support enabled: -gdbpy_version, _ = run_gdb("--eval-command=python import sys; print(sys.version_info)") -if not gdbpy_version: - raise unittest.SkipTest("gdb not built with embedded python support") - -if "major=2" in gdbpy_version: - raise unittest.SkipTest("gdb built with Python 2") - -# Verify that "gdb" can load our custom hooks, as OS security settings may -# disallow this without a customized .gdbinit. -_, gdbpy_errors = run_gdb('--args', sys.executable) -if "auto-loading has been declined" in gdbpy_errors: - msg = "gdb security settings prevent use of custom hooks: " - raise unittest.SkipTest(msg + gdbpy_errors.rstrip()) - -BREAKPOINT_FN='builtin_id' - - def setup_module(): if support.verbose: - print("GDB version %s.%s:" % (gdb_major_version, gdb_minor_version)) - for line in gdb_version.splitlines(): + print(f"gdb version {GDB_VERSION[0]}.{GDB_VERSION[1]}:") + for line in GDB_VERSION_TEXT.splitlines(): print(" " * 4 + line) + print() -@unittest.skipIf(support.PGO, "not useful for PGO") class DebuggerTests(unittest.TestCase): """Test that the debugger can debug Python.""" @@ -163,20 +179,22 @@ def get_stack_trace(self, source=None, script=None, # structures # Generate a list of commands in gdb's language: - commands = ['set breakpoint pending yes', - 'break %s' % breakpoint, - - # The tests assume that the first frame of printed - # backtrace will not contain program counter, - # that is however not guaranteed by gdb - # therefore we need to use 'set print address off' to - # make sure the counter is not there. For example: - # #0 in PyObject_Print ... - # is assumed, but sometimes this can be e.g. - # #0 0x00003fffb7dd1798 in PyObject_Print ... - 'set print address off', - - 'run'] + commands = [ + 'set breakpoint pending yes', + 'break %s' % breakpoint, + + # The tests assume that the first frame of printed + # backtrace will not contain program counter, + # that is however not guaranteed by gdb + # therefore we need to use 'set print address off' to + # make sure the counter is not there. For example: + # #0 in PyObject_Print ... + # is assumed, but sometimes this can be e.g. + # #0 0x00003fffb7dd1798 in PyObject_Print ... + 'set print address off', + + 'run', + ] # GDB as of 7.4 onwards can distinguish between the # value of a variable at entry vs current value: @@ -184,7 +202,7 @@ def get_stack_trace(self, source=None, script=None, # which leads to the selftests failing with errors like this: # AssertionError: 'v@entry=()' != '()' # Disable this: - if (gdb_major_version, gdb_minor_version) >= (7, 4): + if GDB_VERSION >= (7, 4): commands += ['set print entry-values no'] if cmds_after_breakpoint: @@ -237,13 +255,16 @@ def get_stack_trace(self, source=None, script=None, for pattern in ( '(frame information optimized out)', 'Unable to read information on python frame', + # gh-91960: On Python built with "clang -Og", gdb gets # "frame=" for _PyEval_EvalFrameDefault() parameter '(unable to read python frame information)', + # gh-104736: On Python built with "clang -Og" on ppc64le, # "py-bt" displays a truncated or not traceback, but "where" # logs this error message: 'Backtrace stopped: frame did not save the PC', + # gh-104736: When "bt" command displays something like: # "#1 0x0000000000000000 in ?? ()", the traceback is likely # truncated or wrong. @@ -254,42 +275,6 @@ def get_stack_trace(self, source=None, script=None, return out - def get_gdb_repr(self, source, - cmds_after_breakpoint=None, - import_site=False): - # Given an input python source representation of data, - # run "python -c'id(DATA)'" under gdb with a breakpoint on - # builtin_id and scrape out gdb's representation of the "op" - # parameter, and verify that the gdb displays the same string - # - # Verify that the gdb displays the expected string - # - # For a nested structure, the first time we hit the breakpoint will - # give us the top-level structure - - # NOTE: avoid decoding too much of the traceback as some - # undecodable characters may lurk there in optimized mode - # (issue #19743). - cmds_after_breakpoint = cmds_after_breakpoint or ["backtrace 1"] - gdb_output = self.get_stack_trace(source, breakpoint=BREAKPOINT_FN, - cmds_after_breakpoint=cmds_after_breakpoint, - import_site=import_site) - # gdb can insert additional '\n' and space characters in various places - # in its output, depending on the width of the terminal it's connected - # to (using its "wrap_here" function) - m = re.search( - # Match '#0 builtin_id(self=..., v=...)' - r'#0\s+builtin_id\s+\(self\=.*,\s+v=\s*(.*?)?\)' - # Match ' at Python/bltinmodule.c'. - # bpo-38239: builtin_id() is defined in Python/bltinmodule.c, - # but accept any "Directory\file.c" to support Link Time - # Optimization (LTO). - r'\s+at\s+\S*[A-Za-z]+/[A-Za-z0-9_-]+\.c', - gdb_output, re.DOTALL) - if not m: - self.fail('Unexpected gdb output: %r\n%s' % (gdb_output, gdb_output)) - return m.group(1), gdb_output - def assertEndsWith(self, actual, exp_end): '''Ensure that the given "actual" string ends with "exp_end"''' self.assertTrue(actual.endswith(exp_end), @@ -299,6 +284,3 @@ def assertMultilineMatches(self, actual, pattern): m = re.match(pattern, actual, re.DOTALL) if not m: self.fail(msg='%r did not match %r' % (actual, pattern)) - - def get_sample_script(self): - return os.path.join(os.path.dirname(__file__), 'gdb_sample.py') diff --git a/Misc/NEWS.d/next/Tests/2023-09-13-05-58-09.gh-issue-104736.lA25Fu.rst b/Misc/NEWS.d/next/Tests/2023-09-13-05-58-09.gh-issue-104736.lA25Fu.rst new file mode 100644 index 00000000000000..85c370fc87ac41 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-09-13-05-58-09.gh-issue-104736.lA25Fu.rst @@ -0,0 +1,4 @@ +Fix test_gdb on Python built with LLVM clang 16 on Linux ppc64le (ex: Fedora +38). Search patterns in gdb "bt" command output to detect when gdb fails to +retrieve the traceback. For example, skip a test if ``Backtrace stopped: frame +did not save the PC`` is found. Patch by Victor Stinner. From 8f22504d745c4a843ff30b6810ab606817480746 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 4 Oct 2023 14:07:57 +0200 Subject: [PATCH 413/632] [3.11] gh-109974: Fix threading lock_tests race conditions (#110057) (#110355) [3.12] gh-109974: Fix threading lock_tests race conditions (#110057) (#110346) * gh-109974: Fix threading lock_tests race conditions (#110057) Fix race conditions in test_threading lock tests. Wait until a condition is met rather than using time.sleep() with a hardcoded number of seconds. * Replace sleeping loops with support.sleeping_retry() which raises an exception on timeout. * Add wait_threads_blocked(nthread) which computes a sleep depending on the number of threads. Remove _wait() function. * test_set_and_clear(): use a way longer Event.wait() timeout. * BarrierTests.test_repr(): wait until the 2 threads are waiting for the barrier. Use a way longer timeout for Barrier.wait() timeout. * test_thread_leak() no longer needs to count len(threading.enumerate()): Bunch uses threading_helper.wait_threads_exit() internally which does it in wait_for_finished(). * Add BaseLockTests.wait_phase() which implements a timeout. test_reacquire() and test_recursion_count() use wait_phase(). (cherry picked from commit 4e356ad183eeb567783f4a87fd092573da1e9252) * gh-109974: Fix more threading lock_tests race conditions (#110089) * Add context manager on Bunch class. * Bunch now catchs exceptions on executed functions and re-raise them at __exit__() as an ExceptionGroup. * Rewrite BarrierProxy.test_default_timeout(). Use a single thread. Only check that barrier.wait() blocks for at least default timeout seconds. * test_with(): inline _with() function. (cherry picked from commit 743e3572ee940a6cf88fd518e5f4a447905ba5eb) (cherry picked from commit 1d032ea3d67e9725b63322f896d9aa727fd75521) --- Lib/test/lock_tests.py | 623 +++++++++++------- Lib/test/test_importlib/test_locks.py | 3 +- ...-09-29-00-19-21.gh-issue-109974.Sh_g-r.rst | 3 + 3 files changed, 378 insertions(+), 251 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2023-09-29-00-19-21.gh-issue-109974.Sh_g-r.rst diff --git a/Lib/test/lock_tests.py b/Lib/test/lock_tests.py index d5ac1319064721..c5502ac0cdbddf 100644 --- a/Lib/test/lock_tests.py +++ b/Lib/test/lock_tests.py @@ -20,54 +20,74 @@ "(no _at_fork_reinit method)") -def _wait(): - # A crude wait/yield function not relying on synchronization primitives. - time.sleep(0.01) +def wait_threads_blocked(nthread): + # Arbitrary sleep to wait until N threads are blocked, + # like waiting for a lock. + time.sleep(0.010 * nthread) + class Bunch(object): """ A bunch of threads. """ - def __init__(self, f, n, wait_before_exit=False): + def __init__(self, func, nthread, wait_before_exit=False): """ - Construct a bunch of `n` threads running the same function `f`. + Construct a bunch of `nthread` threads running the same function `func`. If `wait_before_exit` is True, the threads won't terminate until do_finish() is called. """ - self.f = f - self.n = n + self.func = func + self.nthread = nthread self.started = [] self.finished = [] + self.exceptions = [] self._can_exit = not wait_before_exit - self.wait_thread = threading_helper.wait_threads_exit() - self.wait_thread.__enter__() + self._wait_thread = None - def task(): - tid = threading.get_ident() - self.started.append(tid) - try: - f() - finally: - self.finished.append(tid) - while not self._can_exit: - _wait() + def task(self): + tid = threading.get_ident() + self.started.append(tid) + try: + self.func() + except BaseException as exc: + self.exceptions.append(exc) + finally: + self.finished.append(tid) + for _ in support.sleeping_retry(support.SHORT_TIMEOUT): + if self._can_exit: + break + + def __enter__(self): + self._wait_thread = threading_helper.wait_threads_exit(support.SHORT_TIMEOUT) + self._wait_thread.__enter__() try: - for i in range(n): - start_new_thread(task, ()) + for _ in range(self.nthread): + start_new_thread(self.task, ()) except: self._can_exit = True raise - def wait_for_started(self): - while len(self.started) < self.n: - _wait() + for _ in support.sleeping_retry(support.SHORT_TIMEOUT): + if len(self.started) >= self.nthread: + break + + return self + + def __exit__(self, exc_type, exc_value, traceback): + for _ in support.sleeping_retry(support.SHORT_TIMEOUT): + if len(self.finished) >= self.nthread: + break - def wait_for_finished(self): - while len(self.finished) < self.n: - _wait() - # Wait for threads exit - self.wait_thread.__exit__(None, None, None) + # Wait until threads completely exit according to _thread._count() + self._wait_thread.__exit__(None, None, None) + + # Break reference cycle + exceptions = self.exceptions + self.exceptions = None + if exceptions: + raise ExceptionGroup(f"{self.func} threads raised exceptions", + exceptions) def do_finish(self): self._can_exit = True @@ -95,6 +115,12 @@ class BaseLockTests(BaseTestCase): Tests for both recursive and non-recursive locks. """ + def wait_phase(self, phase, expected): + for _ in support.sleeping_retry(support.SHORT_TIMEOUT): + if len(phase) >= expected: + break + self.assertEqual(len(phase), expected) + def test_constructor(self): lock = self.locktype() del lock @@ -132,41 +158,57 @@ def test_try_acquire_contended(self): result = [] def f(): result.append(lock.acquire(False)) - Bunch(f, 1).wait_for_finished() + with Bunch(f, 1): + pass self.assertFalse(result[0]) lock.release() def test_acquire_contended(self): lock = self.locktype() lock.acquire() - N = 5 def f(): lock.acquire() lock.release() - b = Bunch(f, N) - b.wait_for_started() - _wait() - self.assertEqual(len(b.finished), 0) - lock.release() - b.wait_for_finished() - self.assertEqual(len(b.finished), N) + N = 5 + with Bunch(f, N) as bunch: + # Threads block on lock.acquire() + wait_threads_blocked(N) + self.assertEqual(len(bunch.finished), 0) + + # Threads unblocked + lock.release() + + self.assertEqual(len(bunch.finished), N) def test_with(self): lock = self.locktype() def f(): lock.acquire() lock.release() - def _with(err=None): + + def with_lock(err=None): with lock: if err is not None: raise err - _with() - # Check the lock is unacquired - Bunch(f, 1).wait_for_finished() - self.assertRaises(TypeError, _with, TypeError) - # Check the lock is unacquired - Bunch(f, 1).wait_for_finished() + + # Acquire the lock, do nothing, with releases the lock + with lock: + pass + + # Check that the lock is unacquired + with Bunch(f, 1): + pass + + # Acquire the lock, raise an exception, with releases the lock + with self.assertRaises(TypeError): + with lock: + raise TypeError + + # Check that the lock is unacquired even if after an exception + # was raised in the previous "with lock:" block + with Bunch(f, 1): + pass def test_thread_leak(self): # The lock shouldn't leak a Thread instance when used from a foreign @@ -175,17 +217,11 @@ def test_thread_leak(self): def f(): lock.acquire() lock.release() - n = len(threading.enumerate()) + # We run many threads in the hope that existing threads ids won't # be recycled. - Bunch(f, 15).wait_for_finished() - if len(threading.enumerate()) != n: - # There is a small window during which a Thread instance's - # target function has finished running, but the Thread is still - # alive and registered. Avoid spurious failures by waiting a - # bit more (seen on a buildbot). - time.sleep(0.4) - self.assertEqual(n, len(threading.enumerate())) + with Bunch(f, 15): + pass def test_timeout(self): lock = self.locktype() @@ -209,7 +245,8 @@ def f(): results.append(lock.acquire(timeout=0.5)) t2 = time.monotonic() results.append(t2 - t1) - Bunch(f, 1).wait_for_finished() + with Bunch(f, 1): + pass self.assertFalse(results[0]) self.assertTimeout(results[1], 0.5) @@ -243,15 +280,13 @@ def f(): phase.append(None) with threading_helper.wait_threads_exit(): + # Thread blocked on lock.acquire() start_new_thread(f, ()) - while len(phase) == 0: - _wait() - _wait() - self.assertEqual(len(phase), 1) + self.wait_phase(phase, 1) + + # Thread unblocked lock.release() - while len(phase) == 1: - _wait() - self.assertEqual(len(phase), 2) + self.wait_phase(phase, 2) def test_different_thread(self): # Lock can be released from a different thread. @@ -259,8 +294,8 @@ def test_different_thread(self): lock.acquire() def f(): lock.release() - b = Bunch(f, 1) - b.wait_for_finished() + with Bunch(f, 1): + pass lock.acquire() lock.release() @@ -350,21 +385,20 @@ def test_recursion_count(self): def f(): lock.acquire() phase.append(None) - while len(phase) == 1: - _wait() + + self.wait_phase(phase, 2) lock.release() phase.append(None) with threading_helper.wait_threads_exit(): + # Thread blocked on lock.acquire() start_new_thread(f, ()) - while len(phase) == 0: - _wait() - self.assertEqual(len(phase), 1) + self.wait_phase(phase, 1) self.assertEqual(0, lock._recursion_count()) + + # Thread unblocked phase.append(None) - while len(phase) == 2: - _wait() - self.assertEqual(len(phase), 3) + self.wait_phase(phase, 3) self.assertEqual(0, lock._recursion_count()) def test_different_thread(self): @@ -372,12 +406,12 @@ def test_different_thread(self): lock = self.locktype() def f(): lock.acquire() - b = Bunch(f, 1, True) - try: - self.assertRaises(RuntimeError, lock.release) - finally: - b.do_finish() - b.wait_for_finished() + + with Bunch(f, 1, True) as bunch: + try: + self.assertRaises(RuntimeError, lock.release) + finally: + bunch.do_finish() def test__is_owned(self): lock = self.locktype() @@ -389,7 +423,8 @@ def test__is_owned(self): result = [] def f(): result.append(lock._is_owned()) - Bunch(f, 1).wait_for_finished() + with Bunch(f, 1): + pass self.assertFalse(result[0]) lock.release() self.assertTrue(lock._is_owned()) @@ -422,12 +457,15 @@ def _check_notify(self, evt): def f(): results1.append(evt.wait()) results2.append(evt.wait()) - b = Bunch(f, N) - b.wait_for_started() - _wait() - self.assertEqual(len(results1), 0) - evt.set() - b.wait_for_finished() + + with Bunch(f, N): + # Threads blocked on first evt.wait() + wait_threads_blocked(N) + self.assertEqual(len(results1), 0) + + # Threads unblocked + evt.set() + self.assertEqual(results1, [True] * N) self.assertEqual(results2, [True] * N) @@ -450,35 +488,43 @@ def f(): r = evt.wait(0.5) t2 = time.monotonic() results2.append((r, t2 - t1)) - Bunch(f, N).wait_for_finished() + + with Bunch(f, N): + pass + self.assertEqual(results1, [False] * N) for r, dt in results2: self.assertFalse(r) self.assertTimeout(dt, 0.5) + # The event is set results1 = [] results2 = [] evt.set() - Bunch(f, N).wait_for_finished() + with Bunch(f, N): + pass + self.assertEqual(results1, [True] * N) for r, dt in results2: self.assertTrue(r) def test_set_and_clear(self): - # Issue #13502: check that wait() returns true even when the event is + # gh-57711: check that wait() returns true even when the event is # cleared before the waiting thread is woken up. - evt = self.eventtype() + event = self.eventtype() results = [] - timeout = 0.250 - N = 5 def f(): - results.append(evt.wait(timeout * 4)) - b = Bunch(f, N) - b.wait_for_started() - time.sleep(timeout) - evt.set() - evt.clear() - b.wait_for_finished() + results.append(event.wait(support.LONG_TIMEOUT)) + + N = 5 + with Bunch(f, N): + # Threads blocked on event.wait() + wait_threads_blocked(N) + + # Threads unblocked + event.set() + event.clear() + self.assertEqual(results, [True] * N) @requires_fork @@ -534,15 +580,14 @@ def _check_notify(self, cond): # Note that this test is sensitive to timing. If the worker threads # don't execute in a timely fashion, the main thread may think they # are further along then they are. The main thread therefore issues - # _wait() statements to try to make sure that it doesn't race ahead - # of the workers. + # wait_threads_blocked() statements to try to make sure that it doesn't + # race ahead of the workers. # Secondly, this test assumes that condition variables are not subject # to spurious wakeups. The absence of spurious wakeups is an implementation # detail of Condition Variables in current CPython, but in general, not # a guaranteed property of condition variables as a programming # construct. In particular, it is possible that this can no longer # be conveniently guaranteed should their implementation ever change. - N = 5 ready = [] results1 = [] results2 = [] @@ -551,58 +596,83 @@ def f(): cond.acquire() ready.append(phase_num) result = cond.wait() + cond.release() results1.append((result, phase_num)) + cond.acquire() ready.append(phase_num) + result = cond.wait() cond.release() results2.append((result, phase_num)) - b = Bunch(f, N) - b.wait_for_started() - # first wait, to ensure all workers settle into cond.wait() before - # we continue. See issues #8799 and #30727. - while len(ready) < 5: - _wait() - ready.clear() - self.assertEqual(results1, []) - # Notify 3 threads at first - cond.acquire() - cond.notify(3) - _wait() - phase_num = 1 - cond.release() - while len(results1) < 3: - _wait() - self.assertEqual(results1, [(True, 1)] * 3) - self.assertEqual(results2, []) - # make sure all awaken workers settle into cond.wait() - while len(ready) < 3: - _wait() - # Notify 5 threads: they might be in their first or second wait - cond.acquire() - cond.notify(5) - _wait() - phase_num = 2 - cond.release() - while len(results1) + len(results2) < 8: - _wait() - self.assertEqual(results1, [(True, 1)] * 3 + [(True, 2)] * 2) - self.assertEqual(results2, [(True, 2)] * 3) - # make sure all workers settle into cond.wait() - while len(ready) < 5: - _wait() - # Notify all threads: they are all in their second wait - cond.acquire() - cond.notify_all() - _wait() - phase_num = 3 - cond.release() - while len(results2) < 5: - _wait() - self.assertEqual(results1, [(True, 1)] * 3 + [(True,2)] * 2) - self.assertEqual(results2, [(True, 2)] * 3 + [(True, 3)] * 2) - b.wait_for_finished() + + N = 5 + with Bunch(f, N): + # first wait, to ensure all workers settle into cond.wait() before + # we continue. See issues #8799 and #30727. + for _ in support.sleeping_retry(support.SHORT_TIMEOUT): + if len(ready) >= N: + break + + ready.clear() + self.assertEqual(results1, []) + + # Notify 3 threads at first + count1 = 3 + cond.acquire() + cond.notify(count1) + wait_threads_blocked(count1) + + # Phase 1 + phase_num = 1 + cond.release() + for _ in support.sleeping_retry(support.SHORT_TIMEOUT): + if len(results1) >= count1: + break + + self.assertEqual(results1, [(True, 1)] * count1) + self.assertEqual(results2, []) + + # Wait until awaken workers are blocked on cond.wait() + for _ in support.sleeping_retry(support.SHORT_TIMEOUT): + if len(ready) >= count1 : + break + + # Notify 5 threads: they might be in their first or second wait + cond.acquire() + cond.notify(5) + wait_threads_blocked(N) + + # Phase 2 + phase_num = 2 + cond.release() + for _ in support.sleeping_retry(support.SHORT_TIMEOUT): + if len(results1) + len(results2) >= (N + count1): + break + + count2 = N - count1 + self.assertEqual(results1, [(True, 1)] * count1 + [(True, 2)] * count2) + self.assertEqual(results2, [(True, 2)] * count1) + + # Make sure all workers settle into cond.wait() + for _ in support.sleeping_retry(support.SHORT_TIMEOUT): + if len(ready) >= N: + break + + # Notify all threads: they are all in their second wait + cond.acquire() + cond.notify_all() + wait_threads_blocked(N) + + # Phase 3 + phase_num = 3 + cond.release() + for _ in support.sleeping_retry(support.SHORT_TIMEOUT): + if len(results2) >= N: + break + self.assertEqual(results1, [(True, 1)] * count1 + [(True, 2)] * count2) + self.assertEqual(results2, [(True, 2)] * count1 + [(True, 3)] * count2) def test_notify(self): cond = self.condtype() @@ -612,19 +682,23 @@ def test_notify(self): def test_timeout(self): cond = self.condtype() + timeout = 0.5 results = [] - N = 5 def f(): cond.acquire() t1 = time.monotonic() - result = cond.wait(0.5) + result = cond.wait(timeout) t2 = time.monotonic() cond.release() results.append((t2 - t1, result)) - Bunch(f, N).wait_for_finished() + + N = 5 + with Bunch(f, N): + pass self.assertEqual(len(results), N) + for dt, result in results: - self.assertTimeout(dt, 0.5) + self.assertTimeout(dt, timeout) # Note that conceptually (that"s the condition variable protocol) # a wait() may succeed even if no one notifies us and before any # timeout occurs. Spurious wakeups can occur. @@ -637,17 +711,16 @@ def test_waitfor(self): state = 0 def f(): with cond: - result = cond.wait_for(lambda : state==4) + result = cond.wait_for(lambda: state == 4) self.assertTrue(result) self.assertEqual(state, 4) - b = Bunch(f, 1) - b.wait_for_started() - for i in range(4): - time.sleep(0.01) - with cond: - state += 1 - cond.notify() - b.wait_for_finished() + + with Bunch(f, 1): + for i in range(4): + time.sleep(0.010) + with cond: + state += 1 + cond.notify() def test_waitfor_timeout(self): cond = self.condtype() @@ -661,15 +734,15 @@ def f(): self.assertFalse(result) self.assertTimeout(dt, 0.1) success.append(None) - b = Bunch(f, 1) - b.wait_for_started() - # Only increment 3 times, so state == 4 is never reached. - for i in range(3): - time.sleep(0.01) - with cond: - state += 1 - cond.notify() - b.wait_for_finished() + + with Bunch(f, 1): + # Only increment 3 times, so state == 4 is never reached. + for i in range(3): + time.sleep(0.010) + with cond: + state += 1 + cond.notify() + self.assertEqual(len(success), 1) @@ -698,73 +771,107 @@ def test_acquire_destroy(self): del sem def test_acquire_contended(self): - sem = self.semtype(7) + sem_value = 7 + sem = self.semtype(sem_value) sem.acquire() - N = 10 + sem_results = [] results1 = [] results2 = [] phase_num = 0 - def f(): + + def func(): sem_results.append(sem.acquire()) results1.append(phase_num) + sem_results.append(sem.acquire()) results2.append(phase_num) - b = Bunch(f, 10) - b.wait_for_started() - while len(results1) + len(results2) < 6: - _wait() - self.assertEqual(results1 + results2, [0] * 6) - phase_num = 1 - for i in range(7): - sem.release() - while len(results1) + len(results2) < 13: - _wait() - self.assertEqual(sorted(results1 + results2), [0] * 6 + [1] * 7) - phase_num = 2 - for i in range(6): + + def wait_count(count): + for _ in support.sleeping_retry(support.SHORT_TIMEOUT): + if len(results1) + len(results2) >= count: + break + + N = 10 + with Bunch(func, N): + # Phase 0 + count1 = sem_value - 1 + wait_count(count1) + self.assertEqual(results1 + results2, [0] * count1) + + # Phase 1 + phase_num = 1 + for i in range(sem_value): + sem.release() + count2 = sem_value + wait_count(count1 + count2) + self.assertEqual(sorted(results1 + results2), + [0] * count1 + [1] * count2) + + # Phase 2 + phase_num = 2 + count3 = (sem_value - 1) + for i in range(count3): + sem.release() + wait_count(count1 + count2 + count3) + self.assertEqual(sorted(results1 + results2), + [0] * count1 + [1] * count2 + [2] * count3) + # The semaphore is still locked + self.assertFalse(sem.acquire(False)) + + # Final release, to let the last thread finish + count4 = 1 sem.release() - while len(results1) + len(results2) < 19: - _wait() - self.assertEqual(sorted(results1 + results2), [0] * 6 + [1] * 7 + [2] * 6) - # The semaphore is still locked - self.assertFalse(sem.acquire(False)) - # Final release, to let the last thread finish - sem.release() - b.wait_for_finished() - self.assertEqual(sem_results, [True] * (6 + 7 + 6 + 1)) + + self.assertEqual(sem_results, + [True] * (count1 + count2 + count3 + count4)) def test_multirelease(self): - sem = self.semtype(7) + sem_value = 7 + sem = self.semtype(sem_value) sem.acquire() + results1 = [] results2 = [] phase_num = 0 - def f(): + def func(): sem.acquire() results1.append(phase_num) + sem.acquire() results2.append(phase_num) - b = Bunch(f, 10) - b.wait_for_started() - while len(results1) + len(results2) < 6: - _wait() - self.assertEqual(results1 + results2, [0] * 6) - phase_num = 1 - sem.release(7) - while len(results1) + len(results2) < 13: - _wait() - self.assertEqual(sorted(results1 + results2), [0] * 6 + [1] * 7) - phase_num = 2 - sem.release(6) - while len(results1) + len(results2) < 19: - _wait() - self.assertEqual(sorted(results1 + results2), [0] * 6 + [1] * 7 + [2] * 6) - # The semaphore is still locked - self.assertFalse(sem.acquire(False)) - # Final release, to let the last thread finish - sem.release() - b.wait_for_finished() + + def wait_count(count): + for _ in support.sleeping_retry(support.SHORT_TIMEOUT): + if len(results1) + len(results2) >= count: + break + + with Bunch(func, 10): + # Phase 0 + count1 = sem_value - 1 + wait_count(count1) + self.assertEqual(results1 + results2, [0] * count1) + + # Phase 1 + phase_num = 1 + count2 = sem_value + sem.release(count2) + wait_count(count1 + count2) + self.assertEqual(sorted(results1 + results2), + [0] * count1 + [1] * count2) + + # Phase 2 + phase_num = 2 + count3 = sem_value - 1 + sem.release(count3) + wait_count(count1 + count2 + count3) + self.assertEqual(sorted(results1 + results2), + [0] * count1 + [1] * count2 + [2] * count3) + # The semaphore is still locked + self.assertFalse(sem.acquire(False)) + + # Final release, to let the last thread finish + sem.release() def test_try_acquire(self): sem = self.semtype(2) @@ -781,7 +888,8 @@ def test_try_acquire_contended(self): def f(): results.append(sem.acquire(False)) results.append(sem.acquire(False)) - Bunch(f, 5).wait_for_finished() + with Bunch(f, 5): + pass # There can be a thread switch between acquiring the semaphore and # appending the result, therefore results will not necessarily be # ordered. @@ -807,12 +915,14 @@ def test_default_value(self): def f(): sem.acquire() sem.release() - b = Bunch(f, 1) - b.wait_for_started() - _wait() - self.assertFalse(b.finished) - sem.release() - b.wait_for_finished() + + with Bunch(f, 1) as bunch: + # Thread blocked on sem.acquire() + wait_threads_blocked(1) + self.assertFalse(bunch.finished) + + # Thread unblocked + sem.release() def test_with(self): sem = self.semtype(2) @@ -883,13 +993,13 @@ class BarrierTests(BaseTestCase): def setUp(self): self.barrier = self.barriertype(self.N, timeout=self.defaultTimeout) + def tearDown(self): self.barrier.abort() def run_threads(self, f): - b = Bunch(f, self.N-1) - f() - b.wait_for_finished() + with Bunch(f, self.N): + pass def multipass(self, results, n): m = self.barrier.parties @@ -980,8 +1090,9 @@ def f(): i = self.barrier.wait() if i == self.N//2: # Wait until the other threads are all in the barrier. - while self.barrier.n_waiting < self.N-1: - time.sleep(0.001) + for _ in support.sleeping_retry(support.SHORT_TIMEOUT): + if self.barrier.n_waiting >= (self.N - 1): + break self.barrier.reset() else: try: @@ -1041,27 +1152,27 @@ def f(): i = self.barrier.wait() if i == self.N // 2: # One thread is late! - time.sleep(1.0) + time.sleep(self.defaultTimeout / 2) # Default timeout is 2.0, so this is shorter. self.assertRaises(threading.BrokenBarrierError, - self.barrier.wait, 0.5) + self.barrier.wait, self.defaultTimeout / 4) self.run_threads(f) def test_default_timeout(self): """ Test the barrier's default timeout """ - # gh-109401: Barrier timeout should be long enough - # to create 4 threads on a slow CI. - timeout = 1.0 - barrier = self.barriertype(self.N, timeout=timeout) + timeout = 0.100 + barrier = self.barriertype(2, timeout=timeout) def f(): - i = barrier.wait() - if i == self.N // 2: - # One thread is later than the default timeout. - time.sleep(timeout * 2) - self.assertRaises(threading.BrokenBarrierError, barrier.wait) - self.run_threads(f) + self.assertRaises(threading.BrokenBarrierError, + barrier.wait) + + start_time = time.monotonic() + with Bunch(f, 1): + pass + dt = time.monotonic() - start_time + self.assertGreaterEqual(dt, timeout) def test_single_thread(self): b = self.barriertype(1) @@ -1069,16 +1180,28 @@ def test_single_thread(self): b.wait() def test_repr(self): - b = self.barriertype(3) - self.assertRegex(repr(b), r"<\w+\.Barrier at .*: waiters=0/3>") + barrier = self.barriertype(3) + timeout = support.LONG_TIMEOUT + self.assertRegex(repr(barrier), r"<\w+\.Barrier at .*: waiters=0/3>") def f(): - b.wait(3) - bunch = Bunch(f, 2) - bunch.wait_for_started() - time.sleep(0.2) - self.assertRegex(repr(b), r"<\w+\.Barrier at .*: waiters=2/3>") - b.wait(3) - bunch.wait_for_finished() - self.assertRegex(repr(b), r"<\w+\.Barrier at .*: waiters=0/3>") - b.abort() - self.assertRegex(repr(b), r"<\w+\.Barrier at .*: broken>") + barrier.wait(timeout) + + N = 2 + with Bunch(f, N): + # Threads blocked on barrier.wait() + for _ in support.sleeping_retry(support.SHORT_TIMEOUT): + if barrier.n_waiting >= N: + break + self.assertRegex(repr(barrier), + r"<\w+\.Barrier at .*: waiters=2/3>") + + # Threads unblocked + barrier.wait(timeout) + + self.assertRegex(repr(barrier), + r"<\w+\.Barrier at .*: waiters=0/3>") + + # Abort the barrier + barrier.abort() + self.assertRegex(repr(barrier), + r"<\w+\.Barrier at .*: broken>") diff --git a/Lib/test/test_importlib/test_locks.py b/Lib/test/test_importlib/test_locks.py index 5549d5448bac1f..986f19690cd99f 100644 --- a/Lib/test/test_importlib/test_locks.py +++ b/Lib/test/test_importlib/test_locks.py @@ -88,7 +88,8 @@ def f(): b.release() if ra: a.release() - lock_tests.Bunch(f, NTHREADS).wait_for_finished() + with lock_tests.Bunch(f, NTHREADS): + pass self.assertEqual(len(results), NTHREADS) return results diff --git a/Misc/NEWS.d/next/Tests/2023-09-29-00-19-21.gh-issue-109974.Sh_g-r.rst b/Misc/NEWS.d/next/Tests/2023-09-29-00-19-21.gh-issue-109974.Sh_g-r.rst new file mode 100644 index 00000000000000..a130cf690a57cb --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-09-29-00-19-21.gh-issue-109974.Sh_g-r.rst @@ -0,0 +1,3 @@ +Fix race conditions in test_threading lock tests. Wait until a condition is met +rather than using :func:`time.sleep` with a hardcoded number of seconds. Patch +by Victor Stinner. From b0e43cb6cb99b5ebe62d00727d54c4f05f5a739e Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 4 Oct 2023 08:30:48 -0600 Subject: [PATCH 414/632] [3.11] Lint: Remove files that no longer fail to parse (GH-110356) (#110361) Remove files that no longer fail to parse --- Lib/test/.ruff.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/Lib/test/.ruff.toml b/Lib/test/.ruff.toml index bd46f98512d6e2..7ef54160ec6a81 100644 --- a/Lib/test/.ruff.toml +++ b/Lib/test/.ruff.toml @@ -9,8 +9,6 @@ extend-exclude = [ "encoded_modules/module_iso_8859_1.py", "encoded_modules/module_koi8_r.py", "test_source_encoding.py", - # Failed to parse - "test_fstring.py", # TODO Fix: F811 Redefinition of unused name "test_buffer.py", "test_dataclasses/__init__.py", From 34c7793cd04526827495293a098db527889d7873 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 5 Oct 2023 05:21:34 -0700 Subject: [PATCH 415/632] [3.11] gh-110365: Fix error overwrite in `termios.tcsetattr` (GH-110366) (#110390) (cherry picked from commit 2bbbab212fb10b3aeaded188fb5d6c001fb4bf74) Co-authored-by: Nikita Sobolev Co-authored-by: Erlend E. Aasland --- ...-10-04-18-56-29.gh-issue-110365.LCxiau.rst | 2 + Modules/termios.c | 39 ++++++++++++------- 2 files changed, 28 insertions(+), 13 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-10-04-18-56-29.gh-issue-110365.LCxiau.rst diff --git a/Misc/NEWS.d/next/Library/2023-10-04-18-56-29.gh-issue-110365.LCxiau.rst b/Misc/NEWS.d/next/Library/2023-10-04-18-56-29.gh-issue-110365.LCxiau.rst new file mode 100644 index 00000000000000..a1ac39b60296a3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-10-04-18-56-29.gh-issue-110365.LCxiau.rst @@ -0,0 +1,2 @@ +Fix :func:`termios.tcsetattr` bug that was overwritting existing errors +during parsing integers from ``term`` list. diff --git a/Modules/termios.c b/Modules/termios.c index 3900a6f0b89860..be5d099072c8ee 100644 --- a/Modules/termios.c +++ b/Modules/termios.c @@ -183,17 +183,25 @@ termios_tcsetattr_impl(PyObject *module, int fd, int when, PyObject *term) return PyErr_SetFromErrno(state->TermiosError); } - mode.c_iflag = (tcflag_t) PyLong_AsLong(PyList_GetItem(term, 0)); - mode.c_oflag = (tcflag_t) PyLong_AsLong(PyList_GetItem(term, 1)); - mode.c_cflag = (tcflag_t) PyLong_AsLong(PyList_GetItem(term, 2)); - mode.c_lflag = (tcflag_t) PyLong_AsLong(PyList_GetItem(term, 3)); - speed_t ispeed = (speed_t) PyLong_AsLong(PyList_GetItem(term, 4)); - speed_t ospeed = (speed_t) PyLong_AsLong(PyList_GetItem(term, 5)); - PyObject *cc = PyList_GetItem(term, 6); - if (PyErr_Occurred()) { - return NULL; - } - + speed_t ispeed, ospeed; +#define SET_FROM_LIST(TYPE, VAR, LIST, N) do { \ + PyObject *item = PyList_GET_ITEM(LIST, N); \ + long num = PyLong_AsLong(item); \ + if (num == -1 && PyErr_Occurred()) { \ + return NULL; \ + } \ + VAR = (TYPE)num; \ +} while (0) + + SET_FROM_LIST(tcflag_t, mode.c_iflag, term, 0); + SET_FROM_LIST(tcflag_t, mode.c_oflag, term, 1); + SET_FROM_LIST(tcflag_t, mode.c_cflag, term, 2); + SET_FROM_LIST(tcflag_t, mode.c_lflag, term, 3); + SET_FROM_LIST(speed_t, ispeed, term, 4); + SET_FROM_LIST(speed_t, ospeed, term, 5); +#undef SET_FROM_LIST + + PyObject *cc = PyList_GET_ITEM(term, 6); if (!PyList_Check(cc) || PyList_Size(cc) != NCCS) { PyErr_Format(PyExc_TypeError, "tcsetattr: attributes[6] must be %d element list", @@ -208,8 +216,13 @@ termios_tcsetattr_impl(PyObject *module, int fd, int when, PyObject *term) if (PyBytes_Check(v) && PyBytes_Size(v) == 1) mode.c_cc[i] = (cc_t) * PyBytes_AsString(v); - else if (PyLong_Check(v)) - mode.c_cc[i] = (cc_t) PyLong_AsLong(v); + else if (PyLong_Check(v)) { + long num = PyLong_AsLong(v); + if (num == -1 && PyErr_Occurred()) { + return NULL; + } + mode.c_cc[i] = (cc_t)num; + } else { PyErr_SetString(PyExc_TypeError, "tcsetattr: elements of attributes must be characters or integers"); From cacea7a27260f56f6a29f0d7d37878c569d59cfb Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 5 Oct 2023 09:58:46 -0700 Subject: [PATCH 416/632] [3.11] gh-110391: socket NetworkConnectionAttributesTest always declare cli (GH-110401) (#110406) gh-110391: socket NetworkConnectionAttributesTest always declare cli (GH-110401) NetworkConnectionAttributesTest of test_socket now always declare the 'cli' attribute, so clientTearDown() cannot fail with AttributeError. (cherry picked from commit e37d4557c3de0476e76ca4b8a1cc8d2566b86c79) Co-authored-by: Victor Stinner --- Lib/test/test_socket.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index af0d2a4e6f630a..190efb416ba39a 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -5239,6 +5239,7 @@ def test_create_connection_timeout(self): class NetworkConnectionAttributesTest(SocketTCPTest, ThreadableTest): + cli = None def __init__(self, methodName='runTest'): SocketTCPTest.__init__(self, methodName=methodName) @@ -5248,7 +5249,8 @@ def clientSetUp(self): self.source_port = socket_helper.find_unused_port() def clientTearDown(self): - self.cli.close() + if self.cli is not None: + self.cli.close() self.cli = None ThreadableTest.clientTearDown(self) From 6a6081f820cd850f1a4cac4a1ba28952bdf4a706 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 5 Oct 2023 10:10:39 -0700 Subject: [PATCH 417/632] [3.11] gh-110383 TimeIt Docs Spelling Fix (GH-110407) (#110410) gh-110383 TimeIt Docs Spelling Fix (GH-110407) Make 0.2 second plural (cherry picked from commit a973bf0f97e55ace9eab100f9eb95d7eedcb28ac) Co-authored-by: Towster15 <105541074+Towster15@users.noreply.github.com> --- Doc/library/timeit.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/timeit.rst b/Doc/library/timeit.rst index b59a6bc3742ae4..d0abea38817234 100644 --- a/Doc/library/timeit.rst +++ b/Doc/library/timeit.rst @@ -151,7 +151,7 @@ The module defines three convenience functions and a public class: so that the total time >= 0.2 second, returning the eventual (number of loops, time taken for that number of loops). It calls :meth:`.timeit` with increasing numbers from the sequence 1, 2, 5, - 10, 20, 50, ... until the time taken is at least 0.2 second. + 10, 20, 50, ... until the time taken is at least 0.2 seconds. If *callback* is given and is not ``None``, it will be called after each trial with two arguments: ``callback(number, time_taken)``. From 8394368f1fce672ad25af512063a0f8f5d8a08f9 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Thu, 5 Oct 2023 18:30:26 +0100 Subject: [PATCH 418/632] [3.11] Docs: Avoid the deprecated ``.. cmdoption::`` directive (GH-110292) (#110303) [3.11] Docs: Avoid the deprecated ``.. cmdoption::`` directive (GH-110292). (cherry picked from commit 77e9aae3837d9f0cf87461d023896f2c4aeb282f) --- Doc/library/ast.rst | 14 ++--- Doc/library/compileall.rst | 34 +++++----- Doc/library/gzip.rst | 10 +-- Doc/library/inspect.rst | 2 +- Doc/library/json.rst | 14 ++--- Doc/library/pickletools.rst | 10 +-- Doc/library/py_compile.rst | 6 +- Doc/library/site.rst | 4 +- Doc/library/tarfile.rst | 20 +++--- Doc/library/timeit.rst | 14 ++--- Doc/library/tokenize.rst | 4 +- Doc/library/trace.rst | 30 ++++----- Doc/library/unittest.rst | 18 +++--- Doc/library/zipapp.rst | 12 ++-- Doc/library/zipfile.rst | 18 +++--- Doc/using/cmdline.rst | 60 +++++++++--------- Doc/using/configure.rst | 120 ++++++++++++++++++------------------ 17 files changed, 195 insertions(+), 195 deletions(-) diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst index 6392f7318d7a6d..e95e18c1778069 100644 --- a/Doc/library/ast.rst +++ b/Doc/library/ast.rst @@ -2337,26 +2337,26 @@ The following options are accepted: .. program:: ast -.. cmdoption:: -h, --help +.. option:: -h, --help Show the help message and exit. -.. cmdoption:: -m - --mode +.. option:: -m + --mode Specify what kind of code must be compiled, like the *mode* argument in :func:`parse`. -.. cmdoption:: --no-type-comments +.. option:: --no-type-comments Don't parse type comments. -.. cmdoption:: -a, --include-attributes +.. option:: -a, --include-attributes Include attributes such as line numbers and column offsets. -.. cmdoption:: -i - --indent +.. option:: -i + --indent Indentation of nodes in AST (number of spaces). diff --git a/Doc/library/compileall.rst b/Doc/library/compileall.rst index 75b97d6ff4588e..a1482c9eb889e8 100644 --- a/Doc/library/compileall.rst +++ b/Doc/library/compileall.rst @@ -24,28 +24,28 @@ compile Python sources. .. program:: compileall -.. cmdoption:: directory ... - file ... +.. option:: directory ... + file ... Positional arguments are files to compile or directories that contain source files, traversed recursively. If no argument is given, behave as if the command line was :samp:`-l {}`. -.. cmdoption:: -l +.. option:: -l Do not recurse into subdirectories, only compile source code files directly contained in the named or implied directories. -.. cmdoption:: -f +.. option:: -f Force rebuild even if timestamps are up-to-date. -.. cmdoption:: -q +.. option:: -q Do not print the list of files compiled. If passed once, error messages will still be printed. If passed twice (``-qq``), all output is suppressed. -.. cmdoption:: -d destdir +.. option:: -d destdir Directory prepended to the path to each file being compiled. This will appear in compilation time tracebacks, and is also compiled in to the @@ -53,45 +53,45 @@ compile Python sources. cases where the source file does not exist at the time the byte-code file is executed. -.. cmdoption:: -s strip_prefix -.. cmdoption:: -p prepend_prefix +.. option:: -s strip_prefix +.. option:: -p prepend_prefix Remove (``-s``) or append (``-p``) the given prefix of paths recorded in the ``.pyc`` files. Cannot be combined with ``-d``. -.. cmdoption:: -x regex +.. option:: -x regex regex is used to search the full path to each file considered for compilation, and if the regex produces a match, the file is skipped. -.. cmdoption:: -i list +.. option:: -i list Read the file ``list`` and add each line that it contains to the list of files and directories to compile. If ``list`` is ``-``, read lines from ``stdin``. -.. cmdoption:: -b +.. option:: -b Write the byte-code files to their legacy locations and names, which may overwrite byte-code files created by another version of Python. The default is to write files to their :pep:`3147` locations and names, which allows byte-code files from multiple versions of Python to coexist. -.. cmdoption:: -r +.. option:: -r Control the maximum recursion level for subdirectories. If this is given, then ``-l`` option will not be taken into account. :program:`python -m compileall -r 0` is equivalent to :program:`python -m compileall -l`. -.. cmdoption:: -j N +.. option:: -j N Use *N* workers to compile the files within the given directory. If ``0`` is used, then the result of :func:`os.cpu_count()` will be used. -.. cmdoption:: --invalidation-mode [timestamp|checked-hash|unchecked-hash] +.. option:: --invalidation-mode [timestamp|checked-hash|unchecked-hash] Control how the generated byte-code files are invalidated at runtime. The ``timestamp`` value, means that ``.pyc`` files with the source timestamp @@ -104,17 +104,17 @@ compile Python sources. variable is not set, and ``checked-hash`` if the ``SOURCE_DATE_EPOCH`` environment variable is set. -.. cmdoption:: -o level +.. option:: -o level Compile with the given optimization level. May be used multiple times to compile for multiple levels at a time (for example, ``compileall -o 1 -o 2``). -.. cmdoption:: -e dir +.. option:: -e dir Ignore symlinks pointing outside the given directory. -.. cmdoption:: --hardlink-dupes +.. option:: --hardlink-dupes If two ``.pyc`` files with different optimization level have the same content, use hard links to consolidate duplicate files. diff --git a/Doc/library/gzip.rst b/Doc/library/gzip.rst index c28cfdc50e3989..4d2fa3d4012a1d 100644 --- a/Doc/library/gzip.rst +++ b/Doc/library/gzip.rst @@ -262,23 +262,23 @@ Once executed the :mod:`gzip` module keeps the input file(s). Command line options ^^^^^^^^^^^^^^^^^^^^ -.. cmdoption:: file +.. option:: file If *file* is not specified, read from :data:`sys.stdin`. -.. cmdoption:: --fast +.. option:: --fast Indicates the fastest compression method (less compression). -.. cmdoption:: --best +.. option:: --best Indicates the slowest compression method (best compression). -.. cmdoption:: -d, --decompress +.. option:: -d, --decompress Decompress the given file. -.. cmdoption:: -h, --help +.. option:: -h, --help Show the help message. diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index 11f2d701199771..8b2bfb322529b1 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -1570,6 +1570,6 @@ By default, accepts the name of a module and prints the source of that module. A class or function within the module can be printed instead by appended a colon and the qualified name of the target object. -.. cmdoption:: --details +.. option:: --details Print information about the specified object rather than the source code diff --git a/Doc/library/json.rst b/Doc/library/json.rst index 6c3059381776c9..e234fe92bc9995 100644 --- a/Doc/library/json.rst +++ b/Doc/library/json.rst @@ -703,7 +703,7 @@ specified, :data:`sys.stdin` and :data:`sys.stdout` will be used respectively: Command line options ^^^^^^^^^^^^^^^^^^^^ -.. cmdoption:: infile +.. option:: infile The JSON file to be validated or pretty-printed: @@ -723,36 +723,36 @@ Command line options If *infile* is not specified, read from :data:`sys.stdin`. -.. cmdoption:: outfile +.. option:: outfile Write the output of the *infile* to the given *outfile*. Otherwise, write it to :data:`sys.stdout`. -.. cmdoption:: --sort-keys +.. option:: --sort-keys Sort the output of dictionaries alphabetically by key. .. versionadded:: 3.5 -.. cmdoption:: --no-ensure-ascii +.. option:: --no-ensure-ascii Disable escaping of non-ascii characters, see :func:`json.dumps` for more information. .. versionadded:: 3.9 -.. cmdoption:: --json-lines +.. option:: --json-lines Parse every input line as separate JSON object. .. versionadded:: 3.8 -.. cmdoption:: --indent, --tab, --no-indent, --compact +.. option:: --indent, --tab, --no-indent, --compact Mutually exclusive options for whitespace control. .. versionadded:: 3.9 -.. cmdoption:: -h, --help +.. option:: -h, --help Show the help message. diff --git a/Doc/library/pickletools.rst b/Doc/library/pickletools.rst index 480f4a6d320815..c6ff4e6ed3d3b5 100644 --- a/Doc/library/pickletools.rst +++ b/Doc/library/pickletools.rst @@ -51,24 +51,24 @@ Command line options .. program:: pickletools -.. cmdoption:: -a, --annotate +.. option:: -a, --annotate Annotate each line with a short opcode description. -.. cmdoption:: -o, --output= +.. option:: -o, --output= Name of a file where the output should be written. -.. cmdoption:: -l, --indentlevel= +.. option:: -l, --indentlevel= The number of blanks by which to indent a new MARK level. -.. cmdoption:: -m, --memo +.. option:: -m, --memo When multiple objects are disassembled, preserve memo between disassemblies. -.. cmdoption:: -p, --preamble= +.. option:: -p, --preamble= When more than one pickle file are specified, print given preamble before each disassembly. diff --git a/Doc/library/py_compile.rst b/Doc/library/py_compile.rst index 69b93a3bdfcb26..7272f36d9a5568 100644 --- a/Doc/library/py_compile.rst +++ b/Doc/library/py_compile.rst @@ -138,13 +138,13 @@ not be compiled. .. program:: python -m py_compile -.. cmdoption:: ... - - +.. option:: ... + - Positional arguments are files to compile. If ``-`` is the only parameter, the list of files is taken from standard input. -.. cmdoption:: -q, --quiet +.. option:: -q, --quiet Suppress errors output. diff --git a/Doc/library/site.rst b/Doc/library/site.rst index c025af608e318b..ab4fa765bf63d5 100644 --- a/Doc/library/site.rst +++ b/Doc/library/site.rst @@ -266,11 +266,11 @@ If it is called without arguments, it will print the contents of :data:`USER_BASE` and whether the directory exists, then the same thing for :data:`USER_SITE`, and finally the value of :data:`ENABLE_USER_SITE`. -.. cmdoption:: --user-base +.. option:: --user-base Print the path to the user base directory. -.. cmdoption:: --user-site +.. option:: --user-site Print the path to the user site-packages directory. diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst index 61a450cf88b0ac..76c8602b7537b7 100644 --- a/Doc/library/tarfile.rst +++ b/Doc/library/tarfile.rst @@ -1149,31 +1149,31 @@ For a list of the files in a tar archive, use the :option:`-l` option: Command-line options ~~~~~~~~~~~~~~~~~~~~ -.. cmdoption:: -l - --list +.. option:: -l + --list List files in a tarfile. -.. cmdoption:: -c ... - --create ... +.. option:: -c ... + --create ... Create tarfile from source files. -.. cmdoption:: -e [] - --extract [] +.. option:: -e [] + --extract [] Extract tarfile into the current directory if *output_dir* is not specified. -.. cmdoption:: -t - --test +.. option:: -t + --test Test whether the tarfile is valid or not. -.. cmdoption:: -v, --verbose +.. option:: -v, --verbose Verbose output. -.. cmdoption:: --filter +.. option:: --filter Specifies the *filter* for ``--extract``. See :ref:`tarfile-extraction-filter` for details. diff --git a/Doc/library/timeit.rst b/Doc/library/timeit.rst index d0abea38817234..c9afb7d802e84f 100644 --- a/Doc/library/timeit.rst +++ b/Doc/library/timeit.rst @@ -214,36 +214,36 @@ Where the following options are understood: .. program:: timeit -.. cmdoption:: -n N, --number=N +.. option:: -n N, --number=N how many times to execute 'statement' -.. cmdoption:: -r N, --repeat=N +.. option:: -r N, --repeat=N how many times to repeat the timer (default 5) -.. cmdoption:: -s S, --setup=S +.. option:: -s S, --setup=S statement to be executed once initially (default ``pass``) -.. cmdoption:: -p, --process +.. option:: -p, --process measure process time, not wallclock time, using :func:`time.process_time` instead of :func:`time.perf_counter`, which is the default .. versionadded:: 3.3 -.. cmdoption:: -u, --unit=U +.. option:: -u, --unit=U specify a time unit for timer output; can select ``nsec``, ``usec``, ``msec``, or ``sec`` .. versionadded:: 3.5 -.. cmdoption:: -v, --verbose +.. option:: -v, --verbose print raw timing results; repeat for more digits precision -.. cmdoption:: -h, --help +.. option:: -h, --help print a short usage message and exit diff --git a/Doc/library/tokenize.rst b/Doc/library/tokenize.rst index a903bff1da8f5e..6f6c4d5371bb7f 100644 --- a/Doc/library/tokenize.rst +++ b/Doc/library/tokenize.rst @@ -171,11 +171,11 @@ The following options are accepted: .. program:: tokenize -.. cmdoption:: -h, --help +.. option:: -h, --help show this help message and exit -.. cmdoption:: -e, --exact +.. option:: -e, --exact display token names using the exact type diff --git a/Doc/library/trace.rst b/Doc/library/trace.rst index 40cf198f1287d7..e9b59a6d186ba2 100644 --- a/Doc/library/trace.rst +++ b/Doc/library/trace.rst @@ -34,11 +34,11 @@ all Python modules imported during the execution into the current directory. .. program:: trace -.. cmdoption:: --help +.. option:: --help Display usage and exit. -.. cmdoption:: --version +.. option:: --version Display the version of the module and exit. @@ -56,28 +56,28 @@ the :option:`--trace <-t>` and :option:`--count <-c>` options. When .. program:: trace -.. cmdoption:: -c, --count +.. option:: -c, --count Produce a set of annotated listing files upon program completion that shows how many times each statement was executed. See also :option:`--coverdir <-C>`, :option:`--file <-f>` and :option:`--no-report <-R>` below. -.. cmdoption:: -t, --trace +.. option:: -t, --trace Display lines as they are executed. -.. cmdoption:: -l, --listfuncs +.. option:: -l, --listfuncs Display the functions executed by running the program. -.. cmdoption:: -r, --report +.. option:: -r, --report Produce an annotated list from an earlier program run that used the :option:`--count <-c>` and :option:`--file <-f>` option. This does not execute any code. -.. cmdoption:: -T, --trackcalls +.. option:: -T, --trackcalls Display the calling relationships exposed by running the program. @@ -86,33 +86,33 @@ Modifiers .. program:: trace -.. cmdoption:: -f, --file= +.. option:: -f, --file= Name of a file to accumulate counts over several tracing runs. Should be used with the :option:`--count <-c>` option. -.. cmdoption:: -C, --coverdir= +.. option:: -C, --coverdir= Directory where the report files go. The coverage report for ``package.module`` is written to file :file:`{dir}/{package}/{module}.cover`. -.. cmdoption:: -m, --missing +.. option:: -m, --missing When generating annotated listings, mark lines which were not executed with ``>>>>>>``. -.. cmdoption:: -s, --summary +.. option:: -s, --summary When using :option:`--count <-c>` or :option:`--report <-r>`, write a brief summary to stdout for each file processed. -.. cmdoption:: -R, --no-report +.. option:: -R, --no-report Do not generate annotated listings. This is useful if you intend to make several runs with :option:`--count <-c>`, and then produce a single set of annotated listings at the end. -.. cmdoption:: -g, --timing +.. option:: -g, --timing Prefix each line with the time since the program started. Only used while tracing. @@ -124,12 +124,12 @@ These options may be repeated multiple times. .. program:: trace -.. cmdoption:: --ignore-module= +.. option:: --ignore-module= Ignore each of the given module names and its submodules (if it is a package). The argument can be a list of names separated by a comma. -.. cmdoption:: --ignore-dir= +.. option:: --ignore-dir= Ignore all modules and packages in the named directory and subdirectories. The argument can be a list of directories separated by :data:`os.pathsep`. diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index 2c6316d66eadfc..c5826698bd35f5 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -206,13 +206,13 @@ Command-line options .. program:: unittest -.. cmdoption:: -b, --buffer +.. option:: -b, --buffer The standard output and standard error streams are buffered during the test run. Output during a passing test is discarded. Output is echoed normally on test fail or error and is added to the failure messages. -.. cmdoption:: -c, --catch +.. option:: -c, --catch :kbd:`Control-C` during the test run waits for the current test to end and then reports all the results so far. A second :kbd:`Control-C` raises the normal @@ -220,11 +220,11 @@ Command-line options See `Signal Handling`_ for the functions that provide this functionality. -.. cmdoption:: -f, --failfast +.. option:: -f, --failfast Stop the test run on the first error or failure. -.. cmdoption:: -k +.. option:: -k Only run test methods and classes that match the pattern or substring. This option may be used multiple times, in which case all test cases that @@ -240,7 +240,7 @@ Command-line options For example, ``-k foo`` matches ``foo_tests.SomeTest.test_something``, ``bar_tests.SomeTest.test_foo``, but not ``bar_tests.FooTest.test_something``. -.. cmdoption:: --locals +.. option:: --locals Show local variables in tracebacks. @@ -286,19 +286,19 @@ The ``discover`` sub-command has the following options: .. program:: unittest discover -.. cmdoption:: -v, --verbose +.. option:: -v, --verbose Verbose output -.. cmdoption:: -s, --start-directory directory +.. option:: -s, --start-directory directory Directory to start discovery (``.`` default) -.. cmdoption:: -p, --pattern pattern +.. option:: -p, --pattern pattern Pattern to match test files (``test*.py`` default) -.. cmdoption:: -t, --top-level-directory directory +.. option:: -t, --top-level-directory directory Top level directory of project (defaults to start directory) diff --git a/Doc/library/zipapp.rst b/Doc/library/zipapp.rst index 81d61d8fe853d3..c98d8a6036bf83 100644 --- a/Doc/library/zipapp.rst +++ b/Doc/library/zipapp.rst @@ -54,7 +54,7 @@ The following options are understood: .. program:: zipapp -.. cmdoption:: -o , --output= +.. option:: -o , --output= Write the output to a file named *output*. If this option is not specified, the output filename will be the same as the input *source*, with the @@ -64,13 +64,13 @@ The following options are understood: An output filename must be specified if the *source* is an archive (and in that case, *output* must not be the same as *source*). -.. cmdoption:: -p , --python= +.. option:: -p , --python= Add a ``#!`` line to the archive specifying *interpreter* as the command to run. Also, on POSIX, make the archive executable. The default is to write no ``#!`` line, and not make the file executable. -.. cmdoption:: -m , --main= +.. option:: -m , --main= Write a ``__main__.py`` file to the archive that executes *mainfn*. The *mainfn* argument should have the form "pkg.mod:fn", where "pkg.mod" is a @@ -79,7 +79,7 @@ The following options are understood: :option:`--main` cannot be specified when copying an archive. -.. cmdoption:: -c, --compress +.. option:: -c, --compress Compress files with the deflate method, reducing the size of the output file. By default, files are stored uncompressed in the archive. @@ -88,13 +88,13 @@ The following options are understood: .. versionadded:: 3.7 -.. cmdoption:: --info +.. option:: --info Display the interpreter embedded in the archive, for diagnostic purposes. In this case, any other options are ignored and SOURCE must be an archive, not a directory. -.. cmdoption:: -h, --help +.. option:: -h, --help Print a short usage message and exit. diff --git a/Doc/library/zipfile.rst b/Doc/library/zipfile.rst index f4867575447c0e..ef84115a4b3c13 100644 --- a/Doc/library/zipfile.rst +++ b/Doc/library/zipfile.rst @@ -904,27 +904,27 @@ For a list of the files in a ZIP archive, use the :option:`-l` option: Command-line options ~~~~~~~~~~~~~~~~~~~~ -.. cmdoption:: -l - --list +.. option:: -l + --list List files in a zipfile. -.. cmdoption:: -c ... - --create ... +.. option:: -c ... + --create ... Create zipfile from source files. -.. cmdoption:: -e - --extract +.. option:: -e + --extract Extract zipfile into target directory. -.. cmdoption:: -t - --test +.. option:: -t + --test Test whether the zipfile is valid or not. -.. cmdoption:: --metadata-encoding +.. option:: --metadata-encoding Specify encoding of member names for :option:`-l`, :option:`-e` and :option:`-t`. diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst index c42ef78c6b09d3..79aefdaf389de5 100644 --- a/Doc/using/cmdline.rst +++ b/Doc/using/cmdline.rst @@ -59,7 +59,7 @@ all consecutive arguments will end up in :data:`sys.argv` -- note that the first element, subscript zero (``sys.argv[0]``), is a string reflecting the program's source. -.. cmdoption:: -c +.. option:: -c Execute the Python code in *command*. *command* can be one or more statements separated by newlines, with significant leading whitespace as in @@ -72,7 +72,7 @@ source. .. audit-event:: cpython.run_command command cmdoption-c -.. cmdoption:: -m +.. option:: -m Search :data:`sys.path` for the named module and execute its contents as the :mod:`__main__` module. @@ -188,35 +188,35 @@ automatically enabled, if available on your platform (see Generic options ~~~~~~~~~~~~~~~ -.. cmdoption:: -? - -h - --help +.. option:: -? + -h + --help Print a short description of all command line options and corresponding environment variables and exit. -.. cmdoption:: --help-env +.. option:: --help-env Print a short description of Python-specific environment variables and exit. .. versionadded:: 3.11 -.. cmdoption:: --help-xoptions +.. option:: --help-xoptions Print a description of implementation-specific :option:`-X` options and exit. .. versionadded:: 3.11 -.. cmdoption:: --help-all +.. option:: --help-all Print complete usage information and exit. .. versionadded:: 3.11 -.. cmdoption:: -V - --version +.. option:: -V + --version Print the Python version number and exit. Example output could be: @@ -240,7 +240,7 @@ Generic options Miscellaneous options ~~~~~~~~~~~~~~~~~~~~~ -.. cmdoption:: -b +.. option:: -b Issue a warning when comparing :class:`bytes` or :class:`bytearray` with :class:`str` or :class:`bytes` with :class:`int`. Issue an error when the @@ -249,13 +249,13 @@ Miscellaneous options .. versionchanged:: 3.5 Affects comparisons of :class:`bytes` with :class:`int`. -.. cmdoption:: -B +.. option:: -B If given, Python won't try to write ``.pyc`` files on the import of source modules. See also :envvar:`PYTHONDONTWRITEBYTECODE`. -.. cmdoption:: --check-hash-based-pycs default|always|never +.. option:: --check-hash-based-pycs default|always|never Control the validation behavior of hash-based ``.pyc`` files. See :ref:`pyc-invalidation`. When set to ``default``, checked and unchecked @@ -269,13 +269,13 @@ Miscellaneous options option. -.. cmdoption:: -d +.. option:: -d Turn on parser debugging output (for expert only, depending on compilation options). See also :envvar:`PYTHONDEBUG`. -.. cmdoption:: -E +.. option:: -E Ignore all :envvar:`PYTHON*` environment variables, e.g. :envvar:`PYTHONPATH` and :envvar:`PYTHONHOME`, that might be set. @@ -283,7 +283,7 @@ Miscellaneous options See also the :option:`-P` and :option:`-I` (isolated) options. -.. cmdoption:: -i +.. option:: -i When a script is passed as first argument or the :option:`-c` option is used, enter interactive mode after executing the script or the command, even when @@ -294,7 +294,7 @@ Miscellaneous options raises an exception. See also :envvar:`PYTHONINSPECT`. -.. cmdoption:: -I +.. option:: -I Run Python in isolated mode. This also implies :option:`-E`, :option:`-P` and :option:`-s` options. @@ -307,7 +307,7 @@ Miscellaneous options .. versionadded:: 3.4 -.. cmdoption:: -O +.. option:: -O Remove assert statements and any code conditional on the value of :const:`__debug__`. Augment the filename for compiled @@ -318,7 +318,7 @@ Miscellaneous options Modify ``.pyc`` filenames according to :pep:`488`. -.. cmdoption:: -OO +.. option:: -OO Do :option:`-O` and also discard docstrings. Augment the filename for compiled (:term:`bytecode`) files by adding ``.opt-2`` before the @@ -328,7 +328,7 @@ Miscellaneous options Modify ``.pyc`` filenames according to :pep:`488`. -.. cmdoption:: -P +.. option:: -P Don't prepend a potentially unsafe path to :data:`sys.path`: @@ -345,14 +345,14 @@ Miscellaneous options .. versionadded:: 3.11 -.. cmdoption:: -q +.. option:: -q Don't display the copyright and version messages even in interactive mode. .. versionadded:: 3.2 -.. cmdoption:: -R +.. option:: -R Turn on hash randomization. This option only has an effect if the :envvar:`PYTHONHASHSEED` environment variable is set to ``0``, since hash @@ -378,7 +378,7 @@ Miscellaneous options .. versionadded:: 3.2.3 -.. cmdoption:: -s +.. option:: -s Don't add the :data:`user site-packages directory ` to :data:`sys.path`. @@ -388,7 +388,7 @@ Miscellaneous options :pep:`370` -- Per user site-packages directory -.. cmdoption:: -S +.. option:: -S Disable the import of the module :mod:`site` and the site-dependent manipulations of :data:`sys.path` that it entails. Also disable these @@ -396,7 +396,7 @@ Miscellaneous options :func:`site.main` if you want them to be triggered). -.. cmdoption:: -u +.. option:: -u Force the stdout and stderr streams to be unbuffered. This option has no effect on the stdin stream. @@ -407,7 +407,7 @@ Miscellaneous options The text layer of the stdout and stderr streams now is unbuffered. -.. cmdoption:: -v +.. option:: -v Print a message each time a module is initialized, showing the place (filename or built-in module) from which it is loaded. When given twice @@ -422,7 +422,7 @@ Miscellaneous options .. _using-on-warnings: -.. cmdoption:: -W arg +.. option:: -W arg Warning control. Python's warning machinery by default prints warning messages to :data:`sys.stderr`. @@ -481,13 +481,13 @@ Miscellaneous options details. -.. cmdoption:: -x +.. option:: -x Skip the first line of the source, allowing use of non-Unix forms of ``#!cmd``. This is intended for a DOS specific hack only. -.. cmdoption:: -X +.. option:: -X Reserved for various implementation-specific options. CPython currently defines the following possible values: @@ -586,7 +586,7 @@ Miscellaneous options Options you shouldn't use ~~~~~~~~~~~~~~~~~~~~~~~~~ -.. cmdoption:: -J +.. option:: -J Reserved for use by Jython_. diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst index 04cd3769f22632..2d9a374ba26708 100644 --- a/Doc/using/configure.rst +++ b/Doc/using/configure.rst @@ -16,7 +16,7 @@ See also the :file:`Misc/SpecialBuilds.txt` in the Python source distribution. General Options --------------- -.. cmdoption:: --enable-loadable-sqlite-extensions +.. option:: --enable-loadable-sqlite-extensions Support loadable extensions in the :mod:`!_sqlite` extension module (default is no) of the :mod:`sqlite3` module. @@ -26,12 +26,12 @@ General Options .. versionadded:: 3.6 -.. cmdoption:: --disable-ipv6 +.. option:: --disable-ipv6 Disable IPv6 support (enabled by default if supported), see the :mod:`socket` module. -.. cmdoption:: --enable-big-digits=[15|30] +.. option:: --enable-big-digits=[15|30] Define the size in bits of Python :class:`int` digits: 15 or 30 bits. @@ -41,13 +41,13 @@ General Options See :data:`sys.int_info.bits_per_digit `. -.. cmdoption:: --with-cxx-main -.. cmdoption:: --with-cxx-main=COMPILER +.. option:: --with-cxx-main +.. option:: --with-cxx-main=COMPILER Compile the Python ``main()`` function and link Python executable with C++ compiler: ``$CXX``, or *COMPILER* if specified. -.. cmdoption:: --with-suffix=SUFFIX +.. option:: --with-suffix=SUFFIX Set the Python executable suffix to *SUFFIX*. @@ -60,7 +60,7 @@ General Options The default suffix on WASM platform is one of ``.js``, ``.html`` or ``.wasm``. -.. cmdoption:: --with-tzpath= +.. option:: --with-tzpath= Select the default time zone search path for :const:`zoneinfo.TZPATH`. See the :ref:`Compile-time configuration @@ -72,7 +72,7 @@ General Options .. versionadded:: 3.9 -.. cmdoption:: --without-decimal-contextvar +.. option:: --without-decimal-contextvar Build the ``_decimal`` extension module using a thread-local context rather than a coroutine-local context (default), see the :mod:`decimal` module. @@ -81,7 +81,7 @@ General Options .. versionadded:: 3.9 -.. cmdoption:: --with-dbmliborder= +.. option:: --with-dbmliborder= Override order to check db backends for the :mod:`dbm` module @@ -91,7 +91,7 @@ General Options * ``gdbm``; * ``bdb``. -.. cmdoption:: --without-c-locale-coercion +.. option:: --without-c-locale-coercion Disable C locale coercion to a UTF-8 based locale (enabled by default). @@ -99,7 +99,7 @@ General Options See :envvar:`PYTHONCOERCECLOCALE` and the :pep:`538`. -.. cmdoption:: --with-platlibdir=DIRNAME +.. option:: --with-platlibdir=DIRNAME Python library directory name (default is ``lib``). @@ -109,7 +109,7 @@ General Options .. versionadded:: 3.9 -.. cmdoption:: --with-wheel-pkg-dir=PATH +.. option:: --with-wheel-pkg-dir=PATH Directory of wheel packages used by the :mod:`ensurepip` module (none by default). @@ -121,7 +121,7 @@ General Options .. versionadded:: 3.10 -.. cmdoption:: --with-pkg-config=[check|yes|no] +.. option:: --with-pkg-config=[check|yes|no] Whether configure should use :program:`pkg-config` to detect build dependencies. @@ -132,7 +132,7 @@ General Options .. versionadded:: 3.11 -.. cmdoption:: --enable-pystats +.. option:: --enable-pystats Turn on internal statistics gathering. @@ -146,7 +146,7 @@ General Options WebAssembly Options ------------------- -.. cmdoption:: --with-emscripten-target=[browser|node] +.. option:: --with-emscripten-target=[browser|node] Set build flavor for ``wasm32-emscripten``. @@ -155,7 +155,7 @@ WebAssembly Options .. versionadded:: 3.11 -.. cmdoption:: --enable-wasm-dynamic-linking +.. option:: --enable-wasm-dynamic-linking Turn on dynamic linking support for WASM. @@ -164,7 +164,7 @@ WebAssembly Options .. versionadded:: 3.11 -.. cmdoption:: --enable-wasm-pthreads +.. option:: --enable-wasm-pthreads Turn on pthreads support for WASM. @@ -174,7 +174,7 @@ WebAssembly Options Install Options --------------- -.. cmdoption:: --prefix=PREFIX +.. option:: --prefix=PREFIX Install architecture-independent files in PREFIX. On Unix, it defaults to :file:`/usr/local`. @@ -184,20 +184,20 @@ Install Options As an example, one can use ``--prefix="$HOME/.local/"`` to install a Python in its home directory. -.. cmdoption:: --exec-prefix=EPREFIX +.. option:: --exec-prefix=EPREFIX Install architecture-dependent files in EPREFIX, defaults to :option:`--prefix`. This value can be retrieved at runtime using :data:`sys.exec_prefix`. -.. cmdoption:: --disable-test-modules +.. option:: --disable-test-modules Don't build nor install test modules, like the :mod:`test` package or the :mod:`!_testcapi` extension module (built and installed by default). .. versionadded:: 3.10 -.. cmdoption:: --with-ensurepip=[upgrade|install|no] +.. option:: --with-ensurepip=[upgrade|install|no] Select the :mod:`ensurepip` command run on Python installation: @@ -215,7 +215,7 @@ Performance options Configuring Python using ``--enable-optimizations --with-lto`` (PGO + LTO) is recommended for best performance. -.. cmdoption:: --enable-optimizations +.. option:: --enable-optimizations Enable Profile Guided Optimization (PGO) using :envvar:`PROFILE_TASK` (disabled by default). @@ -241,7 +241,7 @@ recommended for best performance. .. versionadded:: 3.8 -.. cmdoption:: --with-lto=[full|thin|no|yes] +.. option:: --with-lto=[full|thin|no|yes] Enable Link Time Optimization (LTO) in any build (disabled by default). @@ -253,19 +253,19 @@ recommended for best performance. .. versionadded:: 3.11 To use ThinLTO feature, use ``--with-lto=thin`` on Clang. -.. cmdoption:: --with-computed-gotos +.. option:: --with-computed-gotos Enable computed gotos in evaluation loop (enabled by default on supported compilers). -.. cmdoption:: --without-pymalloc +.. option:: --without-pymalloc Disable the specialized Python memory allocator :ref:`pymalloc ` (enabled by default). See also :envvar:`PYTHONMALLOC` environment variable. -.. cmdoption:: --without-doc-strings +.. option:: --without-doc-strings Disable static documentation strings to reduce the memory footprint (enabled by default). Documentation strings defined in Python are not affected. @@ -274,7 +274,7 @@ recommended for best performance. See the ``PyDoc_STRVAR()`` macro. -.. cmdoption:: --enable-profiling +.. option:: --enable-profiling Enable C-level code profiling with ``gprof`` (disabled by default). @@ -329,12 +329,12 @@ See also the :ref:`Python Development Mode ` and the Debug options ------------- -.. cmdoption:: --with-pydebug +.. option:: --with-pydebug :ref:`Build Python in debug mode `: define the ``Py_DEBUG`` macro (disabled by default). -.. cmdoption:: --with-trace-refs +.. option:: --with-trace-refs Enable tracing references for debugging purpose (disabled by default). @@ -349,7 +349,7 @@ Debug options .. versionadded:: 3.8 -.. cmdoption:: --with-assertions +.. option:: --with-assertions Build with C assertions enabled (default is no): ``assert(...);`` and ``_PyObject_ASSERT(...);``. @@ -362,11 +362,11 @@ Debug options .. versionadded:: 3.6 -.. cmdoption:: --with-valgrind +.. option:: --with-valgrind Enable Valgrind support (default is no). -.. cmdoption:: --with-dtrace +.. option:: --with-dtrace Enable DTrace support (default is no). @@ -375,19 +375,19 @@ Debug options .. versionadded:: 3.6 -.. cmdoption:: --with-address-sanitizer +.. option:: --with-address-sanitizer Enable AddressSanitizer memory error detector, ``asan`` (default is no). .. versionadded:: 3.6 -.. cmdoption:: --with-memory-sanitizer +.. option:: --with-memory-sanitizer Enable MemorySanitizer allocation error detector, ``msan`` (default is no). .. versionadded:: 3.6 -.. cmdoption:: --with-undefined-behavior-sanitizer +.. option:: --with-undefined-behavior-sanitizer Enable UndefinedBehaviorSanitizer undefined behaviour detector, ``ubsan`` (default is no). @@ -398,11 +398,11 @@ Debug options Linker options -------------- -.. cmdoption:: --enable-shared +.. option:: --enable-shared Enable building a shared Python library: ``libpython`` (default is no). -.. cmdoption:: --without-static-libpython +.. option:: --without-static-libpython Do not build ``libpythonMAJOR.MINOR.a`` and do not install ``python.o`` (built and enabled by default). @@ -413,28 +413,28 @@ Linker options Libraries options ----------------- -.. cmdoption:: --with-libs='lib1 ...' +.. option:: --with-libs='lib1 ...' Link against additional libraries (default is no). -.. cmdoption:: --with-system-expat +.. option:: --with-system-expat Build the :mod:`!pyexpat` module using an installed ``expat`` library (default is no). -.. cmdoption:: --with-system-ffi +.. option:: --with-system-ffi Build the :mod:`_ctypes` extension module using an installed ``ffi`` library, see the :mod:`ctypes` module (default is system-dependent). -.. cmdoption:: --with-system-libmpdec +.. option:: --with-system-libmpdec Build the ``_decimal`` extension module using an installed ``mpdec`` library, see the :mod:`decimal` module (default is no). .. versionadded:: 3.3 -.. cmdoption:: --with-readline=editline +.. option:: --with-readline=editline Use ``editline`` library for backend of the :mod:`readline` module. @@ -442,7 +442,7 @@ Libraries options .. versionadded:: 3.10 -.. cmdoption:: --without-readline +.. option:: --without-readline Don't build the :mod:`readline` module (built by default). @@ -450,21 +450,21 @@ Libraries options .. versionadded:: 3.10 -.. cmdoption:: --with-libm=STRING +.. option:: --with-libm=STRING Override ``libm`` math library to *STRING* (default is system-dependent). -.. cmdoption:: --with-libc=STRING +.. option:: --with-libc=STRING Override ``libc`` C library to *STRING* (default is system-dependent). -.. cmdoption:: --with-openssl=DIR +.. option:: --with-openssl=DIR Root of the OpenSSL directory. .. versionadded:: 3.7 -.. cmdoption:: --with-openssl-rpath=[no|auto|DIR] +.. option:: --with-openssl-rpath=[no|auto|DIR] Set runtime library directory (rpath) for OpenSSL libraries: @@ -479,7 +479,7 @@ Libraries options Security Options ---------------- -.. cmdoption:: --with-hash-algorithm=[fnv|siphash13|siphash24] +.. option:: --with-hash-algorithm=[fnv|siphash13|siphash24] Select hash algorithm for use in ``Python/pyhash.c``: @@ -492,7 +492,7 @@ Security Options .. versionadded:: 3.11 ``siphash13`` is added and it is the new default. -.. cmdoption:: --with-builtin-hashlib-hashes=md5,sha1,sha256,sha512,sha3,blake2 +.. option:: --with-builtin-hashlib-hashes=md5,sha1,sha256,sha512,sha3,blake2 Built-in hash modules: @@ -505,7 +505,7 @@ Security Options .. versionadded:: 3.9 -.. cmdoption:: --with-ssl-default-suites=[python|openssl|STRING] +.. option:: --with-ssl-default-suites=[python|openssl|STRING] Override the OpenSSL default cipher suites string: @@ -527,19 +527,19 @@ macOS Options See ``Mac/README.rst``. -.. cmdoption:: --enable-universalsdk -.. cmdoption:: --enable-universalsdk=SDKDIR +.. option:: --enable-universalsdk +.. option:: --enable-universalsdk=SDKDIR Create a universal binary build. *SDKDIR* specifies which macOS SDK should be used to perform the build (default is no). -.. cmdoption:: --enable-framework -.. cmdoption:: --enable-framework=INSTALLDIR +.. option:: --enable-framework +.. option:: --enable-framework=INSTALLDIR Create a Python.framework rather than a traditional Unix install. Optional *INSTALLDIR* specifies the installation path (default is no). -.. cmdoption:: --with-universal-archs=ARCH +.. option:: --with-universal-archs=ARCH Specify the kind of universal binary that should be created. This option is only valid when :option:`--enable-universalsdk` is set. @@ -555,7 +555,7 @@ See ``Mac/README.rst``. * ``intel-64``; * ``all``. -.. cmdoption:: --with-framework-name=FRAMEWORK +.. option:: --with-framework-name=FRAMEWORK Specify the name for the python framework on macOS only valid when :option:`--enable-framework` is set (default: ``Python``). @@ -569,21 +569,21 @@ for another CPU architecture or platform. Cross compiling requires a Python interpreter for the build platform. The version of the build Python must match the version of the cross compiled host Python. -.. cmdoption:: --build=BUILD +.. option:: --build=BUILD configure for building on BUILD, usually guessed by :program:`config.guess`. -.. cmdoption:: --host=HOST +.. option:: --host=HOST cross-compile to build programs to run on HOST (target platform) -.. cmdoption:: --with-build-python=path/to/python +.. option:: --with-build-python=path/to/python path to build ``python`` binary for cross compiling .. versionadded:: 3.11 -.. cmdoption:: CONFIG_SITE=file +.. option:: CONFIG_SITE=file An environment variable that points to a file with configure overrides. From 8da3367067f3e22d2369a00640185525813520fb Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 5 Oct 2023 12:18:44 -0700 Subject: [PATCH 419/632] [3.11] gh-110167: Fix test_socket deadlock in doCleanups() (GH-110416) (#110424) gh-110167: Fix test_socket deadlock in doCleanups() (GH-110416) Fix a deadlock in test_socket when server fails with a timeout but the client is still running in its thread. Don't hold a lock to call cleanup functions in doCleanups(). One of the cleanup function waits until the client completes, whereas the client could deadlock if it called addCleanup() in such situation. doCleanups() is called when the server completed, but the client can still be running in its thread especially if the server failed with a timeout. Don't put a lock on doCleanups() to prevent deadlock between addCleanup() called in the client and doCleanups() waiting for self.done.wait of ThreadableTest._setUp(). (cherry picked from commit 318f5df27109ff8d2519edefa771920a0ec62b92) Co-authored-by: Victor Stinner --- Lib/test/test_socket.py | 12 +++++++----- .../2023-10-05-19-33-49.gh-issue-110167.mIdj3v.rst | 5 +++++ 2 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2023-10-05-19-33-49.gh-issue-110167.mIdj3v.rst diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 190efb416ba39a..bc93d9988eaf03 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -204,8 +204,13 @@ def setUp(self): class ThreadSafeCleanupTestCase: """Subclass of unittest.TestCase with thread-safe cleanup methods. - This subclass protects the addCleanup() and doCleanups() methods - with a recursive lock. + This subclass protects the addCleanup() method with a recursive lock. + + doCleanups() is called when the server completed, but the client can still + be running in its thread especially if the server failed with a timeout. + Don't put a lock on doCleanups() to prevent deadlock between addCleanup() + called in the client and doCleanups() waiting for self.done.wait of + ThreadableTest._setUp() (gh-110167) """ def __init__(self, *args, **kwargs): @@ -216,9 +221,6 @@ def addCleanup(self, *args, **kwargs): with self._cleanup_lock: return super().addCleanup(*args, **kwargs) - def doCleanups(self, *args, **kwargs): - with self._cleanup_lock: - return super().doCleanups(*args, **kwargs) class SocketCANTest(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Tests/2023-10-05-19-33-49.gh-issue-110167.mIdj3v.rst b/Misc/NEWS.d/next/Tests/2023-10-05-19-33-49.gh-issue-110167.mIdj3v.rst new file mode 100644 index 00000000000000..d0cbbf9c3788bb --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-10-05-19-33-49.gh-issue-110167.mIdj3v.rst @@ -0,0 +1,5 @@ +Fix a deadlock in test_socket when server fails with a timeout but the +client is still running in its thread. Don't hold a lock to call cleanup +functions in doCleanups(). One of the cleanup function waits until the +client completes, whereas the client could deadlock if it called +addCleanup() in such situation. Patch by Victor Stinner. From a503bdf21fefb61999d1b4afdd288156b3138655 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 5 Oct 2023 12:53:14 -0700 Subject: [PATCH 420/632] [3.11] gh-109840: Fix multiprocessing test_waitfor_timeout() (GH-110428) (#110431) gh-109840: Fix multiprocessing test_waitfor_timeout() (GH-110428) Don't measure the CI performance: don't fail if cond.wait_for() takes longer than 1 second on a slow CI. (cherry picked from commit 5eae8dc2cb832af6ae1ee340fb0194107fe3bd6e) Co-authored-by: Victor Stinner --- Lib/test/_test_multiprocessing.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 6459757eb93d51..42441fed79bf3e 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -1650,12 +1650,12 @@ def test_waitfor(self): def _test_waitfor_timeout_f(cls, cond, state, success, sem): sem.release() with cond: - expected = 0.1 + expected = 0.100 dt = time.monotonic() result = cond.wait_for(lambda : state.value==4, timeout=expected) dt = time.monotonic() - dt # borrow logic in assertTimeout() from test/lock_tests.py - if not result and expected * 0.6 < dt < expected * 10.0: + if not result and expected * 0.6 <= dt: success.value = True @unittest.skipUnless(HAS_SHAREDCTYPES, 'needs sharedctypes') @@ -1674,7 +1674,7 @@ def test_waitfor_timeout(self): # Only increment 3 times, so state == 4 is never reached. for i in range(3): - time.sleep(0.01) + time.sleep(0.010) with cond: state.value += 1 cond.notify() From a55c203104cedfa5f5c600671607ee2f31425690 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 5 Oct 2023 13:14:32 -0700 Subject: [PATCH 421/632] [3.11] gh-110383: Swap 'the all' -> 'all the' in socket docs (GH-110434) (#110436) Co-authored-by: Bradley Reynolds --- Doc/library/socket.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index 36151ca650e52e..32afacbbd231a7 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -2001,7 +2001,7 @@ The next two examples are identical to the above two, but support both IPv4 and IPv6. The server side will listen to the first address family available (it should listen to both instead). On most of IPv6-ready systems, IPv6 will take precedence and the server may not accept IPv4 traffic. The client side will try -to connect to the all addresses returned as a result of the name resolution, and +to connect to all the addresses returned as a result of the name resolution, and sends traffic to the first one connected successfully. :: # Echo server program From 779481ef15ea44bde76a45e1ddfcbc8b9a39809c Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 5 Oct 2023 13:49:32 -0700 Subject: [PATCH 422/632] [3.11] gh-110429: Fix race condition in "make regen-all" (GH-110433) (#110439) gh-110429: Fix race condition in "make regen-all" (GH-110433) "make regen-pegen" now creates a temporary file called "parser.c.new" instead of "parser.new.c". Previously, if "make clinic" was run in parallel with "make regen-all", clinic may try but fail to open "parser.new.c" if the temporay file was removed in the meanwhile. (cherry picked from commit fb6c4ed2bbb2a867d5f0b9a94656e4714be5d9c2) Co-authored-by: Victor Stinner --- Makefile.pre.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile.pre.in b/Makefile.pre.in index ba8e6349e815de..f84ab130ddbd2e 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1301,8 +1301,8 @@ regen-pegen: PYTHONPATH=$(srcdir)/Tools/peg_generator $(PYTHON_FOR_REGEN) -m pegen -q c \ $(srcdir)/Grammar/python.gram \ $(srcdir)/Grammar/Tokens \ - -o $(srcdir)/Parser/parser.new.c - $(UPDATE_FILE) $(srcdir)/Parser/parser.c $(srcdir)/Parser/parser.new.c + -o $(srcdir)/Parser/parser.c.new + $(UPDATE_FILE) $(srcdir)/Parser/parser.c $(srcdir)/Parser/parser.c.new .PHONY=regen-ast regen-ast: From 331d90f30bcdefce6647c56126ad019a42499f15 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 5 Oct 2023 14:10:01 -0700 Subject: [PATCH 423/632] [3.11] [3.12] gh-110167: Increase support.LOOPBACK_TIMEOUT to 10 seconds (GH-110413) (GH-110427) (#110440) [3.12] gh-110167: Increase support.LOOPBACK_TIMEOUT to 10 seconds (GH-110413) (GH-110427) gh-110167: Increase support.LOOPBACK_TIMEOUT to 10 seconds (GH-110413) Increase support.LOOPBACK_TIMEOUT from 5 to 10 seconds. Also increase the timeout depending on the --timeout option. For example, for a test timeout of 40 minutes (ARM Raspbian 3.x), use LOOPBACK_TIMEOUT of 20 seconds instead of 5 seconds before. (cherry picked from commit 350d89b79588ebd140c3987cc05e3719ca17a973) Co-authored-by: Victor Stinner (cherry picked from commit 0db2f1475e6539e1954e1f8bd53e005c3ecd6a26) Co-authored-by: Victor Stinner --- Lib/test/libregrtest/setup.py | 17 ++++++++++------- Lib/test/support/__init__.py | 8 +------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/Lib/test/libregrtest/setup.py b/Lib/test/libregrtest/setup.py index c097d6d597aa05..ecd7fa33107311 100644 --- a/Lib/test/libregrtest/setup.py +++ b/Lib/test/libregrtest/setup.py @@ -88,16 +88,19 @@ def _test_audit_hook(name, args): setup_unraisable_hook() setup_threading_excepthook() - if ns.timeout is not None: + timeout = ns.timeout + if timeout is not None: # For a slow buildbot worker, increase SHORT_TIMEOUT and LONG_TIMEOUT - support.SHORT_TIMEOUT = max(support.SHORT_TIMEOUT, ns.timeout / 40) - support.LONG_TIMEOUT = max(support.LONG_TIMEOUT, ns.timeout / 4) + support.LOOPBACK_TIMEOUT = max(support.LOOPBACK_TIMEOUT, timeout / 120) + # don't increase INTERNET_TIMEOUT + support.SHORT_TIMEOUT = max(support.SHORT_TIMEOUT, timeout / 40) + support.LONG_TIMEOUT = max(support.LONG_TIMEOUT, timeout / 4) # If --timeout is short: reduce timeouts - support.LOOPBACK_TIMEOUT = min(support.LOOPBACK_TIMEOUT, ns.timeout) - support.INTERNET_TIMEOUT = min(support.INTERNET_TIMEOUT, ns.timeout) - support.SHORT_TIMEOUT = min(support.SHORT_TIMEOUT, ns.timeout) - support.LONG_TIMEOUT = min(support.LONG_TIMEOUT, ns.timeout) + support.LOOPBACK_TIMEOUT = min(support.LOOPBACK_TIMEOUT, timeout) + support.INTERNET_TIMEOUT = min(support.INTERNET_TIMEOUT, timeout) + support.SHORT_TIMEOUT = min(support.SHORT_TIMEOUT, timeout) + support.LONG_TIMEOUT = min(support.LONG_TIMEOUT, timeout) if ns.xmlpath: from test.support.testresult import RegressionTestResult diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index fbbd59d719d193..9da0c8e5c355ba 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -72,13 +72,7 @@ # # The timeout should be long enough for connect(), recv() and send() methods # of socket.socket. -LOOPBACK_TIMEOUT = 5.0 -if sys.platform == 'win32' and ' 32 bit (ARM)' in sys.version: - # bpo-37553: test_socket.SendfileUsingSendTest is taking longer than 2 - # seconds on Windows ARM32 buildbot - LOOPBACK_TIMEOUT = 10 -elif sys.platform == 'vxworks': - LOOPBACK_TIMEOUT = 10 +LOOPBACK_TIMEOUT = 10.0 # Timeout in seconds for network requests going to the internet. The timeout is # short enough to prevent a test to wait for too long if the internet request From 4134036ce9673b76cc8e6236361a77098ee72ad1 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 5 Oct 2023 15:05:20 -0700 Subject: [PATCH 424/632] [3.11] gh-110393: Remove watchdog with hardcoded timeout (GH-110400) (#110444) gh-110393: Remove watchdog with hardcoded timeout (GH-110400) test_builtin and test_socketserver no longer use signal.alarm() to implement a watchdog with a hardcoded timeout (2 and 60 seconds). Python test runner regrtest has two watchdogs: faulthandler and timeout on running worker processes. Tests using short hardcoded timeout can fail on slowest buildbots just because the timeout is too short. (cherry picked from commit 1328fa31fe9c72748fc6fd11d017c82aafd48a49) Co-authored-by: Victor Stinner --- Lib/test/test_builtin.py | 2 -- Lib/test/test_socketserver.py | 7 ------- 2 files changed, 9 deletions(-) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 2f56121616629e..d6edc2b431d32a 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -2146,8 +2146,6 @@ def _run_child(self, child, terminal_input): if pid == 0: # Child try: - # Make sure we don't get stuck if there's a problem - signal.alarm(2) os.close(r) with open(w, "w") as wpipe: child(wpipe) diff --git a/Lib/test/test_socketserver.py b/Lib/test/test_socketserver.py index 2edb1e0c0e21e2..80e1968c0bf676 100644 --- a/Lib/test/test_socketserver.py +++ b/Lib/test/test_socketserver.py @@ -33,11 +33,6 @@ HAVE_FORKING = test.support.has_fork_support requires_forking = unittest.skipUnless(HAVE_FORKING, 'requires forking') -def signal_alarm(n): - """Call signal.alarm when it exists (i.e. not on Windows).""" - if hasattr(signal, 'alarm'): - signal.alarm(n) - # Remember real select() to avoid interferences with mocking _real_select = select.select @@ -77,12 +72,10 @@ class SocketServerTest(unittest.TestCase): """Test all socket servers.""" def setUp(self): - signal_alarm(60) # Kill deadlocks after 60 seconds. self.port_seed = 0 self.test_files = [] def tearDown(self): - signal_alarm(0) # Didn't deadlock. reap_children() for fn in self.test_files: From 67129c379cdfc7e880bc1cfdef3df1f562c94853 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 5 Oct 2023 15:06:28 -0700 Subject: [PATCH 425/632] [3.11] gh-109888: Fix test_os _kill_with_event() on Windows (GH-110421) (#110443) gh-109888: Fix test_os _kill_with_event() on Windows (GH-110421) Replace os.kill() with proc.kill() which catchs PermissionError. Rewrite _kill_with_event(): * Use subprocess context manager ("with proc:"). * Use sleeping_retry() to wait until the child process is ready. * Replace SIGINT with proc.kill() on error. * Replace 10 seconds with SHORT_TIMEOUT to wait until the process is ready. * Replace 0.5 seconds with SHORT_TIMEOUT to wait for the process exit. (cherry picked from commit aaf297c048694cd9652790f8b74e69f7ddadfbde) Co-authored-by: Victor Stinner --- Lib/test/test_os.py | 50 ++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 1e1980e3ac47e5..ff1121efdf74da 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2505,30 +2505,34 @@ def _kill_with_event(self, event, name): tagname = "test_os_%s" % uuid.uuid1() m = mmap.mmap(-1, 1, tagname) m[0] = 0 + # Run a script which has console control handling enabled. - proc = subprocess.Popen([sys.executable, - os.path.join(os.path.dirname(__file__), - "win_console_handler.py"), tagname], - creationflags=subprocess.CREATE_NEW_PROCESS_GROUP) - # Let the interpreter startup before we send signals. See #3137. - count, max = 0, 100 - while count < max and proc.poll() is None: - if m[0] == 1: - break - time.sleep(0.1) - count += 1 - else: - # Forcefully kill the process if we weren't able to signal it. - os.kill(proc.pid, signal.SIGINT) - self.fail("Subprocess didn't finish initialization") - os.kill(proc.pid, event) - # proc.send_signal(event) could also be done here. - # Allow time for the signal to be passed and the process to exit. - time.sleep(0.5) - if not proc.poll(): - # Forcefully kill the process if we weren't able to signal it. - os.kill(proc.pid, signal.SIGINT) - self.fail("subprocess did not stop on {}".format(name)) + script = os.path.join(os.path.dirname(__file__), + "win_console_handler.py") + cmd = [sys.executable, script, tagname] + proc = subprocess.Popen(cmd, + creationflags=subprocess.CREATE_NEW_PROCESS_GROUP) + + with proc: + # Let the interpreter startup before we send signals. See #3137. + for _ in support.sleeping_retry(support.SHORT_TIMEOUT): + if proc.poll() is None: + break + else: + # Forcefully kill the process if we weren't able to signal it. + proc.kill() + self.fail("Subprocess didn't finish initialization") + + os.kill(proc.pid, event) + + try: + # proc.send_signal(event) could also be done here. + # Allow time for the signal to be passed and the process to exit. + proc.wait(timeout=support.SHORT_TIMEOUT) + except subprocess.TimeoutExpired: + # Forcefully kill the process if we weren't able to signal it. + proc.kill() + self.fail("subprocess did not stop on {}".format(name)) @unittest.skip("subprocesses aren't inheriting Ctrl+C property") @support.requires_subprocess() From f7a1d7d060168f668dbc985bd978d8b009d6e300 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 5 Oct 2023 18:10:39 -0700 Subject: [PATCH 426/632] [3.11] gh-103053: Fix make check-clean-src: check "python" program (GH-110449) (#110454) gh-103053: Fix make check-clean-src: check "python" program (GH-110449) "make check-clean-src" now also checks if the "python" program is found in the source directory: fail with an error if it does exist. (cherry picked from commit a155f9f3427578ca5706d27e20bd0576f0395073) Co-authored-by: Victor Stinner --- Makefile.pre.in | 3 ++- .../next/Build/2023-10-06-02-15-23.gh-issue-103053.--7JUF.rst | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Build/2023-10-06-02-15-23.gh-issue-103053.--7JUF.rst diff --git a/Makefile.pre.in b/Makefile.pre.in index f84ab130ddbd2e..1dd8189167aa21 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -592,7 +592,8 @@ build_wasm: check-clean-src $(BUILDPYTHON) platform oldsharedmods python-config # Check that the source is clean when building out of source. check-clean-src: @if test -n "$(VPATH)" -a \( \ - -f "$(srcdir)/Programs/python.o" \ + -f "$(srcdir)/$(BUILDPYTHON)" \ + -o -f "$(srcdir)/Programs/python.o" \ -o -f "$(srcdir)\Python/frozen_modules/importlib._bootstrap.h" \ \); then \ echo "Error: The source directory ($(srcdir)) is not clean" ; \ diff --git a/Misc/NEWS.d/next/Build/2023-10-06-02-15-23.gh-issue-103053.--7JUF.rst b/Misc/NEWS.d/next/Build/2023-10-06-02-15-23.gh-issue-103053.--7JUF.rst new file mode 100644 index 00000000000000..81aa21357287c7 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2023-10-06-02-15-23.gh-issue-103053.--7JUF.rst @@ -0,0 +1,3 @@ +"make check-clean-src" now also checks if the "python" program is found in +the source directory: fail with an error if it does exist. Patch by Victor +Stinner. From 4499e6cafff114cf7cfb360706f22c4b7a54390e Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 5 Oct 2023 18:32:21 -0700 Subject: [PATCH 427/632] [3.11] gh-103053: Fix test_tools.test_freeze on FreeBSD (GH-110451) (#110457) gh-103053: Fix test_tools.test_freeze on FreeBSD (GH-110451) Fix test_tools.test_freeze on FreeBSD: run "make distclean" instead of "make clean" in the copied source directory to remove also the "python" program. Other test_freeze changes: * Log executed commands and directories, and the current directory. * No longer uses make -C option to change the directory, instead use subprocess cwd parameter. (cherry picked from commit a4baa9e8ac62cac3ea6363b15ea585b1998ea1f9) Co-authored-by: Victor Stinner --- ...-10-06-02-32-18.gh-issue-103053.VfxBLI.rst | 3 ++ Tools/freeze/test/freeze.py | 33 +++++++++++-------- 2 files changed, 22 insertions(+), 14 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2023-10-06-02-32-18.gh-issue-103053.VfxBLI.rst diff --git a/Misc/NEWS.d/next/Tests/2023-10-06-02-32-18.gh-issue-103053.VfxBLI.rst b/Misc/NEWS.d/next/Tests/2023-10-06-02-32-18.gh-issue-103053.VfxBLI.rst new file mode 100644 index 00000000000000..90a7ca512c97dd --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-10-06-02-32-18.gh-issue-103053.VfxBLI.rst @@ -0,0 +1,3 @@ +Fix test_tools.test_freeze on FreeBSD: run "make distclean" instead of "make +clean" in the copied source directory to remove also the "python" program. +Patch by Victor Stinner. diff --git a/Tools/freeze/test/freeze.py b/Tools/freeze/test/freeze.py index cdf77c57bbb6ae..d5f4d3d24c2bd6 100644 --- a/Tools/freeze/test/freeze.py +++ b/Tools/freeze/test/freeze.py @@ -27,8 +27,10 @@ class UnsupportedError(Exception): """The operation isn't supported.""" -def _run_quiet(cmd, cwd=None): - #print(f'# {" ".join(shlex.quote(a) for a in cmd)}') +def _run_quiet(cmd, *, cwd=None): + if cwd: + print('+', 'cd', cwd, flush=True) + print('+', shlex.join(cmd), flush=True) try: return subprocess.run( cmd, @@ -48,8 +50,8 @@ def _run_quiet(cmd, cwd=None): raise -def _run_stdout(cmd, cwd=None): - proc = _run_quiet(cmd, cwd) +def _run_stdout(cmd): + proc = _run_quiet(cmd) return proc.stdout.strip() @@ -91,13 +93,18 @@ def copy_source_tree(newroot, oldroot): shutil.copytree(oldroot, newroot, ignore=support.copy_python_src_ignore) if os.path.exists(os.path.join(newroot, 'Makefile')): - _run_quiet([MAKE, 'clean'], newroot) + # Out-of-tree builds require a clean srcdir. "make clean" keeps + # the "python" program, so use "make distclean" instead. + _run_quiet([MAKE, 'distclean'], cwd=newroot) ################################## # freezing def prepare(script=None, outdir=None): + print() + print("cwd:", os.getcwd()) + if not outdir: outdir = OUTDIR os.makedirs(outdir, exist_ok=True) @@ -125,7 +132,7 @@ def prepare(script=None, outdir=None): ensure_opt(cmd, 'cache-file', os.path.join(outdir, 'python-config.cache')) prefix = os.path.join(outdir, 'python-installation') ensure_opt(cmd, 'prefix', prefix) - _run_quiet(cmd, builddir) + _run_quiet(cmd, cwd=builddir) if not MAKE: raise UnsupportedError('make') @@ -135,20 +142,18 @@ def prepare(script=None, outdir=None): # this test is most often run as part of the whole suite with a lot # of other tests running in parallel, from 1-2 vCPU systems up to # people's NNN core beasts. Don't attempt to use it all. - parallel = f'-j{cores*2//3}' + jobs = cores * 2 // 3 + parallel = f'-j{jobs}' else: parallel = '-j2' # Build python. print(f'building python {parallel=} in {builddir}...') - if os.path.exists(os.path.join(srcdir, 'Makefile')): - # Out-of-tree builds require a clean srcdir. - _run_quiet([MAKE, '-C', srcdir, 'clean']) - _run_quiet([MAKE, '-C', builddir, parallel]) + _run_quiet([MAKE, parallel], cwd=builddir) # Install the build. print(f'installing python into {prefix}...') - _run_quiet([MAKE, '-C', builddir, 'install']) + _run_quiet([MAKE, 'install'], cwd=builddir) python = os.path.join(prefix, 'bin', 'python3') return outdir, scriptfile, python @@ -161,8 +166,8 @@ def freeze(python, scriptfile, outdir): print(f'freezing {scriptfile}...') os.makedirs(outdir, exist_ok=True) # Use -E to ignore PYTHONSAFEPATH - _run_quiet([python, '-E', FREEZE, '-o', outdir, scriptfile], outdir) - _run_quiet([MAKE, '-C', os.path.dirname(scriptfile)]) + _run_quiet([python, '-E', FREEZE, '-o', outdir, scriptfile], cwd=outdir) + _run_quiet([MAKE], cwd=os.path.dirname(scriptfile)) name = os.path.basename(scriptfile).rpartition('.')[0] executable = os.path.join(outdir, name) From 88223f15d792159bb2ab26923cb21bee910a4565 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 6 Oct 2023 12:19:49 +0200 Subject: [PATCH 428/632] [3.11] Add support.MS_WINDOWS constant (#110446) (#110452) (#110464) [3.12] Add support.MS_WINDOWS constant (#110446) (#110452) Add support.MS_WINDOWS constant (#110446) (cherry picked from commit e0c44377935de3491b2cbe1e5f87f8b336fdc922) (cherry picked from commit e188534607761af1f8faba483ce0b1e8578c73e5) --- Lib/test/_test_embed_set_config.py | 2 +- Lib/test/pythoninfo.py | 13 +++++++++++++ Lib/test/support/__init__.py | 4 +++- Lib/test/test_asyncio/test_subprocess.py | 5 ++--- Lib/test/test_cppext/__init__.py | 5 ++--- Lib/test/test_cppext/setup.py | 6 ++---- Lib/test/test_embed.py | 4 +--- Lib/test/test_faulthandler.py | 4 +--- Lib/test/test_gdb/__init__.py | 3 +-- Lib/test/test_utf8_mode.py | 3 +-- 10 files changed, 27 insertions(+), 22 deletions(-) diff --git a/Lib/test/_test_embed_set_config.py b/Lib/test/_test_embed_set_config.py index 7ff641b37bf188..05e83a65fd4a35 100644 --- a/Lib/test/_test_embed_set_config.py +++ b/Lib/test/_test_embed_set_config.py @@ -9,9 +9,9 @@ import os import sys import unittest +from test.support import MS_WINDOWS -MS_WINDOWS = (os.name == 'nt') MAX_HASH_SEED = 4294967295 class SetConfigTests(unittest.TestCase): diff --git a/Lib/test/pythoninfo.py b/Lib/test/pythoninfo.py index db8dfe247cca25..3dbcac2f86e81e 100644 --- a/Lib/test/pythoninfo.py +++ b/Lib/test/pythoninfo.py @@ -713,6 +713,19 @@ def collect_test_support(info_add): attributes = ('IPV6_ENABLED',) copy_attributes(info_add, support, 'test_support.%s', attributes) + attributes = ( + 'MS_WINDOWS', + 'has_fork_support', + 'has_socket_support', + 'has_strftime_extensions', + 'has_subprocess_support', + 'is_android', + 'is_emscripten', + 'is_jython', + 'is_wasi', + ) + copy_attributes(info_add, support, 'support.%s', attributes) + call_func(info_add, 'test_support._is_gui_available', support, '_is_gui_available') call_func(info_add, 'test_support.python_is_optimized', support, 'python_is_optimized') diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 9da0c8e5c355ba..1d3b0053bf86c9 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -48,7 +48,7 @@ "check__all__", "skip_if_buggy_ucrt_strfptime", "check_disallow_instantiation", "check_sanitizer", "skip_if_sanitizer", # sys - "is_jython", "is_android", "is_emscripten", "is_wasi", + "MS_WINDOWS", "is_jython", "is_android", "is_emscripten", "is_wasi", "check_impl_detail", "unix_shell", "setswitchinterval", # network "open_urlresource", @@ -501,6 +501,8 @@ def requires_debug_ranges(reason='requires co_positions / debug_ranges'): requires_legacy_unicode_capi = unittest.skipUnless(unicode_legacy_string, 'requires legacy Unicode C API') +MS_WINDOWS = (sys.platform == 'win32') + is_jython = sys.platform.startswith('java') is_android = hasattr(sys, 'getandroidapilevel') diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py index 8b4f14eb48de65..f0d1d95d2c1ec0 100644 --- a/Lib/test/test_asyncio/test_subprocess.py +++ b/Lib/test/test_asyncio/test_subprocess.py @@ -15,8 +15,7 @@ from test.support import os_helper -MS_WINDOWS = (sys.platform == 'win32') -if MS_WINDOWS: +if support.MS_WINDOWS: import msvcrt else: from asyncio import unix_events @@ -266,7 +265,7 @@ def test_stdin_broken_pipe(self): rfd, wfd = os.pipe() self.addCleanup(os.close, rfd) self.addCleanup(os.close, wfd) - if MS_WINDOWS: + if support.MS_WINDOWS: handle = msvcrt.get_osfhandle(rfd) os.set_handle_inheritable(handle, True) code = textwrap.dedent(f''' diff --git a/Lib/test/test_cppext/__init__.py b/Lib/test/test_cppext/__init__.py index 37247e4ac01e71..b3999d011e997b 100644 --- a/Lib/test/test_cppext/__init__.py +++ b/Lib/test/test_cppext/__init__.py @@ -9,7 +9,6 @@ from test.support import os_helper -MS_WINDOWS = (sys.platform == 'win32') SETUP = os.path.join(os.path.dirname(__file__), 'setup.py') @@ -25,7 +24,7 @@ def test_build_cpp03(self): # With MSVC, the linker fails with: cannot open file 'python311.lib' # https://github.com/python/cpython/pull/32175#issuecomment-1111175897 - @unittest.skipIf(MS_WINDOWS, 'test fails on Windows') + @unittest.skipIf(support.MS_WINDOWS, 'test fails on Windows') # Building and running an extension in clang sanitizing mode is not # straightforward @unittest.skipIf( @@ -53,7 +52,7 @@ def _check_build(self, std_cpp03, extension_name): python_exe = 'python' if sys.executable.endswith('.exe'): python_exe += '.exe' - if MS_WINDOWS: + if support.MS_WINDOWS: python = os.path.join(venv_dir, 'Scripts', python_exe) else: python = os.path.join(venv_dir, 'bin', python_exe) diff --git a/Lib/test/test_cppext/setup.py b/Lib/test/test_cppext/setup.py index f74124775acd06..10f4ed21207fc5 100644 --- a/Lib/test/test_cppext/setup.py +++ b/Lib/test/test_cppext/setup.py @@ -4,15 +4,13 @@ import shlex import sys import sysconfig +from test import support from setuptools import setup, Extension -MS_WINDOWS = (sys.platform == 'win32') - - SOURCE = os.path.join(os.path.dirname(__file__), 'extension.cpp') -if not MS_WINDOWS: +if not support.MS_WINDOWS: # C++ compiler flags for GCC and clang CPPFLAGS = [ # gh-91321: The purpose of _testcppext extension is to check that building diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index 2affa17e800c07..1026dcd0acaab7 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -1,7 +1,6 @@ # Run the tests in Programs/_testembed.c (tests for the CPython embedding APIs) from test import support -from test.support import import_helper -from test.support import os_helper +from test.support import import_helper, os_helper, MS_WINDOWS import unittest from collections import namedtuple @@ -20,7 +19,6 @@ if not support.has_subprocess_support: raise unittest.SkipTest("test module requires subprocess") -MS_WINDOWS = (os.name == 'nt') MACOS = (sys.platform == 'darwin') Py_DEBUG = hasattr(sys, 'gettotalrefcount') PYMEM_ALLOCATOR_NOT_SET = 0 diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py index 0e03ee2128891c..50b40d336e4dca 100644 --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py @@ -7,8 +7,7 @@ import subprocess import sys from test import support -from test.support import os_helper -from test.support import script_helper, is_android +from test.support import os_helper, script_helper, is_android, MS_WINDOWS from test.support import skip_if_sanitizer import tempfile import unittest @@ -23,7 +22,6 @@ raise unittest.SkipTest("test module requires subprocess") TIMEOUT = 0.5 -MS_WINDOWS = (os.name == 'nt') def expected_traceback(lineno1, lineno2, header, min_count=1): diff --git a/Lib/test/test_gdb/__init__.py b/Lib/test/test_gdb/__init__.py index d74075e456792d..99557739af6748 100644 --- a/Lib/test/test_gdb/__init__.py +++ b/Lib/test/test_gdb/__init__.py @@ -9,8 +9,7 @@ from test import support -MS_WINDOWS = (os.name == 'nt') -if MS_WINDOWS: +if support.MS_WINDOWS: # On Windows, Python is usually built by MSVC. Passing /p:DebugSymbols=true # option to MSBuild produces PDB debug symbols, but gdb doesn't support PDB # debug symbol files. diff --git a/Lib/test/test_utf8_mode.py b/Lib/test/test_utf8_mode.py index ec29ba6d51b127..f66881044e16df 100644 --- a/Lib/test/test_utf8_mode.py +++ b/Lib/test/test_utf8_mode.py @@ -9,10 +9,9 @@ import unittest from test import support from test.support.script_helper import assert_python_ok, assert_python_failure -from test.support import os_helper +from test.support import os_helper, MS_WINDOWS -MS_WINDOWS = (sys.platform == 'win32') POSIX_LOCALES = ('C', 'POSIX') VXWORKS = (sys.platform == "vxworks") From 49a45f6cd80605998eb50fd95ca4b5531674e290 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 6 Oct 2023 07:11:51 -0700 Subject: [PATCH 429/632] [3.11] gh-110184: Fix subprocess test_pipesize_default() (GH-110465) (#110472) gh-110184: Fix subprocess test_pipesize_default() (GH-110465) For proc.stdin, get the size of the read end of the test pipe. Use subprocess context manager ("with proc:"). (cherry picked from commit d023d4166b255023dac448305270350030101481) Co-authored-by: Victor Stinner --- Lib/test/test_subprocess.py | 41 +++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 3142026ac535b3..b7b16e22fa6419 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -747,31 +747,36 @@ def test_pipesizes(self): @unittest.skipUnless(fcntl and hasattr(fcntl, 'F_GETPIPE_SZ'), 'fcntl.F_GETPIPE_SZ required for test.') def test_pipesize_default(self): - p = subprocess.Popen( + proc = subprocess.Popen( [sys.executable, "-c", 'import sys; sys.stdin.read(); sys.stdout.write("out"); ' 'sys.stderr.write("error!")'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, pipesize=-1) - try: - fp_r, fp_w = os.pipe() + + with proc: try: - default_pipesize = fcntl.fcntl(fp_w, fcntl.F_GETPIPE_SZ) - for fifo in [p.stdin, p.stdout, p.stderr]: - self.assertEqual( - fcntl.fcntl(fifo.fileno(), fcntl.F_GETPIPE_SZ), - default_pipesize) + fp_r, fp_w = os.pipe() + try: + default_read_pipesize = fcntl.fcntl(fp_r, fcntl.F_GETPIPE_SZ) + default_write_pipesize = fcntl.fcntl(fp_w, fcntl.F_GETPIPE_SZ) + finally: + os.close(fp_r) + os.close(fp_w) + + self.assertEqual( + fcntl.fcntl(proc.stdin.fileno(), fcntl.F_GETPIPE_SZ), + default_read_pipesize) + self.assertEqual( + fcntl.fcntl(proc.stdout.fileno(), fcntl.F_GETPIPE_SZ), + default_write_pipesize) + self.assertEqual( + fcntl.fcntl(proc.stderr.fileno(), fcntl.F_GETPIPE_SZ), + default_write_pipesize) + # On other platforms we cannot test the pipe size (yet). But above + # code using pipesize=-1 should not crash. finally: - os.close(fp_r) - os.close(fp_w) - # On other platforms we cannot test the pipe size (yet). But above - # code using pipesize=-1 should not crash. - p.stdin.close() - p.stdout.close() - p.stderr.close() - finally: - p.kill() - p.wait() + proc.kill() def test_env(self): newenv = os.environ.copy() From 3d5aa7ec6129330c10e76435b627ff1df2cb08f7 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 6 Oct 2023 07:35:23 -0700 Subject: [PATCH 430/632] [3.11] Fix typo in Doc/library/textwrap.rst (GH-110328) (#110474) Co-authored-by: InSync <122007197+InSyncWithFoo@users.noreply.github.com> --- Doc/library/textwrap.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/textwrap.rst b/Doc/library/textwrap.rst index e2952ce3cc2ca3..7445410f91808c 100644 --- a/Doc/library/textwrap.rst +++ b/Doc/library/textwrap.rst @@ -238,7 +238,7 @@ hyphenated words; only then will long words be broken if necessary, unless However, the sentence detection algorithm is imperfect: it assumes that a sentence ending consists of a lowercase letter followed by one of ``'.'``, ``'!'``, or ``'?'``, possibly followed by one of ``'"'`` or ``"'"``, - followed by a space. One problem with this is algorithm is that it is + followed by a space. One problem with this algorithm is that it is unable to detect the difference between "Dr." in :: [...] Dr. Frankenstein's monster [...] From 6a33529cf0bcfe9001af58e45ea8feab4daed265 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 7 Oct 2023 16:05:13 +0300 Subject: [PATCH 431/632] [3.11] gh-109521: Fix obscure cases handling in PyImport_GetImporter() (GH-109522) (GH-109781) PyImport_GetImporter() now sets RuntimeError if it fails to get sys.path_hooks or sys.path_importer_cache or they are not list and dict correspondingly. Previously it could return NULL without setting error in obscure cases, crash or raise SystemError if these attributes have wrong type. (cherry picked from commit 62c7015e89cbdedb5218d4fedd45f971885f67a8) --- ...2023-09-17-21-47-31.gh-issue-109521.JDF6i9.rst | 5 +++++ Python/import.c | 15 +++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2023-09-17-21-47-31.gh-issue-109521.JDF6i9.rst diff --git a/Misc/NEWS.d/next/C API/2023-09-17-21-47-31.gh-issue-109521.JDF6i9.rst b/Misc/NEWS.d/next/C API/2023-09-17-21-47-31.gh-issue-109521.JDF6i9.rst new file mode 100644 index 00000000000000..338650c9246686 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2023-09-17-21-47-31.gh-issue-109521.JDF6i9.rst @@ -0,0 +1,5 @@ +:c:func:`PyImport_GetImporter` now sets RuntimeError if it fails to get +:data:`sys.path_hooks` or :data:`sys.path_importer_cache` or they are not +list and dict correspondingly. Previously it could return NULL without +setting error in obscure cases, crash or raise SystemError if these +attributes have wrong type. diff --git a/Python/import.c b/Python/import.c index 38c23e02cec6a2..39144d302193ac 100644 --- a/Python/import.c +++ b/Python/import.c @@ -951,11 +951,22 @@ PyImport_GetImporter(PyObject *path) { PyThreadState *tstate = _PyThreadState_GET(); PyObject *path_importer_cache = PySys_GetObject("path_importer_cache"); + if (path_importer_cache == NULL) { + PyErr_SetString(PyExc_RuntimeError, "lost sys.path_importer_cache"); + return NULL; + } + Py_INCREF(path_importer_cache); PyObject *path_hooks = PySys_GetObject("path_hooks"); - if (path_importer_cache == NULL || path_hooks == NULL) { + if (path_hooks == NULL) { + PyErr_SetString(PyExc_RuntimeError, "lost sys.path_hooks"); + Py_DECREF(path_importer_cache); return NULL; } - return get_path_importer(tstate, path_importer_cache, path_hooks, path); + Py_INCREF(path_hooks); + PyObject *importer = get_path_importer(tstate, path_importer_cache, path_hooks, path); + Py_DECREF(path_hooks); + Py_DECREF(path_importer_cache); + return importer; } #if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) From 682321292f0f342a88311495418ef6f84e4eb452 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 7 Oct 2023 06:23:54 -0700 Subject: [PATCH 432/632] [3.11] gh-109864: Make test_gettext tests order independent (GH-109866) (GH-110503) (cherry picked from commit 1aad4fc5dba993899621de86ae5955883448d6f6) Co-authored-by: Serhiy Storchaka --- Lib/test/test_gettext.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_gettext.py b/Lib/test/test_gettext.py index 8430fc234d00ee..2650ce501c309d 100644 --- a/Lib/test/test_gettext.py +++ b/Lib/test/test_gettext.py @@ -115,6 +115,12 @@ MMOFILE = os.path.join(LOCALEDIR, 'metadata.mo') +def reset_gettext(): + gettext._localedirs.clear() + gettext._current_domain = 'messages' + gettext._translations.clear() + + class GettextBaseTest(unittest.TestCase): def setUp(self): self.addCleanup(os_helper.rmtree, os.path.split(LOCALEDIR)[0]) @@ -132,7 +138,8 @@ def setUp(self): fp.write(base64.decodebytes(MMO_DATA)) self.env = self.enterContext(os_helper.EnvironmentVarGuard()) self.env['LANGUAGE'] = 'xx' - gettext._translations.clear() + reset_gettext() + self.addCleanup(reset_gettext) GNU_MO_DATA_ISSUE_17898 = b'''\ @@ -312,6 +319,10 @@ def test_multiline_strings(self): class PluralFormsTestCase(GettextBaseTest): def setUp(self): GettextBaseTest.setUp(self) + self.localedir = os.curdir + # Set up the bindings + gettext.bindtextdomain('gettext', self.localedir) + gettext.textdomain('gettext') self.mofile = MOFILE def test_plural_forms1(self): @@ -355,7 +366,7 @@ def test_plural_context_forms2(self): x = t.npgettext('With context', 'There is %s file', 'There are %s files', 2) eq(x, 'Hay %s ficheros (context)') - x = gettext.pgettext('With context', 'There is %s file') + x = t.pgettext('With context', 'There is %s file') eq(x, 'Hay %s fichero (context)') # Examples from http://www.gnu.org/software/gettext/manual/gettext.html From af168df78f260a809875b309f35fbf38b58c1fec Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 7 Oct 2023 06:24:27 -0700 Subject: [PATCH 433/632] [3.11] gh-109848: Make test_rot13_func in test_codecs independent (GH-109850) (GH-110505) (cherry picked from commit b987fdb19b981ef6e7f71b41790b5ed4e2064646) Co-authored-by: Serhiy Storchaka --- Lib/test/test_codecs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index 684e6cf7515481..a7440eea67c101 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -3590,9 +3590,10 @@ class Rot13UtilTest(unittest.TestCase): $ echo "Hello World" | python -m encodings.rot_13 """ def test_rot13_func(self): + from encodings.rot_13 import rot13 infile = io.StringIO('Gb or, be abg gb or, gung vf gur dhrfgvba') outfile = io.StringIO() - encodings.rot_13.rot13(infile, outfile) + rot13(infile, outfile) outfile.seek(0) plain_text = outfile.read() self.assertEqual( From f21c09ca03b39319ee6de54f5d706bd6e2313985 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 7 Oct 2023 17:29:46 -0700 Subject: [PATCH 434/632] [3.11] gh-110237: Check `PyList_Append` for errors in `_PyEval_MatchClass` (GH-110238) (#110512) gh-110237: Check `PyList_Append` for errors in `_PyEval_MatchClass` (GH-110238) (cherry picked from commit dd9d781da30aa3740e54c063a40413c542d78c25) Co-authored-by: denballakh <47365157+denballakh@users.noreply.github.com> --- .../2023-10-02-23-17-08.gh-issue-110237._Xub0z.rst | 1 + Python/ceval.c | 14 +++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-02-23-17-08.gh-issue-110237._Xub0z.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-02-23-17-08.gh-issue-110237._Xub0z.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-02-23-17-08.gh-issue-110237._Xub0z.rst new file mode 100644 index 00000000000000..67b95c52f7e4da --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-10-02-23-17-08.gh-issue-110237._Xub0z.rst @@ -0,0 +1 @@ +Fix missing error checks for calls to ``PyList_Append`` in ``_PyEval_MatchClass``. diff --git a/Python/ceval.c b/Python/ceval.c index df11de084d2d72..172bc4743bda90 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1060,7 +1060,9 @@ match_class(PyThreadState *tstate, PyObject *subject, PyObject *type, } if (match_self) { // Easy. Copy the subject itself, and move on to kwargs. - PyList_Append(attrs, subject); + if (PyList_Append(attrs, subject) < 0) { + goto fail; + } } else { for (Py_ssize_t i = 0; i < nargs; i++) { @@ -1076,7 +1078,10 @@ match_class(PyThreadState *tstate, PyObject *subject, PyObject *type, if (attr == NULL) { goto fail; } - PyList_Append(attrs, attr); + if (PyList_Append(attrs, attr) < 0) { + Py_DECREF(attr); + goto fail; + } Py_DECREF(attr); } } @@ -1089,7 +1094,10 @@ match_class(PyThreadState *tstate, PyObject *subject, PyObject *type, if (attr == NULL) { goto fail; } - PyList_Append(attrs, attr); + if (PyList_Append(attrs, attr) < 0) { + Py_DECREF(attr); + goto fail; + } Py_DECREF(attr); } Py_SETREF(attrs, PyList_AsTuple(attrs)); From 2bad6e715ad74da296c8081bd334ee20a68b77ae Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 8 Oct 2023 21:55:37 -0700 Subject: [PATCH 435/632] [3.11] gh-110534 fix a URL redirect to wikipedia article on Fibonacci numbers (GH-110535) (#110537) gh-110534 fix a URL redirect to wikipedia article on Fibonacci numbers (GH-110535) (cherry picked from commit 892ee72b3622de30acd12576b59259fc69e2e40a) Co-authored-by: partev --- Doc/tutorial/introduction.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/tutorial/introduction.rst b/Doc/tutorial/introduction.rst index 24ae720fe2bb62..b3b8e423a09fa1 100644 --- a/Doc/tutorial/introduction.rst +++ b/Doc/tutorial/introduction.rst @@ -480,7 +480,7 @@ First Steps Towards Programming Of course, we can use Python for more complicated tasks than adding two and two together. For instance, we can write an initial sub-sequence of the -`Fibonacci series `_ +`Fibonacci series `_ as follows:: >>> # Fibonacci series: From 6b63d40919f080998c158546c18b89877093de8b Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 9 Oct 2023 11:42:01 +0200 Subject: [PATCH 436/632] [3.11] gh-110497: Add note about `OSError` being an alias to `IOError` in docs (GH-110498) (#110545) gh-110497: Add note about `OSError` being an alias to `IOError` in docs (GH-110498) (cherry picked from commit 5e7edac7717bfe5f3c533d83ddd0f564db8de40b) Co-authored-by: Nikita Sobolev --- Doc/library/ctypes.rst | 8 +++++--- Doc/library/gettext.rst | 2 +- Doc/library/http.cookiejar.rst | 4 ++-- Doc/library/urllib.error.rst | 4 ++-- Doc/library/zipimport.rst | 2 +- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index e2dde683a1a5d3..51cffc029017b2 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -1374,7 +1374,8 @@ way is to instantiate one of the following classes: failure, an :class:`OSError` is automatically raised. .. versionchanged:: 3.3 - :exc:`WindowsError` used to be raised. + :exc:`WindowsError` used to be raised, + which is now an alias of :exc:`OSError`. .. class:: WinDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False, winmode=None) @@ -2047,13 +2048,14 @@ Utility functions .. function:: WinError(code=None, descr=None) Windows only: this function is probably the worst-named thing in ctypes. It - creates an instance of OSError. If *code* is not specified, + creates an instance of :exc:`OSError`. If *code* is not specified, ``GetLastError`` is called to determine the error code. If *descr* is not specified, :func:`FormatError` is called to get a textual description of the error. .. versionchanged:: 3.3 - An instance of :exc:`WindowsError` used to be created. + An instance of :exc:`WindowsError` used to be created, which is now an + alias of :exc:`OSError`. .. function:: wstring_at(address, size=-1) diff --git a/Doc/library/gettext.rst b/Doc/library/gettext.rst index 7ebe91b372d35a..dc6cf5533fccbe 100644 --- a/Doc/library/gettext.rst +++ b/Doc/library/gettext.rst @@ -167,7 +167,7 @@ install themselves in the built-in namespace as the function :func:`!_`. :class:`NullTranslations` instance if *fallback* is true. .. versionchanged:: 3.3 - :exc:`IOError` used to be raised instead of :exc:`OSError`. + :exc:`IOError` used to be raised, it is now an alias of :exc:`OSError`. .. versionchanged:: 3.11 *codeset* parameter is removed. diff --git a/Doc/library/http.cookiejar.rst b/Doc/library/http.cookiejar.rst index 87ef156a0bed57..12a6d768437ea5 100644 --- a/Doc/library/http.cookiejar.rst +++ b/Doc/library/http.cookiejar.rst @@ -44,8 +44,8 @@ The module defines the following exception: cookies from a file. :exc:`LoadError` is a subclass of :exc:`OSError`. .. versionchanged:: 3.3 - LoadError was made a subclass of :exc:`OSError` instead of - :exc:`IOError`. + :exc:`LoadError` used to be a subtype of :exc:`IOError`, which is now an + alias of :exc:`OSError`. The following classes are provided: diff --git a/Doc/library/urllib.error.rst b/Doc/library/urllib.error.rst index 8772e8600795eb..8af07c5ec0474c 100644 --- a/Doc/library/urllib.error.rst +++ b/Doc/library/urllib.error.rst @@ -27,8 +27,8 @@ The following exceptions are raised by :mod:`urllib.error` as appropriate: exception instance. .. versionchanged:: 3.3 - :exc:`URLError` has been made a subclass of :exc:`OSError` instead - of :exc:`IOError`. + :exc:`URLError` used to be a subtype of :exc:`IOError`, which is now an + alias of :exc:`OSError`. .. exception:: HTTPError diff --git a/Doc/library/zipimport.rst b/Doc/library/zipimport.rst index fe1adcae163c23..71647445ed37ef 100644 --- a/Doc/library/zipimport.rst +++ b/Doc/library/zipimport.rst @@ -130,7 +130,7 @@ zipimporter Objects file wasn't found. .. versionchanged:: 3.3 - :exc:`IOError` used to be raised instead of :exc:`OSError`. + :exc:`IOError` used to be raised, it is now an alias of :exc:`OSError`. .. method:: get_filename(fullname) From b473d48505a339225bbbfbf7199ec28c65c0f468 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 9 Oct 2023 12:31:27 +0200 Subject: [PATCH 437/632] [3.11] gh-109286: Update macOS installer to use SQLite 3.43.1 (GH-110482) (#110551) (cherry picked from commit 48419a50b44a195ad7de958f479a924e7c2d3e1b) Co-authored-by: jtranquilli <76231120+jtranquilli@users.noreply.github.com> --- Mac/BuildScript/build-installer.py | 6 +++--- .../macOS/2023-10-04-23-38-24.gh-issue-109286.1ZLMaq.rst | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/macOS/2023-10-04-23-38-24.gh-issue-109286.1ZLMaq.rst diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index 980160dd3077d7..553926852a7ca8 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -358,9 +358,9 @@ def library_recipes(): ), ), dict( - name="SQLite 3.42.0", - url="https://sqlite.org/2023/sqlite-autoconf-3420000.tar.gz", - checksum="0c5a92bc51cf07cae45b4a1e94653dea", + name="SQLite 3.43.1", + url="https://sqlite.org/2023/sqlite-autoconf-3430100.tar.gz", + checksum="77e61befe9c3298da0504f87772a24b0", extra_cflags=('-Os ' '-DSQLITE_ENABLE_FTS5 ' '-DSQLITE_ENABLE_FTS4 ' diff --git a/Misc/NEWS.d/next/macOS/2023-10-04-23-38-24.gh-issue-109286.1ZLMaq.rst b/Misc/NEWS.d/next/macOS/2023-10-04-23-38-24.gh-issue-109286.1ZLMaq.rst new file mode 100644 index 00000000000000..18ac9df73deb37 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2023-10-04-23-38-24.gh-issue-109286.1ZLMaq.rst @@ -0,0 +1 @@ +Update macOS installer to use SQLite 3.43.1. From 26c3e700b15b8f2845d29b2872a0a5854b349730 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 9 Oct 2023 13:42:57 +0200 Subject: [PATCH 438/632] [3.11] gh-110437: Allow overriding VCRuntimeDLL with a semicolon separated list of DLLs to bundle (GH-110470) gh-110437: Allow overriding VCRuntimeDLL with a semicolon separated list of DLLs to bundle (GH-110470) (cherry picked from commit 12cc6792d0ca1d0b72712d77c6efcb0aa0c7e7ba) Co-authored-by: Steve Dower --- .../Windows/2023-10-06-14-20-14.gh-issue-110437.xpYy9q.rst | 2 ++ PCbuild/pyproject.props | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Windows/2023-10-06-14-20-14.gh-issue-110437.xpYy9q.rst diff --git a/Misc/NEWS.d/next/Windows/2023-10-06-14-20-14.gh-issue-110437.xpYy9q.rst b/Misc/NEWS.d/next/Windows/2023-10-06-14-20-14.gh-issue-110437.xpYy9q.rst new file mode 100644 index 00000000000000..777b4942e183f5 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-10-06-14-20-14.gh-issue-110437.xpYy9q.rst @@ -0,0 +1,2 @@ +Allows overriding the source of VC redistributables so that releases can be +guaranteed to never downgrade between updates. diff --git a/PCbuild/pyproject.props b/PCbuild/pyproject.props index 199bf72b0d578c..a20967205d21eb 100644 --- a/PCbuild/pyproject.props +++ b/PCbuild/pyproject.props @@ -229,7 +229,10 @@ public override bool Execute() { - + + + + From e913c6f4d6134ad04ecd59c0408d2b13ff44e342 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 9 Oct 2023 16:10:34 +0200 Subject: [PATCH 439/632] [3.11] gh-110519: Improve deprecation warning in the gettext module (GH-110520) (GH-110564) Deprecation warning about non-integer numbers in gettext now always refers to the line in the user code where gettext function or method is used. Previously, it could refer to a line in gettext code. Also, increase test coverage for NullTranslations and domain-aware functions like dngettext(). (cherry picked from commit 326c6c4e07137b43c49b74bd5528619360080469) Co-authored-by: Serhiy Storchaka --- Lib/gettext.py | 14 +- Lib/test/test_gettext.py | 178 +++++++++++++----- ...-10-08-18-15-02.gh-issue-110519.RDGe8-.rst | 3 + 3 files changed, 144 insertions(+), 51 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-10-08-18-15-02.gh-issue-110519.RDGe8-.rst diff --git a/Lib/gettext.py b/Lib/gettext.py index b72b15f82d4355..e84765bfdf0649 100644 --- a/Lib/gettext.py +++ b/Lib/gettext.py @@ -46,6 +46,7 @@ # find this format documented anywhere. +import operator import os import re import sys @@ -166,14 +167,21 @@ def _parse(tokens, priority=-1): def _as_int(n): try: - i = round(n) + round(n) except TypeError: raise TypeError('Plural value must be an integer, got %s' % (n.__class__.__name__,)) from None + import warnings + frame = sys._getframe(1) + stacklevel = 2 + while frame.f_back is not None and frame.f_globals.get('__name__') == __name__: + stacklevel += 1 + frame = frame.f_back warnings.warn('Plural value must be an integer, got %s' % (n.__class__.__name__,), - DeprecationWarning, 4) + DeprecationWarning, + stacklevel) return n @@ -200,7 +208,7 @@ def c2py(plural): elif c == ')': depth -= 1 - ns = {'_as_int': _as_int} + ns = {'_as_int': _as_int, '__name__': __name__} exec('''if True: def func(n): if not isinstance(n, int): diff --git a/Lib/test/test_gettext.py b/Lib/test/test_gettext.py index 2650ce501c309d..dd33b9b88f6768 100644 --- a/Lib/test/test_gettext.py +++ b/Lib/test/test_gettext.py @@ -2,6 +2,7 @@ import base64 import gettext import unittest +from functools import partial from test import support from test.support import os_helper @@ -122,8 +123,9 @@ def reset_gettext(): class GettextBaseTest(unittest.TestCase): - def setUp(self): - self.addCleanup(os_helper.rmtree, os.path.split(LOCALEDIR)[0]) + @classmethod + def setUpClass(cls): + cls.addClassCleanup(os_helper.rmtree, os.path.split(LOCALEDIR)[0]) if not os.path.isdir(LOCALEDIR): os.makedirs(LOCALEDIR) with open(MOFILE, 'wb') as fp: @@ -136,6 +138,8 @@ def setUp(self): fp.write(base64.decodebytes(UMO_DATA)) with open(MMOFILE, 'wb') as fp: fp.write(base64.decodebytes(MMO_DATA)) + + def setUp(self): self.env = self.enterContext(os_helper.EnvironmentVarGuard()) self.env['LANGUAGE'] = 'xx' reset_gettext() @@ -316,59 +320,137 @@ def test_multiline_strings(self): trggrkg zrffntr pngnybt yvoenel.''') -class PluralFormsTestCase(GettextBaseTest): +class PluralFormsTests: + + def _test_plural_forms(self, ngettext, gettext, + singular, plural, tsingular, tplural, + numbers_only=True): + x = ngettext(singular, plural, 1) + self.assertEqual(x, tsingular) + x = ngettext(singular, plural, 2) + self.assertEqual(x, tplural) + x = gettext(singular) + self.assertEqual(x, tsingular) + + if numbers_only: + lineno = self._test_plural_forms.__code__.co_firstlineno + 9 + with self.assertWarns(DeprecationWarning) as cm: + x = ngettext(singular, plural, 1.0) + self.assertEqual(cm.filename, __file__) + self.assertEqual(cm.lineno, lineno + 4) + self.assertEqual(x, tsingular) + with self.assertWarns(DeprecationWarning) as cm: + x = ngettext(singular, plural, 1.1) + self.assertEqual(cm.filename, __file__) + self.assertEqual(cm.lineno, lineno + 9) + self.assertEqual(x, tplural) + with self.assertRaises(TypeError): + ngettext(singular, plural, None) + else: + x = ngettext(singular, plural, None) + self.assertEqual(x, tplural) + + def test_plural_forms(self): + self._test_plural_forms( + self.ngettext, self.gettext, + 'There is %s file', 'There are %s files', + 'Hay %s fichero', 'Hay %s ficheros') + self._test_plural_forms( + self.ngettext, self.gettext, + '%d file deleted', '%d files deleted', + '%d file deleted', '%d files deleted') + + def test_plural_context_forms(self): + ngettext = partial(self.npgettext, 'With context') + gettext = partial(self.pgettext, 'With context') + self._test_plural_forms( + ngettext, gettext, + 'There is %s file', 'There are %s files', + 'Hay %s fichero (context)', 'Hay %s ficheros (context)') + self._test_plural_forms( + ngettext, gettext, + '%d file deleted', '%d files deleted', + '%d file deleted', '%d files deleted') + + def test_plural_wrong_context_forms(self): + self._test_plural_forms( + partial(self.npgettext, 'Unknown context'), + partial(self.pgettext, 'Unknown context'), + 'There is %s file', 'There are %s files', + 'There is %s file', 'There are %s files') + + +class GNUTranslationsPluralFormsTestCase(PluralFormsTests, GettextBaseTest): def setUp(self): GettextBaseTest.setUp(self) - self.localedir = os.curdir # Set up the bindings - gettext.bindtextdomain('gettext', self.localedir) + gettext.bindtextdomain('gettext', os.curdir) gettext.textdomain('gettext') - self.mofile = MOFILE - def test_plural_forms1(self): - eq = self.assertEqual - x = gettext.ngettext('There is %s file', 'There are %s files', 1) - eq(x, 'Hay %s fichero') - x = gettext.ngettext('There is %s file', 'There are %s files', 2) - eq(x, 'Hay %s ficheros') - x = gettext.gettext('There is %s file') - eq(x, 'Hay %s fichero') - - def test_plural_context_forms1(self): - eq = self.assertEqual - x = gettext.npgettext('With context', - 'There is %s file', 'There are %s files', 1) - eq(x, 'Hay %s fichero (context)') - x = gettext.npgettext('With context', - 'There is %s file', 'There are %s files', 2) - eq(x, 'Hay %s ficheros (context)') - x = gettext.pgettext('With context', 'There is %s file') - eq(x, 'Hay %s fichero (context)') - - def test_plural_forms2(self): - eq = self.assertEqual - with open(self.mofile, 'rb') as fp: - t = gettext.GNUTranslations(fp) - x = t.ngettext('There is %s file', 'There are %s files', 1) - eq(x, 'Hay %s fichero') - x = t.ngettext('There is %s file', 'There are %s files', 2) - eq(x, 'Hay %s ficheros') - x = t.gettext('There is %s file') - eq(x, 'Hay %s fichero') - - def test_plural_context_forms2(self): - eq = self.assertEqual - with open(self.mofile, 'rb') as fp: + self.gettext = gettext.gettext + self.ngettext = gettext.ngettext + self.pgettext = gettext.pgettext + self.npgettext = gettext.npgettext + + +class GNUTranslationsWithDomainPluralFormsTestCase(PluralFormsTests, GettextBaseTest): + def setUp(self): + GettextBaseTest.setUp(self) + # Set up the bindings + gettext.bindtextdomain('gettext', os.curdir) + + self.gettext = partial(gettext.dgettext, 'gettext') + self.ngettext = partial(gettext.dngettext, 'gettext') + self.pgettext = partial(gettext.dpgettext, 'gettext') + self.npgettext = partial(gettext.dnpgettext, 'gettext') + + def test_plural_forms_wrong_domain(self): + self._test_plural_forms( + partial(gettext.dngettext, 'unknown'), + partial(gettext.dgettext, 'unknown'), + 'There is %s file', 'There are %s files', + 'There is %s file', 'There are %s files', + numbers_only=False) + + def test_plural_context_forms_wrong_domain(self): + self._test_plural_forms( + partial(gettext.dnpgettext, 'unknown', 'With context'), + partial(gettext.dpgettext, 'unknown', 'With context'), + 'There is %s file', 'There are %s files', + 'There is %s file', 'There are %s files', + numbers_only=False) + + +class GNUTranslationsClassPluralFormsTestCase(PluralFormsTests, GettextBaseTest): + def setUp(self): + GettextBaseTest.setUp(self) + with open(MOFILE, 'rb') as fp: t = gettext.GNUTranslations(fp) - x = t.npgettext('With context', - 'There is %s file', 'There are %s files', 1) - eq(x, 'Hay %s fichero (context)') - x = t.npgettext('With context', - 'There is %s file', 'There are %s files', 2) - eq(x, 'Hay %s ficheros (context)') - x = t.pgettext('With context', 'There is %s file') - eq(x, 'Hay %s fichero (context)') + self.gettext = t.gettext + self.ngettext = t.ngettext + self.pgettext = t.pgettext + self.npgettext = t.npgettext + + def test_plural_forms_null_translations(self): + t = gettext.NullTranslations() + self._test_plural_forms( + t.ngettext, t.gettext, + 'There is %s file', 'There are %s files', + 'There is %s file', 'There are %s files', + numbers_only=False) + + def test_plural_context_forms_null_translations(self): + t = gettext.NullTranslations() + self._test_plural_forms( + partial(t.npgettext, 'With context'), + partial(t.pgettext, 'With context'), + 'There is %s file', 'There are %s files', + 'There is %s file', 'There are %s files', + numbers_only=False) + + +class PluralFormsInternalTestCase: # Examples from http://www.gnu.org/software/gettext/manual/gettext.html def test_ja(self): diff --git a/Misc/NEWS.d/next/Library/2023-10-08-18-15-02.gh-issue-110519.RDGe8-.rst b/Misc/NEWS.d/next/Library/2023-10-08-18-15-02.gh-issue-110519.RDGe8-.rst new file mode 100644 index 00000000000000..8ff916736584dc --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-10-08-18-15-02.gh-issue-110519.RDGe8-.rst @@ -0,0 +1,3 @@ +Deprecation warning about non-integer number in :mod:`gettext` now alwais +refers to the line in the user code where gettext function or method is +used. Previously it could refer to a line in ``gettext`` code. From 9e9df93ffc6df5141843caf651d33d446676a414 Mon Sep 17 00:00:00 2001 From: Bo Anderson Date: Mon, 9 Oct 2023 20:42:25 +0100 Subject: [PATCH 440/632] [3.11] gh-109191: Fix build with newer editline (gh-110239) (#110575) (cherry picked from commit f4cb0d27cc08f490c42a22e646eb73cc7072d54a) --- ...23-10-05-11-46-20.gh-issue-109191.imUkVN.rst | 1 + Modules/readline.c | 2 +- configure | 17 +++++++++++++++++ configure.ac | 14 ++++++++++++++ pyconfig.h.in | 3 +++ 5 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Build/2023-10-05-11-46-20.gh-issue-109191.imUkVN.rst diff --git a/Misc/NEWS.d/next/Build/2023-10-05-11-46-20.gh-issue-109191.imUkVN.rst b/Misc/NEWS.d/next/Build/2023-10-05-11-46-20.gh-issue-109191.imUkVN.rst new file mode 100644 index 00000000000000..27e5df790bc0c6 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2023-10-05-11-46-20.gh-issue-109191.imUkVN.rst @@ -0,0 +1 @@ +Fix compile error when building with recent versions of libedit. diff --git a/Modules/readline.c b/Modules/readline.c index 27b89de7279464..8c7f526d418f82 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -440,7 +440,7 @@ readline_set_completion_display_matches_hook_impl(PyObject *module, default completion display. */ rl_completion_display_matches_hook = readlinestate_global->completion_display_matches_hook ? -#if defined(_RL_FUNCTION_TYPEDEF) +#if defined(HAVE_RL_COMPDISP_FUNC_T) (rl_compdisp_func_t *)on_completion_display_matches_hook : 0; #else (VFunction *)on_completion_display_matches_hook : 0; diff --git a/configure b/configure index af4a5bbfdfa1a4..b294f93a5564b4 100755 --- a/configure +++ b/configure @@ -21289,6 +21289,23 @@ if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : $as_echo "#define HAVE_RL_APPEND_HISTORY 1" >>confdefs.h +fi + + + # in readline as well as newer editline (April 2023) + ac_fn_c_check_type "$LINENO" "rl_compdisp_func_t" "ac_cv_type_rl_compdisp_func_t" " +#include /* Must be first for Gnu Readline */ +#ifdef WITH_EDITLINE +# include +#else +# include +#endif + +" +if test "x$ac_cv_type_rl_compdisp_func_t" = xyes; then : + +$as_echo "#define HAVE_RL_COMPDISP_FUNC_T 1" >>confdefs.h + fi fi diff --git a/configure.ac b/configure.ac index e1cbb7c7fbe9d9..629b7b76c3c315 100644 --- a/configure.ac +++ b/configure.ac @@ -5918,6 +5918,20 @@ if test "$py_cv_lib_readline" = yes; then AC_CHECK_LIB($LIBREADLINE, append_history, AC_DEFINE(HAVE_RL_APPEND_HISTORY, 1, [Define if readline supports append_history]),,$READLINE_LIBS) + + # in readline as well as newer editline (April 2023) + AC_CHECK_TYPE([rl_compdisp_func_t], + [AC_DEFINE([HAVE_RL_COMPDISP_FUNC_T], [1], + [Define if readline supports rl_compdisp_func_t])], + [], + [ +#include /* Must be first for Gnu Readline */ +#ifdef WITH_EDITLINE +# include +#else +# include +#endif + ]) fi # End of readline checks: restore LIBS diff --git a/pyconfig.h.in b/pyconfig.h.in index 0536047f573ce6..94d02e14c444ac 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -968,6 +968,9 @@ /* Define if you can turn off readline's signal handling. */ #undef HAVE_RL_CATCH_SIGNAL +/* Define if readline supports rl_compdisp_func_t */ +#undef HAVE_RL_COMPDISP_FUNC_T + /* Define if you have readline 2.2 */ #undef HAVE_RL_COMPLETION_APPEND_CHARACTER From 5ada51cd511d2e841ca37a1f5ea6b3245e5bf051 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 10 Oct 2023 03:13:34 +0200 Subject: [PATCH 441/632] [3.11] Remove unused `SPHINXLINT` var from `Doc/Makefile`. (GH-110570) (#110584) Remove unused `SPHINXLINT` var from `Doc/Makefile`. (GH-110570) Remove unused `SPHINXLINT` var. (cherry picked from commit bdbe43c7d0ad5ebda0232a4ab39689ea79a9733a) Co-authored-by: Ezio Melotti --- Doc/Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Doc/Makefile b/Doc/Makefile index 55be2b2dc5c1e6..ca82353eb454f3 100644 --- a/Doc/Makefile +++ b/Doc/Makefile @@ -7,7 +7,6 @@ PYTHON = python3 VENVDIR = ./venv SPHINXBUILD = PATH=$(VENVDIR)/bin:$$PATH sphinx-build -SPHINXLINT = PATH=$(VENVDIR)/bin:$$PATH sphinx-lint BLURB = PATH=$(VENVDIR)/bin:$$PATH blurb JOBS = auto PAPER = From 2943bae7f1faea26770ee9d23a1893cc22e144e7 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 10 Oct 2023 10:24:17 +0200 Subject: [PATCH 442/632] [3.11] Add some 'meta hooks' to our pre-commit config (GH-110587) (#110600) Add some 'meta hooks' to our pre-commit config (GH-110587) (cherry picked from commit d5ec77fafd352b4eb290b86d70e4d0b4673459eb) Co-authored-by: Alex Waygood --- .pre-commit-config.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3cff5658ba4e81..4c42dbb3c9750b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -23,3 +23,8 @@ repos: args: [--enable=default-role] files: ^Doc/|^Misc/NEWS.d/next/ types: [rst] + + - repo: meta + hooks: + - id: check-hooks-apply + - id: check-useless-excludes From d099defc63681a9134771f768059066116122599 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 10 Oct 2023 10:36:28 +0200 Subject: [PATCH 443/632] [3.11] gh-109408: Add the docs whitespace check from patchcheck to pre-commit (GH-109854) (#110595) gh-109408: Add the docs whitespace check from patchcheck to pre-commit (GH-109854) (cherry picked from commit 7426ed0347d66f7ef61ea7ae6c3163258b8fb128) Co-authored-by: Hugo van Kemenade Co-authored-by: Alex Waygood Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- Tools/scripts/patchcheck.py | 44 ++++++++++--------------------------- 2 files changed, 13 insertions(+), 33 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4c42dbb3c9750b..141d12b636a618 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,7 +14,7 @@ repos: exclude: ^Lib/test/test_tomllib/ - id: check-yaml - id: trailing-whitespace - types_or: [c, python, rst] + types_or: [c, inc, python, rst] - repo: https://github.com/sphinx-contrib/sphinx-lint rev: v0.6.8 diff --git a/Tools/scripts/patchcheck.py b/Tools/scripts/patchcheck.py index c2dceea2a2c634..c53dd45d8601d3 100755 --- a/Tools/scripts/patchcheck.py +++ b/Tools/scripts/patchcheck.py @@ -30,7 +30,8 @@ def get_python_source_dir(): def n_files_str(count): """Return 'N file(s)' with the proper plurality on 'file'.""" - return "{} file{}".format(count, "s" if count != 1 else "") + s = "s" if count != 1 else "" + return f"{count} file{s}" def status(message, modal=False, info=None): @@ -84,7 +85,7 @@ def get_git_remote_default_branch(remote_name): It is typically called 'main', but may differ """ - cmd = "git remote show {}".format(remote_name).split() + cmd = f"git remote show {remote_name}".split() env = os.environ.copy() env['LANG'] = 'C' try: @@ -170,9 +171,9 @@ def report_modified_files(file_paths): if count == 0: return n_files_str(count) else: - lines = ["{}:".format(n_files_str(count))] + lines = [f"{n_files_str(count)}:"] for path in file_paths: - lines.append(" {}".format(path)) + lines.append(f" {path}") return "\n".join(lines) @@ -199,27 +200,6 @@ def normalize_c_whitespace(file_paths): return fixed -ws_re = re.compile(br'\s+(\r?\n)$') - -@status("Fixing docs whitespace", info=report_modified_files) -def normalize_docs_whitespace(file_paths): - fixed = [] - for path in file_paths: - abspath = os.path.join(SRCDIR, path) - try: - with open(abspath, 'rb') as f: - lines = f.readlines() - new_lines = [ws_re.sub(br'\1', line) for line in lines] - if new_lines != lines: - shutil.copyfile(abspath, abspath + '.bak') - with open(abspath, 'wb') as f: - f.writelines(new_lines) - fixed.append(path) - except Exception as err: - print('Cannot fix %s: %s' % (path, err)) - return fixed - - @status("Docs modified", modal=True) def docs_modified(file_paths): """Report if any file in the Doc directory has been changed.""" @@ -238,6 +218,7 @@ def reported_news(file_paths): return any(p.startswith(os.path.join('Misc', 'NEWS.d', 'next')) for p in file_paths) + @status("configure regenerated", modal=True, info=str) def regenerated_configure(file_paths): """Check if configure has been regenerated.""" @@ -246,6 +227,7 @@ def regenerated_configure(file_paths): else: return "not needed" + @status("pyconfig.h.in regenerated", modal=True, info=str) def regenerated_pyconfig_h_in(file_paths): """Check if pyconfig.h.in has been regenerated.""" @@ -254,6 +236,7 @@ def regenerated_pyconfig_h_in(file_paths): else: return "not needed" + def ci(pull_request): if pull_request == 'false': print('Not a pull request; skipping') @@ -262,19 +245,18 @@ def ci(pull_request): file_paths = changed_files(base_branch) python_files = [fn for fn in file_paths if fn.endswith('.py')] c_files = [fn for fn in file_paths if fn.endswith(('.c', '.h'))] - doc_files = [fn for fn in file_paths if fn.startswith('Doc') and - fn.endswith(('.rst', '.inc'))] fixed = [] fixed.extend(normalize_whitespace(python_files)) fixed.extend(normalize_c_whitespace(c_files)) - fixed.extend(normalize_docs_whitespace(doc_files)) if not fixed: print('No whitespace issues found') else: - print(f'Please fix the {len(fixed)} file(s) with whitespace issues') - print('(on UNIX you can run `make patchcheck` to make the fixes)') + count = len(fixed) + print(f'Please fix the {n_files_str(count)} with whitespace issues') + print('(on Unix you can run `make patchcheck` to make the fixes)') sys.exit(1) + def main(): base_branch = get_base_branch() file_paths = changed_files(base_branch) @@ -287,8 +269,6 @@ def main(): normalize_whitespace(python_files) # C rules enforcement. normalize_c_whitespace(c_files) - # Doc whitespace enforcement. - normalize_docs_whitespace(doc_files) # Docs updated. docs_modified(doc_files) # Misc/ACKS changed. From 2b3a4182798bc95a426e55d1f9f7199e2f2b5662 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 10 Oct 2023 11:12:52 +0200 Subject: [PATCH 444/632] [3.11] gh-110378: Close invalid generators in contextmanager and asynccontextmanager (GH-110499) (#110589) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit contextmanager and asynccontextmanager context managers now close an invalid underlying generator object that yields more then one value. (cherry picked from commit 96fed66a65097eac2dc528ce29c9ba676bb07689) Co-authored-by: Serhiy Storchaka Co-authored-by: Łukasz Langa --- Lib/contextlib.py | 20 ++++++++++++++---- Lib/test/test_contextlib.py | 21 ++++++++++++++++--- ...-10-07-13-50-12.gh-issue-110378.Y4L8fl.rst | 3 +++ 3 files changed, 37 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-10-07-13-50-12.gh-issue-110378.Y4L8fl.rst diff --git a/Lib/contextlib.py b/Lib/contextlib.py index 58e9a498878d01..4a338f5c637db5 100644 --- a/Lib/contextlib.py +++ b/Lib/contextlib.py @@ -145,7 +145,10 @@ def __exit__(self, typ, value, traceback): except StopIteration: return False else: - raise RuntimeError("generator didn't stop") + try: + raise RuntimeError("generator didn't stop") + finally: + self.gen.close() else: if value is None: # Need to force instantiation so we can reliably @@ -187,7 +190,10 @@ def __exit__(self, typ, value, traceback): raise exc.__traceback__ = traceback return False - raise RuntimeError("generator didn't stop after throw()") + try: + raise RuntimeError("generator didn't stop after throw()") + finally: + self.gen.close() class _AsyncGeneratorContextManager( _GeneratorContextManagerBase, @@ -212,7 +218,10 @@ async def __aexit__(self, typ, value, traceback): except StopAsyncIteration: return False else: - raise RuntimeError("generator didn't stop") + try: + raise RuntimeError("generator didn't stop") + finally: + await self.gen.aclose() else: if value is None: # Need to force instantiation so we can reliably @@ -254,7 +263,10 @@ async def __aexit__(self, typ, value, traceback): raise exc.__traceback__ = traceback return False - raise RuntimeError("generator didn't stop after athrow()") + try: + raise RuntimeError("generator didn't stop after athrow()") + finally: + await self.gen.aclose() def contextmanager(func): diff --git a/Lib/test/test_contextlib.py b/Lib/test/test_contextlib.py index ec06785b5667a6..093f2593a4a464 100644 --- a/Lib/test/test_contextlib.py +++ b/Lib/test/test_contextlib.py @@ -156,9 +156,24 @@ def whoo(): yield ctx = whoo() ctx.__enter__() - self.assertRaises( - RuntimeError, ctx.__exit__, TypeError, TypeError("foo"), None - ) + with self.assertRaises(RuntimeError): + ctx.__exit__(TypeError, TypeError("foo"), None) + if support.check_impl_detail(cpython=True): + # The "gen" attribute is an implementation detail. + self.assertFalse(ctx.gen.gi_suspended) + + def test_contextmanager_trap_second_yield(self): + @contextmanager + def whoo(): + yield + yield + ctx = whoo() + ctx.__enter__() + with self.assertRaises(RuntimeError): + ctx.__exit__(None, None, None) + if support.check_impl_detail(cpython=True): + # The "gen" attribute is an implementation detail. + self.assertFalse(ctx.gen.gi_suspended) def test_contextmanager_except(self): state = [] diff --git a/Misc/NEWS.d/next/Library/2023-10-07-13-50-12.gh-issue-110378.Y4L8fl.rst b/Misc/NEWS.d/next/Library/2023-10-07-13-50-12.gh-issue-110378.Y4L8fl.rst new file mode 100644 index 00000000000000..ef5395fc3c6420 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-10-07-13-50-12.gh-issue-110378.Y4L8fl.rst @@ -0,0 +1,3 @@ +:func:`~contextlib.contextmanager` and +:func:`~contextlib.asynccontextmanager` context managers now close an invalid +underlying generator object that yields more then one value. From 194272179dc2cbe67358fc0ebde73dea90bc5a1b Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 10 Oct 2023 12:06:18 +0200 Subject: [PATCH 445/632] [3.11] gh-78469: Declare missing sethostname for Solaris 10 (GH-109447) (#110581) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add OS version specific macro for Solaris: Py_SUNOS_VERSION. (cherry picked from commit 3b1580af07c0ce90d1c2073ab087772283d7e3b9) Co-authored-by: Jakub Kulík --- Modules/socketmodule.c | 5 +++-- configure | 11 +++++++++++ configure.ac | 8 ++++++++ pyconfig.h.in | 3 +++ 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index db3e0519c63f3d..8d9d3c3e1cd3e3 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -5534,8 +5534,9 @@ socket_sethostname(PyObject *self, PyObject *args) Py_buffer buf; int res, flag = 0; -#ifdef _AIX -/* issue #18259, not declared in any useful header file */ +#if defined(_AIX) || (defined(__sun) && defined(__SVR4) && Py_SUNOS_VERSION <= 510) +/* issue #18259, sethostname is not declared in any useful header file on AIX + * the same is true for Solaris 10 */ extern int sethostname(const char *, size_t); #endif diff --git a/configure b/configure index b294f93a5564b4..8d2f3f4cc0fa5f 100755 --- a/configure +++ b/configure @@ -3860,6 +3860,17 @@ then darwin*) MACHDEP="darwin";; '') MACHDEP="unknown";; esac + + if test "$ac_sys_system" = "SunOS"; then + # For Solaris, there isn't an OS version specific macro defined + # in most compilers, so we define one here. + SUNOS_VERSION=`echo $ac_sys_release | sed -e 's!\.\(0-9\)$!.0\1!g' | tr -d '.'` + +cat >>confdefs.h <<_ACEOF +#define Py_SUNOS_VERSION $SUNOS_VERSION +_ACEOF + + fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: \"$MACHDEP\"" >&5 $as_echo "\"$MACHDEP\"" >&6; } diff --git a/configure.ac b/configure.ac index 629b7b76c3c315..52d5c1f7dd3593 100644 --- a/configure.ac +++ b/configure.ac @@ -582,6 +582,14 @@ then darwin*) MACHDEP="darwin";; '') MACHDEP="unknown";; esac + + if test "$ac_sys_system" = "SunOS"; then + # For Solaris, there isn't an OS version specific macro defined + # in most compilers, so we define one here. + SUNOS_VERSION=`echo $ac_sys_release | sed -e 's!\.\([0-9]\)$!.0\1!g' | tr -d '.'` + AC_DEFINE_UNQUOTED([Py_SUNOS_VERSION], [$SUNOS_VERSION], + [The version of SunOS/Solaris as reported by `uname -r' without the dot.]) + fi fi AC_MSG_RESULT("$MACHDEP") diff --git a/pyconfig.h.in b/pyconfig.h.in index 94d02e14c444ac..a8c35bba448ea8 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -1595,6 +1595,9 @@ /* Define if you want to enable internal statistics gathering. */ #undef Py_STATS +/* The version of SunOS/Solaris as reported by `uname -r' without the dot. */ +#undef Py_SUNOS_VERSION + /* Define if you want to enable tracing references for debugging purpose */ #undef Py_TRACE_REFS From 7fefed091ad2be083d353446c0631d2739cb0ead Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 10 Oct 2023 12:48:07 +0200 Subject: [PATCH 446/632] [3.11] gh-110590: Fix a bug where _sre.compile would overwrite exceptions (GH-110591) (#110614) TypeError would be overwritten by OverflowError if 'code' param contained non-ints. (cherry picked from commit 344d3a222a7864f8157773749bdd77d1c9dfc1e6) Co-authored-by: Nikita Sobolev --- Lib/test/test_re.py | 3 +++ .../Library/2023-10-10-10-46-55.gh-issue-110590.fatz-h.rst | 3 +++ Modules/_sre/sre.c | 3 +++ 3 files changed, 9 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2023-10-10-10-46-55.gh-issue-110590.fatz-h.rst diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index f4d64dc9fcf2ae..b41bcd49385453 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -2725,6 +2725,9 @@ def test_dealloc(self): _sre.compile("abc", 0, [long_overflow], 0, {}, ()) with self.assertRaises(TypeError): _sre.compile({}, 0, [], 0, [], []) + # gh-110590: `TypeError` was overwritten with `OverflowError`: + with self.assertRaises(TypeError): + _sre.compile('', 0, ['abc'], 0, {}, ()) @cpython_only def test_repeat_minmax_overflow_maxrepeat(self): diff --git a/Misc/NEWS.d/next/Library/2023-10-10-10-46-55.gh-issue-110590.fatz-h.rst b/Misc/NEWS.d/next/Library/2023-10-10-10-46-55.gh-issue-110590.fatz-h.rst new file mode 100644 index 00000000000000..20dc3fff205994 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-10-10-10-46-55.gh-issue-110590.fatz-h.rst @@ -0,0 +1,3 @@ +Fix a bug in :meth:`!_sre.compile` where :exc:`TypeError` +would be overwritten by :exc:`OverflowError` when +the *code* argument was a list of non-ints. diff --git a/Modules/_sre/sre.c b/Modules/_sre/sre.c index 448e761c988ca8..81629f0007e6a1 100644 --- a/Modules/_sre/sre.c +++ b/Modules/_sre/sre.c @@ -1437,6 +1437,9 @@ _sre_compile_impl(PyObject *module, PyObject *pattern, int flags, for (i = 0; i < n; i++) { PyObject *o = PyList_GET_ITEM(code, i); unsigned long value = PyLong_AsUnsignedLong(o); + if (value == (unsigned long)-1 && PyErr_Occurred()) { + break; + } self->code[i] = (SRE_CODE) value; if ((unsigned long) self->code[i] != value) { PyErr_SetString(PyExc_OverflowError, From bf1753bb4c75f2dc8f82ee3409dce024ccc1c4de Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 10 Oct 2023 13:12:40 +0200 Subject: [PATCH 447/632] [3.11] gh-110378: Fix test_async_gen_propagates_generator_exit in test_contextlib_async (GH-110500) (#110611) It now fails if the original bug is not fixed, and no longer produce ResourceWarning with fixed code. (cherry picked from commit 5aa62a8de15212577a13966710b3aede46e93824) Co-authored-by: Serhiy Storchaka --- Lib/test/test_contextlib_async.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/Lib/test/test_contextlib_async.py b/Lib/test/test_contextlib_async.py index 3d43ed0fcab168..438bb2dcfcc5eb 100644 --- a/Lib/test/test_contextlib_async.py +++ b/Lib/test/test_contextlib_async.py @@ -49,15 +49,11 @@ async def gen(): async with ctx(): yield 11 - ret = [] - exc = ValueError(22) - with self.assertRaises(ValueError): - async with ctx(): - async for val in gen(): - ret.append(val) - raise exc - - self.assertEqual(ret, [11]) + g = gen() + async for val in g: + self.assertEqual(val, 11) + break + await g.aclose() def test_exit_is_abstract(self): class MissingAexit(AbstractAsyncContextManager): From de62c2c1b3c2b75c36425c32b5536a17a3595a9b Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 10 Oct 2023 14:10:33 +0200 Subject: [PATCH 448/632] [3.11] gh-101100: Fix sphinx warnings in `library/socketserver.rst` (GH-110207) (GH-110624) (cherry picked from commit 756062b296df6242ba324e4cdc8f3e38bfc83617) Co-authored-by: Nikita Sobolev --- Doc/library/socketserver.rst | 77 ++++++++++++++++++++++++------------ Doc/tools/.nitignore | 1 - 2 files changed, 52 insertions(+), 26 deletions(-) diff --git a/Doc/library/socketserver.rst b/Doc/library/socketserver.rst index a409c99b1f6a45..7540375b8d1f50 100644 --- a/Doc/library/socketserver.rst +++ b/Doc/library/socketserver.rst @@ -116,23 +116,28 @@ server is the address family. :class:`ForkingMixIn` and the Forking classes mentioned below are only available on POSIX platforms that support :func:`~os.fork`. - :meth:`socketserver.ForkingMixIn.server_close` waits until all child - processes complete, except if - :attr:`socketserver.ForkingMixIn.block_on_close` attribute is false. + .. attribute:: block_on_close - :meth:`socketserver.ThreadingMixIn.server_close` waits until all non-daemon - threads complete, except if - :attr:`socketserver.ThreadingMixIn.block_on_close` attribute is false. Use - daemonic threads by setting - :data:`ThreadingMixIn.daemon_threads` to ``True`` to not wait until threads - complete. + :meth:`ForkingMixIn.server_close ` + waits until all child processes complete, except if + :attr:`block_on_close` attribute is ``False``. + + :meth:`ThreadingMixIn.server_close ` + waits until all non-daemon threads complete, except if + :attr:`block_on_close` attribute is ``False``. + + .. attribute:: daemon_threads + + For :class:`ThreadingMixIn` use daemonic threads by setting + :data:`ThreadingMixIn.daemon_threads ` + to ``True`` to not wait until threads complete. .. versionchanged:: 3.7 - :meth:`socketserver.ForkingMixIn.server_close` and - :meth:`socketserver.ThreadingMixIn.server_close` now waits until all + :meth:`ForkingMixIn.server_close ` and + :meth:`ThreadingMixIn.server_close ` now waits until all child processes and non-daemonic threads complete. - Add a new :attr:`socketserver.ForkingMixIn.block_on_close` class + Add a new :attr:`ForkingMixIn.block_on_close ` class attribute to opt-in for the pre-3.7 behaviour. @@ -406,13 +411,13 @@ Request Handler Objects This function must do all the work required to service a request. The default implementation does nothing. Several instance attributes are - available to it; the request is available as :attr:`self.request`; the client - address as :attr:`self.client_address`; and the server instance as - :attr:`self.server`, in case it needs access to per-server information. + available to it; the request is available as :attr:`request`; the client + address as :attr:`client_address`; and the server instance as + :attr:`server`, in case it needs access to per-server information. - The type of :attr:`self.request` is different for datagram or stream - services. For stream services, :attr:`self.request` is a socket object; for - datagram services, :attr:`self.request` is a pair of string and socket. + The type of :attr:`request` is different for datagram or stream + services. For stream services, :attr:`request` is a socket object; for + datagram services, :attr:`request` is a pair of string and socket. .. method:: finish() @@ -422,20 +427,42 @@ Request Handler Objects raises an exception, this function will not be called. + .. attribute:: request + + The *new* :class:`socket.socket` object + to be used to communicate with the client. + + + .. attribute:: client_address + + Client address returned by :meth:`BaseServer.get_request`. + + + .. attribute:: server + + :class:`BaseServer` object used for handling the request. + + .. class:: StreamRequestHandler DatagramRequestHandler These :class:`BaseRequestHandler` subclasses override the :meth:`~BaseRequestHandler.setup` and :meth:`~BaseRequestHandler.finish` - methods, and provide :attr:`self.rfile` and :attr:`self.wfile` attributes. - The :attr:`self.rfile` and :attr:`self.wfile` attributes can be - read or written, respectively, to get the request data or return data - to the client. - The :attr:`!rfile` attributes support the :class:`io.BufferedIOBase` readable interface, - and :attr:`!wfile` attributes support the :class:`!io.BufferedIOBase` writable interface. + methods, and provide :attr:`rfile` and :attr:`wfile` attributes. + + .. attribute:: rfile + + A file object from which receives the request is read. + Support the :class:`io.BufferedIOBase` readable interface. + + .. attribute:: wfile + + A file object to which the reply is written. + Support the :class:`io.BufferedIOBase` writable interface + .. versionchanged:: 3.6 - :attr:`StreamRequestHandler.wfile` also supports the + :attr:`wfile` also supports the :class:`io.BufferedIOBase` writable interface. diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 8e1c28a4c7b424..34975d5312f6e7 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -112,7 +112,6 @@ Doc/library/shelve.rst Doc/library/signal.rst Doc/library/smtplib.rst Doc/library/socket.rst -Doc/library/socketserver.rst Doc/library/ssl.rst Doc/library/stdtypes.rst Doc/library/string.rst From e511f544ea444a5653f322d01507563d9cf66268 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 10 Oct 2023 14:11:06 +0200 Subject: [PATCH 449/632] [3.11] gh-81002: Add tests for termios (GH-110386) (GH-110620) (cherry picked from commit 92a9e980245156bf75ede0869f8ba9512e04d2eb) Co-authored-by: Serhiy Storchaka --- Lib/test/test_termios.py | 220 ++++++++++++++++++ ...3-10-05-13-46-50.gh-issue-81002.bOcuV6.rst | 1 + 2 files changed, 221 insertions(+) create mode 100644 Lib/test/test_termios.py create mode 100644 Misc/NEWS.d/next/Tests/2023-10-05-13-46-50.gh-issue-81002.bOcuV6.rst diff --git a/Lib/test/test_termios.py b/Lib/test/test_termios.py new file mode 100644 index 00000000000000..58698ffac2d981 --- /dev/null +++ b/Lib/test/test_termios.py @@ -0,0 +1,220 @@ +import errno +import os +import sys +import tempfile +import unittest +from test.support.import_helper import import_module + +termios = import_module('termios') + + +@unittest.skipUnless(hasattr(os, 'openpty'), "need os.openpty()") +class TestFunctions(unittest.TestCase): + + def setUp(self): + master_fd, self.fd = os.openpty() + self.addCleanup(os.close, master_fd) + self.stream = self.enterContext(open(self.fd, 'wb', buffering=0)) + tmp = self.enterContext(tempfile.TemporaryFile(mode='wb', buffering=0)) + self.bad_fd = tmp.fileno() + + def assertRaisesTermiosError(self, errno, callable, *args): + with self.assertRaises(termios.error) as cm: + callable(*args) + self.assertEqual(cm.exception.args[0], errno) + + def test_tcgetattr(self): + attrs = termios.tcgetattr(self.fd) + self.assertIsInstance(attrs, list) + self.assertEqual(len(attrs), 7) + for i in range(6): + self.assertIsInstance(attrs[i], int) + iflag, oflag, cflag, lflag, ispeed, ospeed, cc = attrs + self.assertIsInstance(cc, list) + self.assertEqual(len(cc), termios.NCCS) + for i, x in enumerate(cc): + if ((lflag & termios.ICANON) == 0 and + (i == termios.VMIN or i == termios.VTIME)): + self.assertIsInstance(x, int) + else: + self.assertIsInstance(x, bytes) + self.assertEqual(len(x), 1) + self.assertEqual(termios.tcgetattr(self.stream), attrs) + + def test_tcgetattr_errors(self): + self.assertRaisesTermiosError(errno.ENOTTY, termios.tcgetattr, self.bad_fd) + self.assertRaises(ValueError, termios.tcgetattr, -1) + self.assertRaises(OverflowError, termios.tcgetattr, 2**1000) + self.assertRaises(TypeError, termios.tcgetattr, object()) + self.assertRaises(TypeError, termios.tcgetattr) + + def test_tcsetattr(self): + attrs = termios.tcgetattr(self.fd) + termios.tcsetattr(self.fd, termios.TCSANOW, attrs) + termios.tcsetattr(self.fd, termios.TCSADRAIN, attrs) + termios.tcsetattr(self.fd, termios.TCSAFLUSH, attrs) + termios.tcsetattr(self.stream, termios.TCSANOW, attrs) + + def test_tcsetattr_errors(self): + attrs = termios.tcgetattr(self.fd) + self.assertRaises(TypeError, termios.tcsetattr, self.fd, termios.TCSANOW, tuple(attrs)) + self.assertRaises(TypeError, termios.tcsetattr, self.fd, termios.TCSANOW, attrs[:-1]) + self.assertRaises(TypeError, termios.tcsetattr, self.fd, termios.TCSANOW, attrs + [0]) + for i in range(6): + attrs2 = attrs[:] + attrs2[i] = 2**1000 + self.assertRaises(OverflowError, termios.tcsetattr, self.fd, termios.TCSANOW, attrs2) + attrs2[i] = object() + self.assertRaises(TypeError, termios.tcsetattr, self.fd, termios.TCSANOW, attrs2) + self.assertRaises(TypeError, termios.tcsetattr, self.fd, termios.TCSANOW, attrs[:-1] + [attrs[-1][:-1]]) + self.assertRaises(TypeError, termios.tcsetattr, self.fd, termios.TCSANOW, attrs[:-1] + [attrs[-1] + [b'\0']]) + for i in range(len(attrs[-1])): + attrs2 = attrs[:] + attrs2[-1] = attrs2[-1][:] + attrs2[-1][i] = 2**1000 + self.assertRaises(OverflowError, termios.tcsetattr, self.fd, termios.TCSANOW, attrs2) + attrs2[-1][i] = object() + self.assertRaises(TypeError, termios.tcsetattr, self.fd, termios.TCSANOW, attrs2) + attrs2[-1][i] = b'' + self.assertRaises(TypeError, termios.tcsetattr, self.fd, termios.TCSANOW, attrs2) + attrs2[-1][i] = b'\0\0' + self.assertRaises(TypeError, termios.tcsetattr, self.fd, termios.TCSANOW, attrs2) + self.assertRaises(TypeError, termios.tcsetattr, self.fd, termios.TCSANOW, object()) + self.assertRaises(TypeError, termios.tcsetattr, self.fd, termios.TCSANOW) + self.assertRaisesTermiosError(errno.EINVAL, termios.tcsetattr, self.fd, -1, attrs) + self.assertRaises(OverflowError, termios.tcsetattr, self.fd, 2**1000, attrs) + self.assertRaises(TypeError, termios.tcsetattr, self.fd, object(), attrs) + self.assertRaisesTermiosError(errno.ENOTTY, termios.tcsetattr, self.bad_fd, termios.TCSANOW, attrs) + self.assertRaises(ValueError, termios.tcsetattr, -1, termios.TCSANOW, attrs) + self.assertRaises(OverflowError, termios.tcsetattr, 2**1000, termios.TCSANOW, attrs) + self.assertRaises(TypeError, termios.tcsetattr, object(), termios.TCSANOW, attrs) + self.assertRaises(TypeError, termios.tcsetattr, self.fd, termios.TCSANOW) + + def test_tcsendbreak(self): + try: + termios.tcsendbreak(self.fd, 1) + except termios.error as exc: + if exc.args[0] == errno.ENOTTY and sys.platform.startswith('freebsd'): + self.skipTest('termios.tcsendbreak() is not supported ' + 'with pseudo-terminals (?) on this platform') + raise + termios.tcsendbreak(self.stream, 1) + + def test_tcsendbreak_errors(self): + self.assertRaises(OverflowError, termios.tcsendbreak, self.fd, 2**1000) + self.assertRaises(TypeError, termios.tcsendbreak, self.fd, 0.0) + self.assertRaises(TypeError, termios.tcsendbreak, self.fd, object()) + self.assertRaisesTermiosError(errno.ENOTTY, termios.tcsendbreak, self.bad_fd, 0) + self.assertRaises(ValueError, termios.tcsendbreak, -1, 0) + self.assertRaises(OverflowError, termios.tcsendbreak, 2**1000, 0) + self.assertRaises(TypeError, termios.tcsendbreak, object(), 0) + self.assertRaises(TypeError, termios.tcsendbreak, self.fd) + + def test_tcdrain(self): + termios.tcdrain(self.fd) + termios.tcdrain(self.stream) + + def test_tcdrain_errors(self): + self.assertRaisesTermiosError(errno.ENOTTY, termios.tcdrain, self.bad_fd) + self.assertRaises(ValueError, termios.tcdrain, -1) + self.assertRaises(OverflowError, termios.tcdrain, 2**1000) + self.assertRaises(TypeError, termios.tcdrain, object()) + self.assertRaises(TypeError, termios.tcdrain) + + def test_tcflush(self): + termios.tcflush(self.fd, termios.TCIFLUSH) + termios.tcflush(self.fd, termios.TCOFLUSH) + termios.tcflush(self.fd, termios.TCIOFLUSH) + + def test_tcflush_errors(self): + self.assertRaisesTermiosError(errno.EINVAL, termios.tcflush, self.fd, -1) + self.assertRaises(OverflowError, termios.tcflush, self.fd, 2**1000) + self.assertRaises(TypeError, termios.tcflush, self.fd, object()) + self.assertRaisesTermiosError(errno.ENOTTY, termios.tcflush, self.bad_fd, termios.TCIFLUSH) + self.assertRaises(ValueError, termios.tcflush, -1, termios.TCIFLUSH) + self.assertRaises(OverflowError, termios.tcflush, 2**1000, termios.TCIFLUSH) + self.assertRaises(TypeError, termios.tcflush, object(), termios.TCIFLUSH) + self.assertRaises(TypeError, termios.tcflush, self.fd) + + def test_tcflow(self): + termios.tcflow(self.fd, termios.TCOOFF) + termios.tcflow(self.fd, termios.TCOON) + termios.tcflow(self.fd, termios.TCIOFF) + termios.tcflow(self.fd, termios.TCION) + + def test_tcflow_errors(self): + self.assertRaisesTermiosError(errno.EINVAL, termios.tcflow, self.fd, -1) + self.assertRaises(OverflowError, termios.tcflow, self.fd, 2**1000) + self.assertRaises(TypeError, termios.tcflow, self.fd, object()) + self.assertRaisesTermiosError(errno.ENOTTY, termios.tcflow, self.bad_fd, termios.TCOON) + self.assertRaises(ValueError, termios.tcflow, -1, termios.TCOON) + self.assertRaises(OverflowError, termios.tcflow, 2**1000, termios.TCOON) + self.assertRaises(TypeError, termios.tcflow, object(), termios.TCOON) + self.assertRaises(TypeError, termios.tcflow, self.fd) + + def test_tcgetwinsize(self): + size = termios.tcgetwinsize(self.fd) + self.assertIsInstance(size, tuple) + self.assertEqual(len(size), 2) + self.assertIsInstance(size[0], int) + self.assertIsInstance(size[1], int) + self.assertEqual(termios.tcgetwinsize(self.stream), size) + + def test_tcgetwinsize_errors(self): + self.assertRaisesTermiosError(errno.ENOTTY, termios.tcgetwinsize, self.bad_fd) + self.assertRaises(ValueError, termios.tcgetwinsize, -1) + self.assertRaises(OverflowError, termios.tcgetwinsize, 2**1000) + self.assertRaises(TypeError, termios.tcgetwinsize, object()) + self.assertRaises(TypeError, termios.tcgetwinsize) + + def test_tcsetwinsize(self): + size = termios.tcgetwinsize(self.fd) + termios.tcsetwinsize(self.fd, size) + termios.tcsetwinsize(self.fd, list(size)) + termios.tcsetwinsize(self.stream, size) + + def test_tcsetwinsize_errors(self): + size = termios.tcgetwinsize(self.fd) + self.assertRaises(TypeError, termios.tcsetwinsize, self.fd, size[:-1]) + self.assertRaises(TypeError, termios.tcsetwinsize, self.fd, size + (0,)) + self.assertRaises(TypeError, termios.tcsetwinsize, self.fd, object()) + self.assertRaises(OverflowError, termios.tcsetwinsize, self.fd, (size[0], 2**1000)) + self.assertRaises(TypeError, termios.tcsetwinsize, self.fd, (size[0], float(size[1]))) + self.assertRaises(TypeError, termios.tcsetwinsize, self.fd, (size[0], object())) + self.assertRaises(OverflowError, termios.tcsetwinsize, self.fd, (2**1000, size[1])) + self.assertRaises(TypeError, termios.tcsetwinsize, self.fd, (float(size[0]), size[1])) + self.assertRaises(TypeError, termios.tcsetwinsize, self.fd, (object(), size[1])) + self.assertRaisesTermiosError(errno.ENOTTY, termios.tcsetwinsize, self.bad_fd, size) + self.assertRaises(ValueError, termios.tcsetwinsize, -1, size) + self.assertRaises(OverflowError, termios.tcsetwinsize, 2**1000, size) + self.assertRaises(TypeError, termios.tcsetwinsize, object(), size) + self.assertRaises(TypeError, termios.tcsetwinsize, self.fd) + + +class TestModule(unittest.TestCase): + def test_constants(self): + self.assertIsInstance(termios.B0, int) + self.assertIsInstance(termios.B38400, int) + self.assertIsInstance(termios.TCSANOW, int) + self.assertIsInstance(termios.TCSADRAIN, int) + self.assertIsInstance(termios.TCSAFLUSH, int) + self.assertIsInstance(termios.TCIFLUSH, int) + self.assertIsInstance(termios.TCOFLUSH, int) + self.assertIsInstance(termios.TCIOFLUSH, int) + self.assertIsInstance(termios.TCOOFF, int) + self.assertIsInstance(termios.TCOON, int) + self.assertIsInstance(termios.TCIOFF, int) + self.assertIsInstance(termios.TCION, int) + self.assertIsInstance(termios.VTIME, int) + self.assertIsInstance(termios.VMIN, int) + self.assertIsInstance(termios.NCCS, int) + self.assertLess(termios.VTIME, termios.NCCS) + self.assertLess(termios.VMIN, termios.NCCS) + + def test_exception(self): + self.assertTrue(issubclass(termios.error, Exception)) + self.assertFalse(issubclass(termios.error, OSError)) + + +if __name__ == '__main__': + unittest.main() diff --git a/Misc/NEWS.d/next/Tests/2023-10-05-13-46-50.gh-issue-81002.bOcuV6.rst b/Misc/NEWS.d/next/Tests/2023-10-05-13-46-50.gh-issue-81002.bOcuV6.rst new file mode 100644 index 00000000000000..d69f6746d9e9aa --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-10-05-13-46-50.gh-issue-81002.bOcuV6.rst @@ -0,0 +1 @@ +Add tests for :mod:`termios`. From f72b18d146739e4a358499e621ef41f50fa2680a Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 10 Oct 2023 15:00:24 +0200 Subject: [PATCH 450/632] [3.11] Don't doubly-parallelise sphinx-lint (GH-110617) (#110627) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- .pre-commit-config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 141d12b636a618..1ae3dd71354e63 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -23,6 +23,7 @@ repos: args: [--enable=default-role] files: ^Doc/|^Misc/NEWS.d/next/ types: [rst] + require_serial: true - repo: meta hooks: From 3788c48d1227a9a6176e461c1bf6975c60afe5cf Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 10 Oct 2023 17:27:22 +0300 Subject: [PATCH 451/632] [3.11] gh-110388: Add tests for tty (GH-110394) (GH-110634) (cherry picked from commit 7f702b2) --- Lib/test/test_tty.py | 60 +++++++++++++++++++ ...-10-05-14-22-48.gh-issue-110388.1-HQJO.rst | 1 + 2 files changed, 61 insertions(+) create mode 100644 Lib/test/test_tty.py create mode 100644 Misc/NEWS.d/next/Tests/2023-10-05-14-22-48.gh-issue-110388.1-HQJO.rst diff --git a/Lib/test/test_tty.py b/Lib/test/test_tty.py new file mode 100644 index 00000000000000..ec1f3407cddf84 --- /dev/null +++ b/Lib/test/test_tty.py @@ -0,0 +1,60 @@ +import os +import unittest +from test.support.import_helper import import_module + +termios = import_module('termios') +tty = import_module('tty') + + +@unittest.skipUnless(hasattr(os, 'openpty'), "need os.openpty()") +class TestTty(unittest.TestCase): + + def setUp(self): + master_fd, self.fd = os.openpty() + self.addCleanup(os.close, master_fd) + self.stream = self.enterContext(open(self.fd, 'wb', buffering=0)) + self.fd = self.stream.fileno() + self.mode = termios.tcgetattr(self.fd) + self.addCleanup(termios.tcsetattr, self.fd, termios.TCSANOW, self.mode) + self.addCleanup(termios.tcsetattr, self.fd, termios.TCSAFLUSH, self.mode) + + def check_cbreak(self, mode): + self.assertEqual(mode[3] & termios.ECHO, 0) + self.assertEqual(mode[3] & termios.ICANON, 0) + self.assertEqual(mode[6][termios.VMIN], 1) + self.assertEqual(mode[6][termios.VTIME], 0) + + def check_raw(self, mode): + self.assertEqual(mode[0] & termios.ICRNL, 0) + self.check_cbreak(mode) + self.assertEqual(mode[0] & termios.ISTRIP, 0) + self.assertEqual(mode[0] & termios.ICRNL, 0) + self.assertEqual(mode[1] & termios.OPOST, 0) + self.assertEqual(mode[2] & termios.PARENB, termios.CS8 & termios.PARENB) + self.assertEqual(mode[2] & termios.CSIZE, termios.CS8 & termios.CSIZE) + self.assertEqual(mode[2] & termios.CS8, termios.CS8) + self.assertEqual(mode[3] & termios.ECHO, 0) + self.assertEqual(mode[3] & termios.ICANON, 0) + self.assertEqual(mode[3] & termios.ISIG, 0) + self.assertEqual(mode[6][termios.VMIN], 1) + self.assertEqual(mode[6][termios.VTIME], 0) + + def test_setraw(self): + tty.setraw(self.fd) + mode = termios.tcgetattr(self.fd) + self.check_raw(mode) + tty.setraw(self.fd, termios.TCSANOW) + tty.setraw(self.stream) + tty.setraw(fd=self.fd, when=termios.TCSANOW) + + def test_setcbreak(self): + tty.setcbreak(self.fd) + mode = termios.tcgetattr(self.fd) + self.check_cbreak(mode) + tty.setcbreak(self.fd, termios.TCSANOW) + tty.setcbreak(self.stream) + tty.setcbreak(fd=self.fd, when=termios.TCSANOW) + + +if __name__ == '__main__': + unittest.main() diff --git a/Misc/NEWS.d/next/Tests/2023-10-05-14-22-48.gh-issue-110388.1-HQJO.rst b/Misc/NEWS.d/next/Tests/2023-10-05-14-22-48.gh-issue-110388.1-HQJO.rst new file mode 100644 index 00000000000000..caac41f81547de --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-10-05-14-22-48.gh-issue-110388.1-HQJO.rst @@ -0,0 +1 @@ +Add tests for :mod:`tty`. From 4b67878daa9fee58db80609f851ada9402fcd71c Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 10 Oct 2023 17:03:11 +0200 Subject: [PATCH 452/632] [3.11] gh-110631: Set three-space indents for reST in EditorConfig (GH-110635) (#110638) gh-110631: Set three-space indents for reST in EditorConfig (GH-110635) Set three-space indents in EditorConfig (cherry picked from commit 66a9b1082049855889854bfde617059499c26dd2) Co-authored-by: Hugo van Kemenade --- .editorconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.editorconfig b/.editorconfig index 81445d2d79c739..0169eed951cd3f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,5 +8,8 @@ indent_style = space [*.{py,c,cpp,h}] indent_size = 4 +[*.rst] +indent_size = 3 + [*.yml] indent_size = 2 From 28c6cc1928bf517076a44b4112f7f56dae7f076b Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 10 Oct 2023 23:02:21 +0200 Subject: [PATCH 453/632] [3.11] [3.12] gh-108303: Move all certificates to `Lib/test/certdata/` (GH-109489) (GH-109682) (#110646) [3.12] gh-108303: Move all certificates to `Lib/test/certdata/` (GH-109489) (GH-109682) * gh-108303: Move all certificates to `Lib/test/certdata/` (GH-109489) (cherry picked from commit e57ecf6bbc59f999d27b125ea51b042c24a07bd9) Python 3.12 backport: update also `test_nntplib`. (cherry picked from commit c2d542b42cd109d81c0308f9c4437c38ac74d2e0) Co-authored-by: Nikita Sobolev Co-authored-by: T. Wouters --- Lib/test/{ => certdata}/allsans.pem | 0 Lib/test/{ => certdata}/badcert.pem | 0 Lib/test/{ => certdata}/badkey.pem | 0 Lib/test/{ => certdata}/capath/4e1295a3.0 | 0 Lib/test/{ => certdata}/capath/5ed36f99.0 | 0 Lib/test/{ => certdata}/capath/6e88d7b8.0 | 0 Lib/test/{ => certdata}/capath/99d0fa06.0 | 0 Lib/test/{ => certdata}/capath/b1930218.0 | 0 Lib/test/{ => certdata}/capath/ceff1710.0 | 0 Lib/test/{ => certdata}/ffdh3072.pem | 0 Lib/test/{ => certdata}/idnsans.pem | 0 Lib/test/{ => certdata}/keycert.passwd.pem | 0 Lib/test/{ => certdata}/keycert.pem | 0 Lib/test/{ => certdata}/keycert2.pem | 0 Lib/test/{ => certdata}/keycert3.pem | 0 Lib/test/{ => certdata}/keycert4.pem | 0 Lib/test/{ => certdata}/keycertecc.pem | 0 Lib/test/{ => certdata}/make_ssl_certs.py | 0 Lib/test/{ => certdata}/nokia.pem | 0 Lib/test/{ => certdata}/nosan.pem | 0 Lib/test/{ => certdata}/nullbytecert.pem | 0 Lib/test/{ => certdata}/nullcert.pem | 0 Lib/test/{ => certdata}/pycacert.pem | 0 Lib/test/{ => certdata}/pycakey.pem | 0 Lib/test/{ => certdata}/revocation.crl | 0 Lib/test/{ => certdata}/secp384r1.pem | 0 .../selfsigned_pythontestdotnet.pem | 0 Lib/test/{ => certdata}/ssl_cert.pem | 0 Lib/test/{ => certdata}/ssl_key.passwd.pem | 0 Lib/test/{ => certdata}/ssl_key.pem | 0 Lib/test/{ => certdata}/talos-2019-0758.pem | 0 Lib/test/ssl_servers.py | 2 +- Lib/test/test_asyncio/utils.py | 16 ++++++++-------- Lib/test/test_ftplib.py | 4 ++-- Lib/test/test_httplib.py | 8 +++++--- Lib/test/test_imaplib.py | 4 ++-- Lib/test/test_logging.py | 2 +- Lib/test/test_nntplib.py | 2 +- Lib/test/test_poplib.py | 4 ++-- Lib/test/test_ssl.py | 12 ++++++------ Lib/test/test_urllib2_localnet.py | 4 ++-- Makefile.pre.in | 3 ++- 42 files changed, 32 insertions(+), 29 deletions(-) rename Lib/test/{ => certdata}/allsans.pem (100%) rename Lib/test/{ => certdata}/badcert.pem (100%) rename Lib/test/{ => certdata}/badkey.pem (100%) rename Lib/test/{ => certdata}/capath/4e1295a3.0 (100%) rename Lib/test/{ => certdata}/capath/5ed36f99.0 (100%) rename Lib/test/{ => certdata}/capath/6e88d7b8.0 (100%) rename Lib/test/{ => certdata}/capath/99d0fa06.0 (100%) rename Lib/test/{ => certdata}/capath/b1930218.0 (100%) rename Lib/test/{ => certdata}/capath/ceff1710.0 (100%) rename Lib/test/{ => certdata}/ffdh3072.pem (100%) rename Lib/test/{ => certdata}/idnsans.pem (100%) rename Lib/test/{ => certdata}/keycert.passwd.pem (100%) rename Lib/test/{ => certdata}/keycert.pem (100%) rename Lib/test/{ => certdata}/keycert2.pem (100%) rename Lib/test/{ => certdata}/keycert3.pem (100%) rename Lib/test/{ => certdata}/keycert4.pem (100%) rename Lib/test/{ => certdata}/keycertecc.pem (100%) rename Lib/test/{ => certdata}/make_ssl_certs.py (100%) rename Lib/test/{ => certdata}/nokia.pem (100%) rename Lib/test/{ => certdata}/nosan.pem (100%) rename Lib/test/{ => certdata}/nullbytecert.pem (100%) rename Lib/test/{ => certdata}/nullcert.pem (100%) rename Lib/test/{ => certdata}/pycacert.pem (100%) rename Lib/test/{ => certdata}/pycakey.pem (100%) rename Lib/test/{ => certdata}/revocation.crl (100%) rename Lib/test/{ => certdata}/secp384r1.pem (100%) rename Lib/test/{ => certdata}/selfsigned_pythontestdotnet.pem (100%) rename Lib/test/{ => certdata}/ssl_cert.pem (100%) rename Lib/test/{ => certdata}/ssl_key.passwd.pem (100%) rename Lib/test/{ => certdata}/ssl_key.pem (100%) rename Lib/test/{ => certdata}/talos-2019-0758.pem (100%) diff --git a/Lib/test/allsans.pem b/Lib/test/certdata/allsans.pem similarity index 100% rename from Lib/test/allsans.pem rename to Lib/test/certdata/allsans.pem diff --git a/Lib/test/badcert.pem b/Lib/test/certdata/badcert.pem similarity index 100% rename from Lib/test/badcert.pem rename to Lib/test/certdata/badcert.pem diff --git a/Lib/test/badkey.pem b/Lib/test/certdata/badkey.pem similarity index 100% rename from Lib/test/badkey.pem rename to Lib/test/certdata/badkey.pem diff --git a/Lib/test/capath/4e1295a3.0 b/Lib/test/certdata/capath/4e1295a3.0 similarity index 100% rename from Lib/test/capath/4e1295a3.0 rename to Lib/test/certdata/capath/4e1295a3.0 diff --git a/Lib/test/capath/5ed36f99.0 b/Lib/test/certdata/capath/5ed36f99.0 similarity index 100% rename from Lib/test/capath/5ed36f99.0 rename to Lib/test/certdata/capath/5ed36f99.0 diff --git a/Lib/test/capath/6e88d7b8.0 b/Lib/test/certdata/capath/6e88d7b8.0 similarity index 100% rename from Lib/test/capath/6e88d7b8.0 rename to Lib/test/certdata/capath/6e88d7b8.0 diff --git a/Lib/test/capath/99d0fa06.0 b/Lib/test/certdata/capath/99d0fa06.0 similarity index 100% rename from Lib/test/capath/99d0fa06.0 rename to Lib/test/certdata/capath/99d0fa06.0 diff --git a/Lib/test/capath/b1930218.0 b/Lib/test/certdata/capath/b1930218.0 similarity index 100% rename from Lib/test/capath/b1930218.0 rename to Lib/test/certdata/capath/b1930218.0 diff --git a/Lib/test/capath/ceff1710.0 b/Lib/test/certdata/capath/ceff1710.0 similarity index 100% rename from Lib/test/capath/ceff1710.0 rename to Lib/test/certdata/capath/ceff1710.0 diff --git a/Lib/test/ffdh3072.pem b/Lib/test/certdata/ffdh3072.pem similarity index 100% rename from Lib/test/ffdh3072.pem rename to Lib/test/certdata/ffdh3072.pem diff --git a/Lib/test/idnsans.pem b/Lib/test/certdata/idnsans.pem similarity index 100% rename from Lib/test/idnsans.pem rename to Lib/test/certdata/idnsans.pem diff --git a/Lib/test/keycert.passwd.pem b/Lib/test/certdata/keycert.passwd.pem similarity index 100% rename from Lib/test/keycert.passwd.pem rename to Lib/test/certdata/keycert.passwd.pem diff --git a/Lib/test/keycert.pem b/Lib/test/certdata/keycert.pem similarity index 100% rename from Lib/test/keycert.pem rename to Lib/test/certdata/keycert.pem diff --git a/Lib/test/keycert2.pem b/Lib/test/certdata/keycert2.pem similarity index 100% rename from Lib/test/keycert2.pem rename to Lib/test/certdata/keycert2.pem diff --git a/Lib/test/keycert3.pem b/Lib/test/certdata/keycert3.pem similarity index 100% rename from Lib/test/keycert3.pem rename to Lib/test/certdata/keycert3.pem diff --git a/Lib/test/keycert4.pem b/Lib/test/certdata/keycert4.pem similarity index 100% rename from Lib/test/keycert4.pem rename to Lib/test/certdata/keycert4.pem diff --git a/Lib/test/keycertecc.pem b/Lib/test/certdata/keycertecc.pem similarity index 100% rename from Lib/test/keycertecc.pem rename to Lib/test/certdata/keycertecc.pem diff --git a/Lib/test/make_ssl_certs.py b/Lib/test/certdata/make_ssl_certs.py similarity index 100% rename from Lib/test/make_ssl_certs.py rename to Lib/test/certdata/make_ssl_certs.py diff --git a/Lib/test/nokia.pem b/Lib/test/certdata/nokia.pem similarity index 100% rename from Lib/test/nokia.pem rename to Lib/test/certdata/nokia.pem diff --git a/Lib/test/nosan.pem b/Lib/test/certdata/nosan.pem similarity index 100% rename from Lib/test/nosan.pem rename to Lib/test/certdata/nosan.pem diff --git a/Lib/test/nullbytecert.pem b/Lib/test/certdata/nullbytecert.pem similarity index 100% rename from Lib/test/nullbytecert.pem rename to Lib/test/certdata/nullbytecert.pem diff --git a/Lib/test/nullcert.pem b/Lib/test/certdata/nullcert.pem similarity index 100% rename from Lib/test/nullcert.pem rename to Lib/test/certdata/nullcert.pem diff --git a/Lib/test/pycacert.pem b/Lib/test/certdata/pycacert.pem similarity index 100% rename from Lib/test/pycacert.pem rename to Lib/test/certdata/pycacert.pem diff --git a/Lib/test/pycakey.pem b/Lib/test/certdata/pycakey.pem similarity index 100% rename from Lib/test/pycakey.pem rename to Lib/test/certdata/pycakey.pem diff --git a/Lib/test/revocation.crl b/Lib/test/certdata/revocation.crl similarity index 100% rename from Lib/test/revocation.crl rename to Lib/test/certdata/revocation.crl diff --git a/Lib/test/secp384r1.pem b/Lib/test/certdata/secp384r1.pem similarity index 100% rename from Lib/test/secp384r1.pem rename to Lib/test/certdata/secp384r1.pem diff --git a/Lib/test/selfsigned_pythontestdotnet.pem b/Lib/test/certdata/selfsigned_pythontestdotnet.pem similarity index 100% rename from Lib/test/selfsigned_pythontestdotnet.pem rename to Lib/test/certdata/selfsigned_pythontestdotnet.pem diff --git a/Lib/test/ssl_cert.pem b/Lib/test/certdata/ssl_cert.pem similarity index 100% rename from Lib/test/ssl_cert.pem rename to Lib/test/certdata/ssl_cert.pem diff --git a/Lib/test/ssl_key.passwd.pem b/Lib/test/certdata/ssl_key.passwd.pem similarity index 100% rename from Lib/test/ssl_key.passwd.pem rename to Lib/test/certdata/ssl_key.passwd.pem diff --git a/Lib/test/ssl_key.pem b/Lib/test/certdata/ssl_key.pem similarity index 100% rename from Lib/test/ssl_key.pem rename to Lib/test/certdata/ssl_key.pem diff --git a/Lib/test/talos-2019-0758.pem b/Lib/test/certdata/talos-2019-0758.pem similarity index 100% rename from Lib/test/talos-2019-0758.pem rename to Lib/test/certdata/talos-2019-0758.pem diff --git a/Lib/test/ssl_servers.py b/Lib/test/ssl_servers.py index a4bd7455d47e76..15b071e04dda1f 100644 --- a/Lib/test/ssl_servers.py +++ b/Lib/test/ssl_servers.py @@ -14,7 +14,7 @@ here = os.path.dirname(__file__) HOST = socket_helper.HOST -CERTFILE = os.path.join(here, 'keycert.pem') +CERTFILE = os.path.join(here, 'certdata', 'keycert.pem') # This one's based on HTTPServer, which is based on socketserver diff --git a/Lib/test/test_asyncio/utils.py b/Lib/test/test_asyncio/utils.py index 19de8540fdb546..f49db4e37b3592 100644 --- a/Lib/test/test_asyncio/utils.py +++ b/Lib/test/test_asyncio/utils.py @@ -43,21 +43,21 @@ CLOCK_RES = 0.020 -def data_file(filename): +def data_file(*filename): if hasattr(support, 'TEST_HOME_DIR'): - fullname = os.path.join(support.TEST_HOME_DIR, filename) + fullname = os.path.join(support.TEST_HOME_DIR, *filename) if os.path.isfile(fullname): return fullname - fullname = os.path.join(os.path.dirname(__file__), '..', filename) + fullname = os.path.join(os.path.dirname(__file__), '..', *filename) if os.path.isfile(fullname): return fullname - raise FileNotFoundError(filename) + raise FileNotFoundError(os.path.join(filename)) -ONLYCERT = data_file('ssl_cert.pem') -ONLYKEY = data_file('ssl_key.pem') -SIGNED_CERTFILE = data_file('keycert3.pem') -SIGNING_CA = data_file('pycacert.pem') +ONLYCERT = data_file('certdata', 'ssl_cert.pem') +ONLYKEY = data_file('certdata', 'ssl_key.pem') +SIGNED_CERTFILE = data_file('certdata', 'keycert3.pem') +SIGNING_CA = data_file('certdata', 'pycacert.pem') PEERCERT = { 'OCSP': ('http://testca.pythontest.net/testca/ocsp/',), 'caIssuers': ('http://testca.pythontest.net/testca/pycacert.cer',), diff --git a/Lib/test/test_ftplib.py b/Lib/test/test_ftplib.py index 7d6b12ffadcef3..a90a53f278664f 100644 --- a/Lib/test/test_ftplib.py +++ b/Lib/test/test_ftplib.py @@ -327,8 +327,8 @@ def handle_error(self): if ssl is not None: - CERTFILE = os.path.join(os.path.dirname(__file__), "keycert3.pem") - CAFILE = os.path.join(os.path.dirname(__file__), "pycacert.pem") + CERTFILE = os.path.join(os.path.dirname(__file__), "certdata", "keycert3.pem") + CAFILE = os.path.join(os.path.dirname(__file__), "certdata", "pycacert.pem") class SSLConnection(asyncore.dispatcher): """An asyncore.dispatcher subclass supporting TLS/SSL.""" diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index 47dbf08f3700d9..f6a9c820b54d31 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -23,11 +23,13 @@ here = os.path.dirname(__file__) # Self-signed cert file for 'localhost' -CERT_localhost = os.path.join(here, 'keycert.pem') +CERT_localhost = os.path.join(here, 'certdata', 'keycert.pem') # Self-signed cert file for 'fakehostname' -CERT_fakehostname = os.path.join(here, 'keycert2.pem') +CERT_fakehostname = os.path.join(here, 'certdata', 'keycert2.pem') # Self-signed cert file for self-signed.pythontest.net -CERT_selfsigned_pythontestdotnet = os.path.join(here, 'selfsigned_pythontestdotnet.pem') +CERT_selfsigned_pythontestdotnet = os.path.join( + here, 'certdata', 'selfsigned_pythontestdotnet.pem', +) # constants for testing chunked encoding chunked_start = ( diff --git a/Lib/test/test_imaplib.py b/Lib/test/test_imaplib.py index f097ba68154f25..bd0fc9c2da1c23 100644 --- a/Lib/test/test_imaplib.py +++ b/Lib/test/test_imaplib.py @@ -26,8 +26,8 @@ support.requires_working_socket(module=True) -CERTFILE = os.path.join(os.path.dirname(__file__) or os.curdir, "keycert3.pem") -CAFILE = os.path.join(os.path.dirname(__file__) or os.curdir, "pycacert.pem") +CERTFILE = os.path.join(os.path.dirname(__file__) or os.curdir, "certdata", "keycert3.pem") +CAFILE = os.path.join(os.path.dirname(__file__) or os.curdir, "certdata", "pycacert.pem") class TestImaplib(unittest.TestCase): diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 55c5cd565814e8..ccf479d8e7e1c2 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -2075,7 +2075,7 @@ def test_output(self): sslctx = None else: here = os.path.dirname(__file__) - localhost_cert = os.path.join(here, "keycert.pem") + localhost_cert = os.path.join(here, "certdata", "keycert.pem") sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) sslctx.load_cert_chain(localhost_cert) diff --git a/Lib/test/test_nntplib.py b/Lib/test/test_nntplib.py index 31a02f86abb003..30ae557978308c 100644 --- a/Lib/test/test_nntplib.py +++ b/Lib/test/test_nntplib.py @@ -20,7 +20,7 @@ ssl = None -certfile = os.path.join(os.path.dirname(__file__), 'keycert3.pem') +certfile = os.path.join(os.path.dirname(__file__), 'certdata', 'keycert3.pem') if ssl is not None: SSLError = ssl.SSLError diff --git a/Lib/test/test_poplib.py b/Lib/test/test_poplib.py index 5ad9202433dcfb..49ba9931974ec6 100644 --- a/Lib/test/test_poplib.py +++ b/Lib/test/test_poplib.py @@ -32,8 +32,8 @@ import ssl SUPPORTS_SSL = True - CERTFILE = os.path.join(os.path.dirname(__file__) or os.curdir, "keycert3.pem") - CAFILE = os.path.join(os.path.dirname(__file__) or os.curdir, "pycacert.pem") + CERTFILE = os.path.join(os.path.dirname(__file__) or os.curdir, "certdata", "keycert3.pem") + CAFILE = os.path.join(os.path.dirname(__file__) or os.curdir, "certdata", "pycacert.pem") requires_ssl = skipUnless(SUPPORTS_SSL, 'SSL not supported') diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 1bfec237484124..c98f7679a9f06e 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -63,10 +63,10 @@ PROTOCOL_TO_TLS_VERSION[proto] = ver def data_file(*name): - return os.path.join(os.path.dirname(__file__), *name) + return os.path.join(os.path.dirname(__file__), "certdata", *name) # The custom key and certificate files used in test_ssl are generated -# using Lib/test/make_ssl_certs.py. +# using Lib/test/certdata/make_ssl_certs.py. # Other certificates are simply fetched from the internet servers they # are meant to authenticate. @@ -675,7 +675,7 @@ def test_errors_sslwrap(self): def bad_cert_test(self, certfile): """Check that trying to use the given client certificate fails""" certfile = os.path.join(os.path.dirname(__file__) or os.curdir, - certfile) + "certdata", certfile) sock = socket.socket() self.addCleanup(sock.close) with self.assertRaises(ssl.SSLError): @@ -3557,12 +3557,12 @@ def test_socketserver(self): # try to connect if support.verbose: sys.stdout.write('\n') - with open(CERTFILE, 'rb') as f: + # Get this test file itself: + with open(__file__, 'rb') as f: d1 = f.read() d2 = '' # now fetch the same data from the HTTPS server - url = 'https://localhost:%d/%s' % ( - server.port, os.path.split(CERTFILE)[1]) + url = f'https://localhost:{server.port}/test_ssl.py' context = ssl.create_default_context(cafile=SIGNING_CA) f = urllib.request.urlopen(url, context=context) try: diff --git a/Lib/test/test_urllib2_localnet.py b/Lib/test/test_urllib2_localnet.py index f4729358557c95..96e43970d49fb9 100644 --- a/Lib/test/test_urllib2_localnet.py +++ b/Lib/test/test_urllib2_localnet.py @@ -22,9 +22,9 @@ here = os.path.dirname(__file__) # Self-signed cert file for 'localhost' -CERT_localhost = os.path.join(here, 'keycert.pem') +CERT_localhost = os.path.join(here, 'certdata', 'keycert.pem') # Self-signed cert file for 'fakehostname' -CERT_fakehostname = os.path.join(here, 'keycert2.pem') +CERT_fakehostname = os.path.join(here, 'certdata', 'keycert2.pem') # Loopback http server infrastructure diff --git a/Makefile.pre.in b/Makefile.pre.in index 1dd8189167aa21..49238902718eb8 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1947,7 +1947,8 @@ TESTSUBDIRS= ctypes/test \ lib2to3/tests/data/fixers/myfixes \ test \ test/audiodata \ - test/capath \ + test/certdata \ + test/certdata/capath \ test/cjkencodings \ test/crashers \ test/data \ From a009bb872c57ad3317caaf4caad5327d0ff664e2 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 11 Oct 2023 00:41:12 +0200 Subject: [PATCH 454/632] [3.11] gh-109286: Update Windows installer to use SQLite 3.43.1 (#110403) (#110479) --- .../next/Windows/2023-10-05-15-23-23.gh-issue-109286.N8OzMg.rst | 1 + PCbuild/get_externals.bat | 2 +- PCbuild/python.props | 2 +- PCbuild/readme.txt | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2023-10-05-15-23-23.gh-issue-109286.N8OzMg.rst diff --git a/Misc/NEWS.d/next/Windows/2023-10-05-15-23-23.gh-issue-109286.N8OzMg.rst b/Misc/NEWS.d/next/Windows/2023-10-05-15-23-23.gh-issue-109286.N8OzMg.rst new file mode 100644 index 00000000000000..14a2aff70803ce --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-10-05-15-23-23.gh-issue-109286.N8OzMg.rst @@ -0,0 +1 @@ +Update Windows installer to use SQLite 3.43.1. diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index 60dc725bb6a2fb..5e2781e9757c4d 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -54,7 +54,7 @@ set libraries= set libraries=%libraries% bzip2-1.0.8 if NOT "%IncludeLibffiSrc%"=="false" set libraries=%libraries% libffi-3.4.4 if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-3.0.11 -set libraries=%libraries% sqlite-3.42.0.0 +set libraries=%libraries% sqlite-3.43.1.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.12.1 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.12.1 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tix-8.4.3.6 diff --git a/PCbuild/python.props b/PCbuild/python.props index 35c6e92be45dc9..496bc3dd4cf794 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -68,7 +68,7 @@ - $(ExternalsDir)sqlite-3.42.0.0\ + $(ExternalsDir)sqlite-3.43.1.0\ $(ExternalsDir)bzip2-1.0.8\ $(ExternalsDir)xz-5.2.5\ $(ExternalsDir)libffi-3.4.4\ diff --git a/PCbuild/readme.txt b/PCbuild/readme.txt index d6149a3cedeaa3..6a2c7a95b83718 100644 --- a/PCbuild/readme.txt +++ b/PCbuild/readme.txt @@ -187,7 +187,7 @@ _ssl again when building. _sqlite3 - Wraps SQLite 3.42.0, which is itself built by sqlite3.vcxproj + Wraps SQLite 3.43.1, which is itself built by sqlite3.vcxproj Homepage: https://www.sqlite.org/ _tkinter From 46347d3caf92e773f99b301ab4de7d13c99e04e3 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 11 Oct 2023 02:21:51 +0200 Subject: [PATCH 455/632] [3.11] gh-110647: Fix signal test_stress_modifying_handlers() (GH-110650) (#110659) gh-110647: Fix signal test_stress_modifying_handlers() (GH-110650) * cycle_handlers() now waits until at least one signal is received. * num_received_signals can be equal to num_sent_signals. (cherry picked from commit e07c37cd5212c9d13749b4d02a1d68e1efcba6cf) Co-authored-by: Victor Stinner --- Lib/test/test_signal.py | 4 ++-- .../next/Tests/2023-10-10-23-20-13.gh-issue-110647.jKG3sY.rst | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2023-10-10-23-20-13.gh-issue-110647.jKG3sY.rst diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py index a00c7e539400d9..0f3343b10e4a07 100644 --- a/Lib/test/test_signal.py +++ b/Lib/test/test_signal.py @@ -1347,7 +1347,7 @@ def set_interrupts(): num_sent_signals += 1 def cycle_handlers(): - while num_sent_signals < 100: + while num_sent_signals < 100 or num_received_signals < 1: for i in range(20000): # Cycle between a Python-defined and a non-Python handler for handler in [custom_handler, signal.SIG_IGN]: @@ -1380,7 +1380,7 @@ def cycle_handlers(): if not ignored: # Sanity check that some signals were received, but not all self.assertGreater(num_received_signals, 0) - self.assertLess(num_received_signals, num_sent_signals) + self.assertLessEqual(num_received_signals, num_sent_signals) finally: do_stop = True t.join() diff --git a/Misc/NEWS.d/next/Tests/2023-10-10-23-20-13.gh-issue-110647.jKG3sY.rst b/Misc/NEWS.d/next/Tests/2023-10-10-23-20-13.gh-issue-110647.jKG3sY.rst new file mode 100644 index 00000000000000..00f38c844755ec --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-10-10-23-20-13.gh-issue-110647.jKG3sY.rst @@ -0,0 +1,2 @@ +Fix test_stress_modifying_handlers() of test_signal. Patch by Victor +Stinner. From 7984dc28b3c3163b0e58ba326417cd3e4bc3dbaa Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 11 Oct 2023 03:22:51 +0200 Subject: [PATCH 456/632] [3.11] gh-110656: Fix logging test_post_fork_child_no_deadlock() if ASAN (GH-110657) (#110665) gh-110656: Fix logging test_post_fork_child_no_deadlock() if ASAN (GH-110657) Skip test_post_fork_child_no_deadlock() if Python is built with ASAN. Add support.HAVE_ASAN_FORK_BUG. (cherry picked from commit f901f56313610389027cb4eae80d1d4b071aef69) Co-authored-by: Victor Stinner --- Lib/test/_test_multiprocessing.py | 4 ++-- Lib/test/support/__init__.py | 4 ++++ Lib/test/test_logging.py | 8 ++++++++ Lib/test/test_threading.py | 9 +-------- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 42441fed79bf3e..834bc6b87fb8d2 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -77,10 +77,10 @@ msvcrt = None -if support.check_sanitizer(address=True): +if support.HAVE_ASAN_FORK_BUG: # gh-89363: Skip multiprocessing tests if Python is built with ASAN to # work around a libasan race condition: dead lock in pthread_create(). - raise unittest.SkipTest("libasan has a pthread_create() dead lock") + raise unittest.SkipTest("libasan has a pthread_create() dead lock related to thread+fork") def latin(s): diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 1d3b0053bf86c9..f5307b6442bf66 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -426,6 +426,10 @@ def skip_if_sanitizer(reason=None, *, address=False, memory=False, ub=False): skip = check_sanitizer(address=address, memory=memory, ub=ub) return unittest.skipIf(skip, reason) +# gh-89363: True if fork() can hang if Python is built with Address Sanitizer +# (ASAN): libasan race condition, dead lock in pthread_create(). +HAVE_ASAN_FORK_BUG = check_sanitizer(address=True) + def system_must_validate_cert(f): """Skip the test on TLS certificate validation failures.""" diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index ccf479d8e7e1c2..e097acd85eb72b 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -76,6 +76,13 @@ pass +# gh-89363: Skip fork() test if Python is built with Address Sanitizer (ASAN) +# to work around a libasan race condition, dead lock in pthread_create(). +skip_if_asan_fork = unittest.skipIf( + support.HAVE_ASAN_FORK_BUG, + "libasan has a pthread_create() dead lock related to thread+fork") + + class BaseTest(unittest.TestCase): """Base class for logging tests.""" @@ -682,6 +689,7 @@ def remove_loop(fname, tries): # register_at_fork mechanism is also present and used. @support.requires_fork() @threading_helper.requires_working_threading() + @skip_if_asan_fork def test_post_fork_child_no_deadlock(self): """Ensure child logging locks are not held; bpo-6721 & bpo-36533.""" class _OurHandler(logging.Handler): diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index a4192acd6dbc5c..17319c366a483b 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -37,19 +37,12 @@ Py_DEBUG = hasattr(sys, 'gettotalrefcount') -# gh-89363: Skip fork() test if Python is built with Address Sanitizer (ASAN) -# to work around a libasan race condition, dead lock in pthread_create(). -skip_if_asan_fork = support.skip_if_sanitizer( - "libasan has a pthread_create() dead lock", - address=True) - - def skip_unless_reliable_fork(test): if not support.has_fork_support: return unittest.skip("requires working os.fork()")(test) if sys.platform in platforms_to_skip: return unittest.skip("due to known OS bug related to thread+fork")(test) - if support.check_sanitizer(address=True): + if support.HAVE_ASAN_FORK_BUG: return unittest.skip("libasan has a pthread_create() dead lock related to thread+fork")(test) return test From 8ca7a230e7f6734987478a4c4db2efded594fb84 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 11 Oct 2023 04:09:53 +0200 Subject: [PATCH 457/632] [3.11] gh-110666: Fix multiprocessing test_terminate() elapsed (GH-110667) (#110669) gh-110666: Fix multiprocessing test_terminate() elapsed (GH-110667) multiprocessing test_terminate() and test_wait_socket_slow() no longer test the CI performance: no longer check maximum elapsed time. Add CLOCK_RES constant: tolerate a difference of 100 ms. (cherry picked from commit 1556f426da3f2fb5842689999933c8038b65c034) Co-authored-by: Victor Stinner --- Lib/test/_test_multiprocessing.py | 35 +++++++++++++------------------ 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 834bc6b87fb8d2..84bca5b3b259fa 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -83,6 +83,11 @@ raise unittest.SkipTest("libasan has a pthread_create() dead lock related to thread+fork") +# gh-110666: Tolerate a difference of 100 ms when comparing timings +# (clock resolution) +CLOCK_RES = 0.100 + + def latin(s): return s.encode('latin') @@ -1654,8 +1659,7 @@ def _test_waitfor_timeout_f(cls, cond, state, success, sem): dt = time.monotonic() result = cond.wait_for(lambda : state.value==4, timeout=expected) dt = time.monotonic() - dt - # borrow logic in assertTimeout() from test/lock_tests.py - if not result and expected * 0.6 <= dt: + if not result and (expected - CLOCK_RES) <= dt: success.value = True @unittest.skipUnless(HAS_SHAREDCTYPES, 'needs sharedctypes') @@ -2677,14 +2681,11 @@ def test_make_pool(self): p.join() def test_terminate(self): - result = self.pool.map_async( - time.sleep, [0.1 for i in range(10000)], chunksize=1 - ) + # Simulate slow tasks which take "forever" to complete + args = [support.LONG_TIMEOUT for i in range(10_000)] + result = self.pool.map_async(time.sleep, args, chunksize=1) self.pool.terminate() - join = TimingWrapper(self.pool.join) - join() - # Sanity check the pool didn't wait for all tasks to finish - self.assertLess(join.elapsed, 2.0) + self.pool.join() def test_empty_iterable(self): # See Issue 12157 @@ -4831,7 +4832,7 @@ class TestWait(unittest.TestCase): def _child_test_wait(cls, w, slow): for i in range(10): if slow: - time.sleep(random.random()*0.1) + time.sleep(random.random() * 0.100) w.send((i, os.getpid())) w.close() @@ -4871,7 +4872,7 @@ def _child_test_wait_socket(cls, address, slow): s.connect(address) for i in range(10): if slow: - time.sleep(random.random()*0.1) + time.sleep(random.random() * 0.100) s.sendall(('%s\n' % i).encode('ascii')) s.close() @@ -4920,25 +4921,19 @@ def test_wait_socket_slow(self): def test_wait_timeout(self): from multiprocessing.connection import wait - expected = 5 + timeout = 5.0 # seconds a, b = multiprocessing.Pipe() start = time.monotonic() - res = wait([a, b], expected) + res = wait([a, b], timeout) delta = time.monotonic() - start self.assertEqual(res, []) - self.assertLess(delta, expected * 2) - self.assertGreater(delta, expected * 0.5) + self.assertGreater(delta, timeout - CLOCK_RES) b.send(None) - - start = time.monotonic() res = wait([a, b], 20) - delta = time.monotonic() - start - self.assertEqual(res, [a]) - self.assertLess(delta, 0.4) @classmethod def signal_and_sleep(cls, sem, period): From c6d5628be950bdf2c31243b4cc0d9e0b658458dd Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 11 Oct 2023 05:07:03 +0200 Subject: [PATCH 458/632] [3.11] gh-110662: multiprocessing test_async_timeout() increase timeout (GH-110663) (#110675) gh-110662: multiprocessing test_async_timeout() increase timeout (GH-110663) Increase timeout from 1 second to 30 seconds, if not longer. The important part is that apply_async() takes longer than TIMEOUT2. (cherry picked from commit 790ecf6302e47b84da5d1c3b14dbdf070bce615b) Co-authored-by: Victor Stinner --- Lib/test/_test_multiprocessing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 84bca5b3b259fa..3bcb1d1d46991f 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -2577,7 +2577,7 @@ def test_async(self): self.assertTimingAlmostEqual(get.elapsed, TIMEOUT1) def test_async_timeout(self): - res = self.pool.apply_async(sqr, (6, TIMEOUT2 + 1.0)) + res = self.pool.apply_async(sqr, (6, TIMEOUT2 + support.SHORT_TIMEOUT)) get = TimingWrapper(res.get) self.assertRaises(multiprocessing.TimeoutError, get, timeout=TIMEOUT2) self.assertTimingAlmostEqual(get.elapsed, TIMEOUT2) From a8b2d12a258a72963beb86406c69eb928863a09b Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Wed, 11 Oct 2023 11:53:26 +0200 Subject: [PATCH 459/632] [3.11] gh-110631: fix wrong indentation in the `Doc/whatsnew` dir (GH-110632) (#110691) fix wrong indentation in the `Doc/whatsnew` dir (#110632) --- Doc/whatsnew/2.6.rst | 20 ++--- Doc/whatsnew/2.7.rst | 2 +- Doc/whatsnew/3.10.rst | 200 +++++++++++++++++++++--------------------- Doc/whatsnew/3.3.rst | 128 +++++++++++++-------------- Doc/whatsnew/3.7.rst | 14 +-- 5 files changed, 182 insertions(+), 182 deletions(-) diff --git a/Doc/whatsnew/2.6.rst b/Doc/whatsnew/2.6.rst index 96d9b792b3723a..759a0757ebefa7 100644 --- a/Doc/whatsnew/2.6.rst +++ b/Doc/whatsnew/2.6.rst @@ -875,11 +875,11 @@ The signature of the new function is:: The parameters are: - * *args*: positional arguments whose values will be printed out. - * *sep*: the separator, which will be printed between arguments. - * *end*: the ending text, which will be printed after all of the - arguments have been output. - * *file*: the file object to which the output will be sent. +* *args*: positional arguments whose values will be printed out. +* *sep*: the separator, which will be printed between arguments. +* *end*: the ending text, which will be printed after all of the + arguments have been output. +* *file*: the file object to which the output will be sent. .. seealso:: @@ -1138,13 +1138,13 @@ indicate that the external caller is done. The *flags* argument to :c:func:`PyObject_GetBuffer` specifies constraints upon the memory returned. Some examples are: - * :c:macro:`PyBUF_WRITABLE` indicates that the memory must be writable. +* :c:macro:`PyBUF_WRITABLE` indicates that the memory must be writable. - * :c:macro:`PyBUF_LOCK` requests a read-only or exclusive lock on the memory. +* :c:macro:`PyBUF_LOCK` requests a read-only or exclusive lock on the memory. - * :c:macro:`PyBUF_C_CONTIGUOUS` and :c:macro:`PyBUF_F_CONTIGUOUS` - requests a C-contiguous (last dimension varies the fastest) or - Fortran-contiguous (first dimension varies the fastest) array layout. +* :c:macro:`PyBUF_C_CONTIGUOUS` and :c:macro:`PyBUF_F_CONTIGUOUS` + requests a C-contiguous (last dimension varies the fastest) or + Fortran-contiguous (first dimension varies the fastest) array layout. Two new argument codes for :c:func:`PyArg_ParseTuple`, ``s*`` and ``z*``, return locked buffer objects for a parameter. diff --git a/Doc/whatsnew/2.7.rst b/Doc/whatsnew/2.7.rst index 787fe8468b50d1..caf28ff50b3aa2 100644 --- a/Doc/whatsnew/2.7.rst +++ b/Doc/whatsnew/2.7.rst @@ -2367,7 +2367,7 @@ Port-Specific Changes: Mac OS X installation and a user-installed copy of the same version. (Changed by Ronald Oussoren; :issue:`4865`.) - .. versionchanged:: 2.7.13 + .. versionchanged:: 2.7.13 As of 2.7.13, this change was removed. ``/Library/Python/2.7/site-packages``, the site-packages directory diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 572bb4b07e5030..144a4940794d81 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -221,116 +221,116 @@ have been incorporated. Some of the most notable ones are as follows: * Missing ``:`` before blocks: - .. code-block:: python + .. code-block:: python - >>> if rocket.position > event_horizon - File "", line 1 - if rocket.position > event_horizon - ^ - SyntaxError: expected ':' + >>> if rocket.position > event_horizon + File "", line 1 + if rocket.position > event_horizon + ^ + SyntaxError: expected ':' - (Contributed by Pablo Galindo in :issue:`42997`.) + (Contributed by Pablo Galindo in :issue:`42997`.) * Unparenthesised tuples in comprehensions targets: - .. code-block:: python + .. code-block:: python - >>> {x,y for x,y in zip('abcd', '1234')} - File "", line 1 - {x,y for x,y in zip('abcd', '1234')} - ^ - SyntaxError: did you forget parentheses around the comprehension target? + >>> {x,y for x,y in zip('abcd', '1234')} + File "", line 1 + {x,y for x,y in zip('abcd', '1234')} + ^ + SyntaxError: did you forget parentheses around the comprehension target? - (Contributed by Pablo Galindo in :issue:`43017`.) + (Contributed by Pablo Galindo in :issue:`43017`.) * Missing commas in collection literals and between expressions: - .. code-block:: python + .. code-block:: python - >>> items = { - ... x: 1, - ... y: 2 - ... z: 3, - File "", line 3 - y: 2 - ^ - SyntaxError: invalid syntax. Perhaps you forgot a comma? + >>> items = { + ... x: 1, + ... y: 2 + ... z: 3, + File "", line 3 + y: 2 + ^ + SyntaxError: invalid syntax. Perhaps you forgot a comma? - (Contributed by Pablo Galindo in :issue:`43822`.) + (Contributed by Pablo Galindo in :issue:`43822`.) * Multiple Exception types without parentheses: - .. code-block:: python + .. code-block:: python - >>> try: - ... build_dyson_sphere() - ... except NotEnoughScienceError, NotEnoughResourcesError: - File "", line 3 - except NotEnoughScienceError, NotEnoughResourcesError: - ^ - SyntaxError: multiple exception types must be parenthesized + >>> try: + ... build_dyson_sphere() + ... except NotEnoughScienceError, NotEnoughResourcesError: + File "", line 3 + except NotEnoughScienceError, NotEnoughResourcesError: + ^ + SyntaxError: multiple exception types must be parenthesized - (Contributed by Pablo Galindo in :issue:`43149`.) + (Contributed by Pablo Galindo in :issue:`43149`.) * Missing ``:`` and values in dictionary literals: - .. code-block:: python + .. code-block:: python - >>> values = { - ... x: 1, - ... y: 2, - ... z: - ... } - File "", line 4 - z: - ^ - SyntaxError: expression expected after dictionary key and ':' + >>> values = { + ... x: 1, + ... y: 2, + ... z: + ... } + File "", line 4 + z: + ^ + SyntaxError: expression expected after dictionary key and ':' - >>> values = {x:1, y:2, z w:3} - File "", line 1 - values = {x:1, y:2, z w:3} - ^ - SyntaxError: ':' expected after dictionary key + >>> values = {x:1, y:2, z w:3} + File "", line 1 + values = {x:1, y:2, z w:3} + ^ + SyntaxError: ':' expected after dictionary key - (Contributed by Pablo Galindo in :issue:`43823`.) + (Contributed by Pablo Galindo in :issue:`43823`.) * ``try`` blocks without ``except`` or ``finally`` blocks: - .. code-block:: python + .. code-block:: python - >>> try: - ... x = 2 - ... something = 3 - File "", line 3 - something = 3 - ^^^^^^^^^ - SyntaxError: expected 'except' or 'finally' block + >>> try: + ... x = 2 + ... something = 3 + File "", line 3 + something = 3 + ^^^^^^^^^ + SyntaxError: expected 'except' or 'finally' block - (Contributed by Pablo Galindo in :issue:`44305`.) + (Contributed by Pablo Galindo in :issue:`44305`.) * Usage of ``=`` instead of ``==`` in comparisons: - .. code-block:: python + .. code-block:: python - >>> if rocket.position = event_horizon: - File "", line 1 - if rocket.position = event_horizon: - ^ - SyntaxError: cannot assign to attribute here. Maybe you meant '==' instead of '='? + >>> if rocket.position = event_horizon: + File "", line 1 + if rocket.position = event_horizon: + ^ + SyntaxError: cannot assign to attribute here. Maybe you meant '==' instead of '='? - (Contributed by Pablo Galindo in :issue:`43797`.) + (Contributed by Pablo Galindo in :issue:`43797`.) * Usage of ``*`` in f-strings: - .. code-block:: python + .. code-block:: python - >>> f"Black holes {*all_black_holes} and revelations" - File "", line 1 - (*all_black_holes) - ^ - SyntaxError: f-string: cannot use starred expression here + >>> f"Black holes {*all_black_holes} and revelations" + File "", line 1 + (*all_black_holes) + ^ + SyntaxError: f-string: cannot use starred expression here - (Contributed by Pablo Galindo in :issue:`41064`.) + (Contributed by Pablo Galindo in :issue:`41064`.) IndentationErrors ~~~~~~~~~~~~~~~~~ @@ -365,10 +365,10 @@ raised from: (Contributed by Pablo Galindo in :issue:`38530`.) - .. warning:: - Notice this won't work if :c:func:`PyErr_Display` is not called to display the error - which can happen if some other custom error display function is used. This is a common - scenario in some REPLs like IPython. +.. warning:: + Notice this won't work if :c:func:`PyErr_Display` is not called to display the error + which can happen if some other custom error display function is used. This is a common + scenario in some REPLs like IPython. NameErrors ~~~~~~~~~~ @@ -387,10 +387,10 @@ was raised from: (Contributed by Pablo Galindo in :issue:`38530`.) - .. warning:: - Notice this won't work if :c:func:`PyErr_Display` is not called to display the error, - which can happen if some other custom error display function is used. This is a common - scenario in some REPLs like IPython. +.. warning:: + Notice this won't work if :c:func:`PyErr_Display` is not called to display the error, + which can happen if some other custom error display function is used. This is a common + scenario in some REPLs like IPython. PEP 626: Precise line numbers for debugging and other tools @@ -433,16 +433,16 @@ A match statement takes an expression and compares its value to successive patterns given as one or more case blocks. Specifically, pattern matching operates by: - 1. using data with type and shape (the ``subject``) - 2. evaluating the ``subject`` in the ``match`` statement - 3. comparing the subject with each pattern in a ``case`` statement - from top to bottom until a match is confirmed. - 4. executing the action associated with the pattern of the confirmed - match - 5. If an exact match is not confirmed, the last case, a wildcard ``_``, - if provided, will be used as the matching case. If an exact match is - not confirmed and a wildcard case does not exist, the entire match - block is a no-op. +1. using data with type and shape (the ``subject``) +2. evaluating the ``subject`` in the ``match`` statement +3. comparing the subject with each pattern in a ``case`` statement + from top to bottom until a match is confirmed. +4. executing the action associated with the pattern of the confirmed + match +5. If an exact match is not confirmed, the last case, a wildcard ``_``, + if provided, will be used as the matching case. If an exact match is + not confirmed and a wildcard case does not exist, the entire match + block is a no-op. Declarative approach ~~~~~~~~~~~~~~~~~~~~ @@ -2210,16 +2210,16 @@ Removed * Removed ``Py_UNICODE_str*`` functions manipulating ``Py_UNICODE*`` strings. (Contributed by Inada Naoki in :issue:`41123`.) - * ``Py_UNICODE_strlen``: use :c:func:`PyUnicode_GetLength` or - :c:macro:`PyUnicode_GET_LENGTH` - * ``Py_UNICODE_strcat``: use :c:func:`PyUnicode_CopyCharacters` or - :c:func:`PyUnicode_FromFormat` - * ``Py_UNICODE_strcpy``, ``Py_UNICODE_strncpy``: use - :c:func:`PyUnicode_CopyCharacters` or :c:func:`PyUnicode_Substring` - * ``Py_UNICODE_strcmp``: use :c:func:`PyUnicode_Compare` - * ``Py_UNICODE_strncmp``: use :c:func:`PyUnicode_Tailmatch` - * ``Py_UNICODE_strchr``, ``Py_UNICODE_strrchr``: use - :c:func:`PyUnicode_FindChar` + * ``Py_UNICODE_strlen``: use :c:func:`PyUnicode_GetLength` or + :c:macro:`PyUnicode_GET_LENGTH` + * ``Py_UNICODE_strcat``: use :c:func:`PyUnicode_CopyCharacters` or + :c:func:`PyUnicode_FromFormat` + * ``Py_UNICODE_strcpy``, ``Py_UNICODE_strncpy``: use + :c:func:`PyUnicode_CopyCharacters` or :c:func:`PyUnicode_Substring` + * ``Py_UNICODE_strcmp``: use :c:func:`PyUnicode_Compare` + * ``Py_UNICODE_strncmp``: use :c:func:`PyUnicode_Tailmatch` + * ``Py_UNICODE_strchr``, ``Py_UNICODE_strrchr``: use + :c:func:`PyUnicode_FindChar` * Removed ``PyUnicode_GetMax()``. Please migrate to new (:pep:`393`) APIs. (Contributed by Inada Naoki in :issue:`41103`.) diff --git a/Doc/whatsnew/3.3.rst b/Doc/whatsnew/3.3.rst index becff2f27a309e..3bc3d2f7c3740e 100644 --- a/Doc/whatsnew/3.3.rst +++ b/Doc/whatsnew/3.3.rst @@ -916,12 +916,12 @@ abstract methods. The recommended approach to declaring abstract descriptors is now to provide :attr:`__isabstractmethod__` as a dynamically updated property. The built-in descriptors have been updated accordingly. - * :class:`abc.abstractproperty` has been deprecated, use :class:`property` - with :func:`abc.abstractmethod` instead. - * :class:`abc.abstractclassmethod` has been deprecated, use - :class:`classmethod` with :func:`abc.abstractmethod` instead. - * :class:`abc.abstractstaticmethod` has been deprecated, use - :class:`staticmethod` with :func:`abc.abstractmethod` instead. +* :class:`abc.abstractproperty` has been deprecated, use :class:`property` + with :func:`abc.abstractmethod` instead. +* :class:`abc.abstractclassmethod` has been deprecated, use + :class:`classmethod` with :func:`abc.abstractmethod` instead. +* :class:`abc.abstractstaticmethod` has been deprecated, use + :class:`staticmethod` with :func:`abc.abstractmethod` instead. (Contributed by Darren Dale in :issue:`11610`.) @@ -1059,32 +1059,32 @@ function to the :mod:`crypt` module. curses ------ - * If the :mod:`curses` module is linked to the ncursesw library, use Unicode - functions when Unicode strings or characters are passed (e.g. - :c:func:`waddwstr`), and bytes functions otherwise (e.g. :c:func:`waddstr`). - * Use the locale encoding instead of ``utf-8`` to encode Unicode strings. - * :class:`curses.window` has a new :attr:`curses.window.encoding` attribute. - * The :class:`curses.window` class has a new :meth:`~curses.window.get_wch` - method to get a wide character - * The :mod:`curses` module has a new :meth:`~curses.unget_wch` function to - push a wide character so the next :meth:`~curses.window.get_wch` will return - it +* If the :mod:`curses` module is linked to the ncursesw library, use Unicode + functions when Unicode strings or characters are passed (e.g. + :c:func:`waddwstr`), and bytes functions otherwise (e.g. :c:func:`waddstr`). +* Use the locale encoding instead of ``utf-8`` to encode Unicode strings. +* :class:`curses.window` has a new :attr:`curses.window.encoding` attribute. +* The :class:`curses.window` class has a new :meth:`~curses.window.get_wch` + method to get a wide character +* The :mod:`curses` module has a new :meth:`~curses.unget_wch` function to + push a wide character so the next :meth:`~curses.window.get_wch` will return + it (Contributed by Iñigo Serna in :issue:`6755`.) datetime -------- - * Equality comparisons between naive and aware :class:`~datetime.datetime` - instances now return :const:`False` instead of raising :exc:`TypeError` - (:issue:`15006`). - * New :meth:`datetime.datetime.timestamp` method: Return POSIX timestamp - corresponding to the :class:`~datetime.datetime` instance. - * The :meth:`datetime.datetime.strftime` method supports formatting years - older than 1000. - * The :meth:`datetime.datetime.astimezone` method can now be - called without arguments to convert datetime instance to the system - timezone. +* Equality comparisons between naive and aware :class:`~datetime.datetime` + instances now return :const:`False` instead of raising :exc:`TypeError` + (:issue:`15006`). +* New :meth:`datetime.datetime.timestamp` method: Return POSIX timestamp + corresponding to the :class:`~datetime.datetime` instance. +* The :meth:`datetime.datetime.strftime` method supports formatting years + older than 1000. +* The :meth:`datetime.datetime.astimezone` method can now be + called without arguments to convert datetime instance to the system + timezone. .. _new-decimal: @@ -1209,25 +1209,25 @@ the ``Message`` object it is serializing. The default policy is The minimum set of controls implemented by all ``policy`` objects are: - .. tabularcolumns:: |l|L| +.. tabularcolumns:: |l|L| - =============== ======================================================= - max_line_length The maximum length, excluding the linesep character(s), - individual lines may have when a ``Message`` is - serialized. Defaults to 78. +=============== ======================================================= +max_line_length The maximum length, excluding the linesep character(s), + individual lines may have when a ``Message`` is + serialized. Defaults to 78. - linesep The character used to separate individual lines when a - ``Message`` is serialized. Defaults to ``\n``. +linesep The character used to separate individual lines when a + ``Message`` is serialized. Defaults to ``\n``. - cte_type ``7bit`` or ``8bit``. ``8bit`` applies only to a - ``Bytes`` ``generator``, and means that non-ASCII may - be used where allowed by the protocol (or where it - exists in the original input). +cte_type ``7bit`` or ``8bit``. ``8bit`` applies only to a + ``Bytes`` ``generator``, and means that non-ASCII may + be used where allowed by the protocol (or where it + exists in the original input). - raise_on_defect Causes a ``parser`` to raise error when defects are - encountered instead of adding them to the ``Message`` - object's ``defects`` list. - =============== ======================================================= +raise_on_defect Causes a ``parser`` to raise error when defects are + encountered instead of adding them to the ``Message`` + object's ``defects`` list. +=============== ======================================================= A new policy instance, with new settings, is created using the :meth:`~email.policy.Policy.clone` method of policy objects. ``clone`` takes @@ -1262,21 +1262,21 @@ removal of the code) may occur if deemed necessary by the core developers. The new policies are instances of :class:`~email.policy.EmailPolicy`, and add the following additional controls: - .. tabularcolumns:: |l|L| +.. tabularcolumns:: |l|L| - =============== ======================================================= - refold_source Controls whether or not headers parsed by a - :mod:`~email.parser` are refolded by the - :mod:`~email.generator`. It can be ``none``, ``long``, - or ``all``. The default is ``long``, which means that - source headers with a line longer than - ``max_line_length`` get refolded. ``none`` means no - line get refolded, and ``all`` means that all lines - get refolded. +=============== ======================================================= +refold_source Controls whether or not headers parsed by a + :mod:`~email.parser` are refolded by the + :mod:`~email.generator`. It can be ``none``, ``long``, + or ``all``. The default is ``long``, which means that + source headers with a line longer than + ``max_line_length`` get refolded. ``none`` means no + line get refolded, and ``all`` means that all lines + get refolded. - header_factory A callable that take a ``name`` and ``value`` and - produces a custom header object. - =============== ======================================================= +header_factory A callable that take a ``name`` and ``value`` and + produces a custom header object. +=============== ======================================================= The ``header_factory`` is the key to the new features provided by the new policies. When one of the new policies is used, any header retrieved from @@ -1351,18 +1351,18 @@ API. New utility functions: - * :func:`~email.utils.format_datetime`: given a :class:`~datetime.datetime`, - produce a string formatted for use in an email header. +* :func:`~email.utils.format_datetime`: given a :class:`~datetime.datetime`, + produce a string formatted for use in an email header. - * :func:`~email.utils.parsedate_to_datetime`: given a date string from - an email header, convert it into an aware :class:`~datetime.datetime`, - or a naive :class:`~datetime.datetime` if the offset is ``-0000``. +* :func:`~email.utils.parsedate_to_datetime`: given a date string from + an email header, convert it into an aware :class:`~datetime.datetime`, + or a naive :class:`~datetime.datetime` if the offset is ``-0000``. - * :func:`~email.utils.localtime`: With no argument, returns the - current local time as an aware :class:`~datetime.datetime` using the local - :class:`~datetime.timezone`. Given an aware :class:`~datetime.datetime`, - converts it into an aware :class:`~datetime.datetime` using the - local :class:`~datetime.timezone`. +* :func:`~email.utils.localtime`: With no argument, returns the + current local time as an aware :class:`~datetime.datetime` using the local + :class:`~datetime.timezone`. Given an aware :class:`~datetime.datetime`, + converts it into an aware :class:`~datetime.datetime` using the + local :class:`~datetime.timezone`. ftplib diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index a3fd6321f3124e..48d712623e05dd 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -1580,13 +1580,13 @@ The initialization of the default warnings filters has changed as follows: * warnings filters enabled via the command line or the environment now have the following order of precedence: - * the ``BytesWarning`` filter for :option:`-b` (or ``-bb``) - * any filters specified with the :option:`-W` option - * any filters specified with the :envvar:`PYTHONWARNINGS` environment - variable - * any other CPython specific filters (e.g. the ``default`` filter added - for the new ``-X dev`` mode) - * any implicit filters defined directly by the warnings machinery + * the ``BytesWarning`` filter for :option:`-b` (or ``-bb``) + * any filters specified with the :option:`-W` option + * any filters specified with the :envvar:`PYTHONWARNINGS` environment + variable + * any other CPython specific filters (e.g. the ``default`` filter added + for the new ``-X dev`` mode) + * any implicit filters defined directly by the warnings machinery * in :ref:`CPython debug builds `, all warnings are now displayed by default (the implicit filter list is empty) From 79b81d1825cde3e051aa43d9e27edfbaeda84fda Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 11 Oct 2023 13:13:06 +0200 Subject: [PATCH 460/632] [3.11] gh-76106: Remove the cleanup lock in test_socket (GH-110539) (GH-110700) It does not already work (because it locks only addCleanup(), not doCleanups()), and it is no longer needed since the clean up procedure waits for all test threads to join. (cherry picked from commit f27b83090701b9c215e0d65f1f924fb9330cb649) Co-authored-by: Serhiy Storchaka --- Lib/test/test_socket.py | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index bc93d9988eaf03..3fceac340a461c 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -201,26 +201,6 @@ def setUp(self): self.serv = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDPLITE) self.port = socket_helper.bind_port(self.serv) -class ThreadSafeCleanupTestCase: - """Subclass of unittest.TestCase with thread-safe cleanup methods. - - This subclass protects the addCleanup() method with a recursive lock. - - doCleanups() is called when the server completed, but the client can still - be running in its thread especially if the server failed with a timeout. - Don't put a lock on doCleanups() to prevent deadlock between addCleanup() - called in the client and doCleanups() waiting for self.done.wait of - ThreadableTest._setUp() (gh-110167) - """ - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self._cleanup_lock = threading.RLock() - - def addCleanup(self, *args, **kwargs): - with self._cleanup_lock: - return super().addCleanup(*args, **kwargs) - class SocketCANTest(unittest.TestCase): @@ -613,8 +593,7 @@ def setUp(self): self.serv.listen() -class ThreadedSocketTestMixin(ThreadSafeCleanupTestCase, SocketTestBase, - ThreadableTest): +class ThreadedSocketTestMixin(SocketTestBase, ThreadableTest): """Mixin to add client socket and allow client/server tests. Client socket is self.cli and its address is self.cli_addr. See @@ -2698,7 +2677,7 @@ def _testRecvFromNegative(self): # here assumes that datagram delivery on the local machine will be # reliable. -class SendrecvmsgBase(ThreadSafeCleanupTestCase): +class SendrecvmsgBase: # Base class for sendmsg()/recvmsg() tests. # Time in seconds to wait before considering a test failed, or @@ -4564,7 +4543,6 @@ def testInterruptedRecvmsgIntoTimeout(self): @unittest.skipUnless(hasattr(signal, "alarm") or hasattr(signal, "setitimer"), "Don't have signal.alarm or signal.setitimer") class InterruptedSendTimeoutTest(InterruptedTimeoutBase, - ThreadSafeCleanupTestCase, SocketListeningTestMixin, TCPTestBase): # Test interrupting the interruptible send*() methods with signals # when a timeout is set. From ad504156372932922409b75d051a755008e40fb6 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 11 Oct 2023 21:42:24 +0200 Subject: [PATCH 461/632] [3.11] gh-99834: Update macOS installer to Tcl/Tk 8.6.13. (GH-110710) (cherry picked from commit 13e460086b007691f2ca1c5ff677cdb70d19eba8) Co-authored-by: Ned Deily --- Mac/BuildScript/build-installer.py | 6 +++--- .../macOS/2023-05-21-23-54-52.gh-issue-99834.6ANPts.rst | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/macOS/2023-05-21-23-54-52.gh-issue-99834.6ANPts.rst diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index 553926852a7ca8..f3728e3259e514 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -264,10 +264,10 @@ def library_recipes(): tk_patches = ['tk868_on_10_8_10_9.patch'] else: - tcl_tk_ver='8.6.12' - tcl_checksum='87ea890821d2221f2ab5157bc5eb885f' + tcl_tk_ver='8.6.13' + tcl_checksum='43a1fae7412f61ff11de2cfd05d28cfc3a73762f354a417c62370a54e2caf066' - tk_checksum='1d6dcf6120356e3d211e056dff5e462a' + tk_checksum='2e65fa069a23365440a3c56c556b8673b5e32a283800d8d9b257e3f584ce0675' tk_patches = [ ] diff --git a/Misc/NEWS.d/next/macOS/2023-05-21-23-54-52.gh-issue-99834.6ANPts.rst b/Misc/NEWS.d/next/macOS/2023-05-21-23-54-52.gh-issue-99834.6ANPts.rst new file mode 100644 index 00000000000000..aeb64d25b09add --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2023-05-21-23-54-52.gh-issue-99834.6ANPts.rst @@ -0,0 +1 @@ +Update macOS installer to Tcl/Tk 8.6.13. From fd061a9bbe5b46c295ebe0d76b4411d0345efc7d Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 11 Oct 2023 22:48:03 +0200 Subject: [PATCH 462/632] [3.11] gh-108826: Document `dis` module CLI and rename `_test` function to `main` (#108827) (#110689) Co-authored-by: Hugo van Kemenade Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Radislav Chugunov <52372310+chgnrdv@users.noreply.github.com> --- Doc/library/asyncio.rst | 2 + Doc/library/cmdline.rst | 55 +++++++++++++++++++ Doc/library/compileall.rst | 2 + Doc/library/dis.rst | 26 +++++++++ Doc/library/gzip.rst | 2 + Doc/library/index.rst | 1 + Doc/library/pickletools.rst | 2 + Doc/library/profile.rst | 2 + Doc/library/py_compile.rst | 1 + Doc/library/sysconfig.rst | 1 + Lib/dis.py | 5 +- ...-09-03-13-43-49.gh-issue-108826.KG7abS.rst | 1 + 12 files changed, 97 insertions(+), 3 deletions(-) create mode 100644 Doc/library/cmdline.rst create mode 100644 Misc/NEWS.d/next/Documentation/2023-09-03-13-43-49.gh-issue-108826.KG7abS.rst diff --git a/Doc/library/asyncio.rst b/Doc/library/asyncio.rst index c6a046f534e9a1..c75ab47404c1e4 100644 --- a/Doc/library/asyncio.rst +++ b/Doc/library/asyncio.rst @@ -56,6 +56,8 @@ Additionally, there are **low-level** APIs for * :ref:`bridge ` callback-based libraries and code with async/await syntax. +.. _asyncio-cli: + You can experiment with an ``asyncio`` concurrent context in the REPL: .. code-block:: pycon diff --git a/Doc/library/cmdline.rst b/Doc/library/cmdline.rst new file mode 100644 index 00000000000000..af137b8ec34c09 --- /dev/null +++ b/Doc/library/cmdline.rst @@ -0,0 +1,55 @@ +++++++++++++++++++++++++++++++++++++ +Modules command-line interface (CLI) +++++++++++++++++++++++++++++++++++++ + +The following modules have a command-line interface. + +* :ref:`ast ` +* :ref:`asyncio ` +* :mod:`base64` +* :ref:`calendar ` +* :mod:`code` +* :ref:`compileall ` +* :mod:`cProfile`: see :ref:`profile ` +* :ref:`difflib ` +* :ref:`dis ` +* :mod:`doctest` +* :mod:`!encodings.rot_13` +* :mod:`ensurepip` +* :mod:`filecmp` +* :mod:`fileinput` +* :mod:`ftplib` +* :ref:`gzip ` +* :ref:`http.server ` +* :mod:`!idlelib` +* :ref:`inspect ` +* :ref:`json.tool ` +* :mod:`mimetypes` +* :mod:`pdb` +* :mod:`pickle` +* :ref:`pickletools ` +* :mod:`platform` +* :mod:`poplib` +* :ref:`profile ` +* :mod:`pstats` +* :ref:`py_compile ` +* :mod:`pyclbr` +* :mod:`pydoc` +* :mod:`quopri` +* :mod:`runpy` +* :ref:`site ` +* :ref:`sysconfig ` +* :mod:`tabnanny` +* :ref:`tarfile ` +* :mod:`!this` +* :ref:`timeit ` +* :ref:`tokenize ` +* :ref:`trace ` +* :mod:`turtledemo` +* :ref:`unittest ` +* :mod:`venv` +* :mod:`webbrowser` +* :ref:`zipapp ` +* :ref:`zipfile ` + +See also the :ref:`Python command-line interface `. diff --git a/Doc/library/compileall.rst b/Doc/library/compileall.rst index a1482c9eb889e8..6d16734ddca21e 100644 --- a/Doc/library/compileall.rst +++ b/Doc/library/compileall.rst @@ -16,6 +16,8 @@ have write permission to the library directories. .. include:: ../includes/wasm-notavail.rst +.. _compileall-cli: + Command-line use ---------------- diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index f56784e769a3ee..a12f5937cd379e 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -64,6 +64,32 @@ the following command can be used to display the disassembly of (The "2" is a line number). +.. _dis-cli: + +Command-line interface +---------------------- + +The :mod:`dis` module can be invoked as a script from the command line: + +.. code-block:: sh + + python -m dis [-h] [-C] [infile] + +The following options are accepted: + +.. program:: dis + +.. cmdoption:: -h, --help + + Display usage and exit. + +.. cmdoption:: -C, --show-caches + + Show inline caches. + +If :file:`infile` is specified, its disassembled code will be written to stdout. +Otherwise, disassembly is performed on compiled source code recieved from stdin. + Bytecode analysis ----------------- diff --git a/Doc/library/gzip.rst b/Doc/library/gzip.rst index 4d2fa3d4012a1d..fdb546236aeadc 100644 --- a/Doc/library/gzip.rst +++ b/Doc/library/gzip.rst @@ -246,6 +246,8 @@ Example of how to GZIP compress a binary string:: .. program:: gzip +.. _gzip-cli: + Command Line Interface ---------------------- diff --git a/Doc/library/index.rst b/Doc/library/index.rst index d064b680f9aaa4..0b348ae6f5c8c0 100644 --- a/Doc/library/index.rst +++ b/Doc/library/index.rst @@ -73,5 +73,6 @@ the `Python Package Index `_. language.rst windows.rst unix.rst + cmdline.rst superseded.rst security_warnings.rst diff --git a/Doc/library/pickletools.rst b/Doc/library/pickletools.rst index c6ff4e6ed3d3b5..41930f8cbe8412 100644 --- a/Doc/library/pickletools.rst +++ b/Doc/library/pickletools.rst @@ -17,6 +17,8 @@ are useful for Python core developers who are working on the :mod:`pickle`; ordinary users of the :mod:`pickle` module probably won't find the :mod:`pickletools` module relevant. +.. _pickletools-cli: + Command line usage ------------------ diff --git a/Doc/library/profile.rst b/Doc/library/profile.rst index 723f927135a0f4..69274b0c354a25 100644 --- a/Doc/library/profile.rst +++ b/Doc/library/profile.rst @@ -121,6 +121,8 @@ results to a file by specifying a filename to the :func:`run` function:: The :class:`pstats.Stats` class reads profile results from a file and formats them in various ways. +.. _profile-cli: + The files :mod:`cProfile` and :mod:`profile` can also be invoked as a script to profile another script. For example:: diff --git a/Doc/library/py_compile.rst b/Doc/library/py_compile.rst index 7272f36d9a5568..38c416f9ad0305 100644 --- a/Doc/library/py_compile.rst +++ b/Doc/library/py_compile.rst @@ -125,6 +125,7 @@ byte-code cache files in the directory containing the source code. This option is useful when the ``.pycs`` are kept up to date by some system external to Python like a build system. +.. _py_compile-cli: Command-Line Interface ---------------------- diff --git a/Doc/library/sysconfig.rst b/Doc/library/sysconfig.rst index 17448f2a2abace..2de47cfd36a0f7 100644 --- a/Doc/library/sysconfig.rst +++ b/Doc/library/sysconfig.rst @@ -278,6 +278,7 @@ Other functions Return the path of :file:`Makefile`. +.. _sysconfig-cli: Using :mod:`sysconfig` as a script ---------------------------------- diff --git a/Lib/dis.py b/Lib/dis.py index 5bf52c3e6d3ac3..196c886f785082 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -759,8 +759,7 @@ def dis(self): return output.getvalue() -def _test(): - """Simple test program to disassemble a file.""" +def main(): import argparse parser = argparse.ArgumentParser() @@ -772,4 +771,4 @@ def _test(): dis(code) if __name__ == "__main__": - _test() + main() diff --git a/Misc/NEWS.d/next/Documentation/2023-09-03-13-43-49.gh-issue-108826.KG7abS.rst b/Misc/NEWS.d/next/Documentation/2023-09-03-13-43-49.gh-issue-108826.KG7abS.rst new file mode 100644 index 00000000000000..139b8f3457930c --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2023-09-03-13-43-49.gh-issue-108826.KG7abS.rst @@ -0,0 +1 @@ +:mod:`dis` module command-line interface is now mentioned in documentation. From 07471cda29fbd5cba90f5f9a8b221e43d8247bf0 Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Wed, 11 Oct 2023 23:11:41 +0200 Subject: [PATCH 463/632] [3.11] gh-110631: Fix reST indentation in `Doc/library` (GH-110685) (#110737) * [3.11] gh-110631: Fix reST indentation in `Doc/library` (GH-110685) Fix wrong indentation in the Doc/library dir.. (cherry picked from commit bb7923f556537a463c403dc1097726d8a8e1a6f2) Co-authored-by: Ezio Melotti * Fix merge glitch. --- Doc/library/__main__.rst | 46 +- Doc/library/_thread.rst | 2 +- Doc/library/binascii.rst | 9 +- Doc/library/collections.rst | 78 +-- Doc/library/concurrent.futures.rst | 270 +++++----- Doc/library/ctypes.rst | 84 +-- Doc/library/curses.rst | 6 +- Doc/library/dialog.rst | 12 +- Doc/library/email.contentmanager.rst | 50 +- Doc/library/email.policy.rst | 18 +- Doc/library/enum.rst | 28 +- Doc/library/functions.rst | 30 +- Doc/library/graphlib.rst | 16 +- Doc/library/idle.rst | 22 +- Doc/library/inspect.rst | 18 +- Doc/library/io.rst | 10 +- Doc/library/logging.config.rst | 14 +- Doc/library/lzma.rst | 51 +- Doc/library/multiprocessing.rst | 28 +- Doc/library/numbers.rst | 34 +- Doc/library/profile.rst | 8 +- Doc/library/re.rst | 2 +- Doc/library/shelve.rst | 6 +- Doc/library/socket.rst | 4 +- Doc/library/sqlite3.rst | 24 +- Doc/library/ssl.rst | 24 +- Doc/library/stdtypes.rst | 6 +- Doc/library/string.rst | 100 ++-- Doc/library/subprocess.rst | 28 +- Doc/library/tkinter.rst | 36 +- Doc/library/tkinter.ttk.rst | 748 +++++++++++++-------------- Doc/library/unittest.mock.rst | 10 +- Doc/library/urllib.request.rst | 12 +- 33 files changed, 920 insertions(+), 914 deletions(-) diff --git a/Doc/library/__main__.rst b/Doc/library/__main__.rst index c35f67245c97ec..435310d525d754 100644 --- a/Doc/library/__main__.rst +++ b/Doc/library/__main__.rst @@ -54,45 +54,45 @@ The top-level code environment can be: * the scope of an interactive prompt:: - >>> __name__ - '__main__' + >>> __name__ + '__main__' * the Python module passed to the Python interpreter as a file argument: - .. code-block:: shell-session + .. code-block:: shell-session - $ python3 helloworld.py - Hello, world! + $ python3 helloworld.py + Hello, world! * the Python module or package passed to the Python interpreter with the :option:`-m` argument: - .. code-block:: shell-session + .. code-block:: shell-session - $ python3 -m tarfile - usage: tarfile.py [-h] [-v] (...) + $ python3 -m tarfile + usage: tarfile.py [-h] [-v] (...) * Python code read by the Python interpreter from standard input: - .. code-block:: shell-session + .. code-block:: shell-session - $ echo "import this" | python3 - The Zen of Python, by Tim Peters + $ echo "import this" | python3 + The Zen of Python, by Tim Peters - Beautiful is better than ugly. - Explicit is better than implicit. - ... + Beautiful is better than ugly. + Explicit is better than implicit. + ... * Python code passed to the Python interpreter with the :option:`-c` argument: - .. code-block:: shell-session + .. code-block:: shell-session - $ python3 -c "import this" - The Zen of Python, by Tim Peters + $ python3 -c "import this" + The Zen of Python, by Tim Peters - Beautiful is better than ugly. - Explicit is better than implicit. - ... + Beautiful is better than ugly. + Explicit is better than implicit. + ... In each of these situations, the top-level module's ``__name__`` is set to ``'__main__'``. @@ -102,9 +102,9 @@ top-level environment by checking its own ``__name__``, which allows a common idiom for conditionally executing code when the module is not initialized from an import statement:: - if __name__ == '__main__': - # Execute when the module is not initialized from an import statement. - ... + if __name__ == '__main__': + # Execute when the module is not initialized from an import statement. + ... .. seealso:: diff --git a/Doc/library/_thread.rst b/Doc/library/_thread.rst index 4227a4fe1e33a2..5e506d2c37d6e0 100644 --- a/Doc/library/_thread.rst +++ b/Doc/library/_thread.rst @@ -206,7 +206,7 @@ In addition to these methods, lock objects can also be used via the **Caveats:** - .. index:: pair: module; signal +.. index:: pair: module; signal * Threads interact strangely with interrupts: the :exc:`KeyboardInterrupt` exception will be received by an arbitrary thread. (When the :mod:`signal` diff --git a/Doc/library/binascii.rst b/Doc/library/binascii.rst index 21960cb7972e6e..d065fa3506da11 100644 --- a/Doc/library/binascii.rst +++ b/Doc/library/binascii.rst @@ -58,10 +58,11 @@ The :mod:`binascii` module defines the following functions: data will raise :exc:`binascii.Error`. Valid base64: - * Conforms to :rfc:`3548`. - * Contains only characters from the base64 alphabet. - * Contains no excess data after padding (including excess padding, newlines, etc.). - * Does not start with a padding. + + * Conforms to :rfc:`3548`. + * Contains only characters from the base64 alphabet. + * Contains no excess data after padding (including excess padding, newlines, etc.). + * Does not start with a padding. .. versionchanged:: 3.11 Added the *strict_mode* parameter. diff --git a/Doc/library/collections.rst b/Doc/library/collections.rst index 285d362104119a..5ea3a5dd6254c7 100644 --- a/Doc/library/collections.rst +++ b/Doc/library/collections.rst @@ -120,26 +120,26 @@ The class can be used to simulate nested scopes and is useful in templating. .. seealso:: - * The `MultiContext class - `_ - in the Enthought `CodeTools package - `_ has options to support - writing to any mapping in the chain. + * The `MultiContext class + `_ + in the Enthought `CodeTools package + `_ has options to support + writing to any mapping in the chain. - * Django's `Context class - `_ - for templating is a read-only chain of mappings. It also features - pushing and popping of contexts similar to the - :meth:`~collections.ChainMap.new_child` method and the - :attr:`~collections.ChainMap.parents` property. + * Django's `Context class + `_ + for templating is a read-only chain of mappings. It also features + pushing and popping of contexts similar to the + :meth:`~collections.ChainMap.new_child` method and the + :attr:`~collections.ChainMap.parents` property. - * The `Nested Contexts recipe - `_ has options to control - whether writes and other mutations apply only to the first mapping or to - any mapping in the chain. + * The `Nested Contexts recipe + `_ has options to control + whether writes and other mutations apply only to the first mapping or to + any mapping in the chain. - * A `greatly simplified read-only version of Chainmap - `_. + * A `greatly simplified read-only version of Chainmap + `_. :class:`ChainMap` Examples and Recipes @@ -428,22 +428,22 @@ or subtracting from an empty counter. .. seealso:: - * `Bag class `_ - in Smalltalk. + * `Bag class `_ + in Smalltalk. - * Wikipedia entry for `Multisets `_. + * Wikipedia entry for `Multisets `_. - * `C++ multisets `_ - tutorial with examples. + * `C++ multisets `_ + tutorial with examples. - * For mathematical operations on multisets and their use cases, see - *Knuth, Donald. The Art of Computer Programming Volume II, - Section 4.6.3, Exercise 19*. + * For mathematical operations on multisets and their use cases, see + *Knuth, Donald. The Art of Computer Programming Volume II, + Section 4.6.3, Exercise 19*. - * To enumerate all distinct multisets of a given size over a given set of - elements, see :func:`itertools.combinations_with_replacement`:: + * To enumerate all distinct multisets of a given size over a given set of + elements, see :func:`itertools.combinations_with_replacement`:: - map(Counter, combinations_with_replacement('ABC', 2)) # --> AA AB AC BB BC CC + map(Counter, combinations_with_replacement('ABC', 2)) # --> AA AB AC BB BC CC :class:`deque` objects @@ -1058,20 +1058,20 @@ fields: .. seealso:: - * See :class:`typing.NamedTuple` for a way to add type hints for named - tuples. It also provides an elegant notation using the :keyword:`class` - keyword:: + * See :class:`typing.NamedTuple` for a way to add type hints for named + tuples. It also provides an elegant notation using the :keyword:`class` + keyword:: - class Component(NamedTuple): - part_number: int - weight: float - description: Optional[str] = None + class Component(NamedTuple): + part_number: int + weight: float + description: Optional[str] = None - * See :meth:`types.SimpleNamespace` for a mutable namespace based on an - underlying dictionary instead of a tuple. + * See :meth:`types.SimpleNamespace` for a mutable namespace based on an + underlying dictionary instead of a tuple. - * The :mod:`dataclasses` module provides a decorator and functions for - automatically adding generated special methods to user-defined classes. + * The :mod:`dataclasses` module provides a decorator and functions for + automatically adding generated special methods to user-defined classes. :class:`OrderedDict` objects diff --git a/Doc/library/concurrent.futures.rst b/Doc/library/concurrent.futures.rst index c2c64d9c6ee769..8ab2c3a2be2d65 100644 --- a/Doc/library/concurrent.futures.rst +++ b/Doc/library/concurrent.futures.rst @@ -29,83 +29,83 @@ Executor Objects An abstract class that provides methods to execute calls asynchronously. It should not be used directly, but through its concrete subclasses. - .. method:: submit(fn, /, *args, **kwargs) + .. method:: submit(fn, /, *args, **kwargs) - Schedules the callable, *fn*, to be executed as ``fn(*args, **kwargs)`` - and returns a :class:`Future` object representing the execution of the - callable. :: + Schedules the callable, *fn*, to be executed as ``fn(*args, **kwargs)`` + and returns a :class:`Future` object representing the execution of the + callable. :: - with ThreadPoolExecutor(max_workers=1) as executor: - future = executor.submit(pow, 323, 1235) - print(future.result()) + with ThreadPoolExecutor(max_workers=1) as executor: + future = executor.submit(pow, 323, 1235) + print(future.result()) - .. method:: map(func, *iterables, timeout=None, chunksize=1) + .. method:: map(func, *iterables, timeout=None, chunksize=1) - Similar to :func:`map(func, *iterables) ` except: + Similar to :func:`map(func, *iterables) ` except: - * the *iterables* are collected immediately rather than lazily; + * the *iterables* are collected immediately rather than lazily; - * *func* is executed asynchronously and several calls to - *func* may be made concurrently. + * *func* is executed asynchronously and several calls to + *func* may be made concurrently. - The returned iterator raises a :exc:`TimeoutError` - if :meth:`~iterator.__next__` is called and the result isn't available - after *timeout* seconds from the original call to :meth:`Executor.map`. - *timeout* can be an int or a float. If *timeout* is not specified or - ``None``, there is no limit to the wait time. + The returned iterator raises a :exc:`TimeoutError` + if :meth:`~iterator.__next__` is called and the result isn't available + after *timeout* seconds from the original call to :meth:`Executor.map`. + *timeout* can be an int or a float. If *timeout* is not specified or + ``None``, there is no limit to the wait time. - If a *func* call raises an exception, then that exception will be - raised when its value is retrieved from the iterator. + If a *func* call raises an exception, then that exception will be + raised when its value is retrieved from the iterator. - When using :class:`ProcessPoolExecutor`, this method chops *iterables* - into a number of chunks which it submits to the pool as separate - tasks. The (approximate) size of these chunks can be specified by - setting *chunksize* to a positive integer. For very long iterables, - using a large value for *chunksize* can significantly improve - performance compared to the default size of 1. With - :class:`ThreadPoolExecutor`, *chunksize* has no effect. + When using :class:`ProcessPoolExecutor`, this method chops *iterables* + into a number of chunks which it submits to the pool as separate + tasks. The (approximate) size of these chunks can be specified by + setting *chunksize* to a positive integer. For very long iterables, + using a large value for *chunksize* can significantly improve + performance compared to the default size of 1. With + :class:`ThreadPoolExecutor`, *chunksize* has no effect. - .. versionchanged:: 3.5 - Added the *chunksize* argument. + .. versionchanged:: 3.5 + Added the *chunksize* argument. - .. method:: shutdown(wait=True, *, cancel_futures=False) + .. method:: shutdown(wait=True, *, cancel_futures=False) - Signal the executor that it should free any resources that it is using - when the currently pending futures are done executing. Calls to - :meth:`Executor.submit` and :meth:`Executor.map` made after shutdown will - raise :exc:`RuntimeError`. + Signal the executor that it should free any resources that it is using + when the currently pending futures are done executing. Calls to + :meth:`Executor.submit` and :meth:`Executor.map` made after shutdown will + raise :exc:`RuntimeError`. - If *wait* is ``True`` then this method will not return until all the - pending futures are done executing and the resources associated with the - executor have been freed. If *wait* is ``False`` then this method will - return immediately and the resources associated with the executor will be - freed when all pending futures are done executing. Regardless of the - value of *wait*, the entire Python program will not exit until all - pending futures are done executing. + If *wait* is ``True`` then this method will not return until all the + pending futures are done executing and the resources associated with the + executor have been freed. If *wait* is ``False`` then this method will + return immediately and the resources associated with the executor will be + freed when all pending futures are done executing. Regardless of the + value of *wait*, the entire Python program will not exit until all + pending futures are done executing. - If *cancel_futures* is ``True``, this method will cancel all pending - futures that the executor has not started running. Any futures that - are completed or running won't be cancelled, regardless of the value - of *cancel_futures*. + If *cancel_futures* is ``True``, this method will cancel all pending + futures that the executor has not started running. Any futures that + are completed or running won't be cancelled, regardless of the value + of *cancel_futures*. - If both *cancel_futures* and *wait* are ``True``, all futures that the - executor has started running will be completed prior to this method - returning. The remaining futures are cancelled. + If both *cancel_futures* and *wait* are ``True``, all futures that the + executor has started running will be completed prior to this method + returning. The remaining futures are cancelled. - You can avoid having to call this method explicitly if you use the - :keyword:`with` statement, which will shutdown the :class:`Executor` - (waiting as if :meth:`Executor.shutdown` were called with *wait* set to - ``True``):: + You can avoid having to call this method explicitly if you use the + :keyword:`with` statement, which will shutdown the :class:`Executor` + (waiting as if :meth:`Executor.shutdown` were called with *wait* set to + ``True``):: - import shutil - with ThreadPoolExecutor(max_workers=4) as e: - e.submit(shutil.copy, 'src1.txt', 'dest1.txt') - e.submit(shutil.copy, 'src2.txt', 'dest2.txt') - e.submit(shutil.copy, 'src3.txt', 'dest3.txt') - e.submit(shutil.copy, 'src4.txt', 'dest4.txt') + import shutil + with ThreadPoolExecutor(max_workers=4) as e: + e.submit(shutil.copy, 'src1.txt', 'dest1.txt') + e.submit(shutil.copy, 'src2.txt', 'dest2.txt') + e.submit(shutil.copy, 'src3.txt', 'dest3.txt') + e.submit(shutil.copy, 'src4.txt', 'dest4.txt') - .. versionchanged:: 3.9 - Added *cancel_futures*. + .. versionchanged:: 3.9 + Added *cancel_futures*. ThreadPoolExecutor @@ -337,117 +337,117 @@ The :class:`Future` class encapsulates the asynchronous execution of a callable. instances are created by :meth:`Executor.submit` and should not be created directly except for testing. - .. method:: cancel() + .. method:: cancel() - Attempt to cancel the call. If the call is currently being executed or - finished running and cannot be cancelled then the method will return - ``False``, otherwise the call will be cancelled and the method will - return ``True``. + Attempt to cancel the call. If the call is currently being executed or + finished running and cannot be cancelled then the method will return + ``False``, otherwise the call will be cancelled and the method will + return ``True``. - .. method:: cancelled() + .. method:: cancelled() - Return ``True`` if the call was successfully cancelled. + Return ``True`` if the call was successfully cancelled. - .. method:: running() + .. method:: running() - Return ``True`` if the call is currently being executed and cannot be - cancelled. + Return ``True`` if the call is currently being executed and cannot be + cancelled. - .. method:: done() + .. method:: done() - Return ``True`` if the call was successfully cancelled or finished - running. + Return ``True`` if the call was successfully cancelled or finished + running. - .. method:: result(timeout=None) + .. method:: result(timeout=None) - Return the value returned by the call. If the call hasn't yet completed - then this method will wait up to *timeout* seconds. If the call hasn't - completed in *timeout* seconds, then a - :exc:`TimeoutError` will be raised. *timeout* can be - an int or float. If *timeout* is not specified or ``None``, there is no - limit to the wait time. + Return the value returned by the call. If the call hasn't yet completed + then this method will wait up to *timeout* seconds. If the call hasn't + completed in *timeout* seconds, then a + :exc:`TimeoutError` will be raised. *timeout* can be + an int or float. If *timeout* is not specified or ``None``, there is no + limit to the wait time. - If the future is cancelled before completing then :exc:`.CancelledError` - will be raised. + If the future is cancelled before completing then :exc:`.CancelledError` + will be raised. - If the call raised an exception, this method will raise the same exception. + If the call raised an exception, this method will raise the same exception. - .. method:: exception(timeout=None) + .. method:: exception(timeout=None) - Return the exception raised by the call. If the call hasn't yet - completed then this method will wait up to *timeout* seconds. If the - call hasn't completed in *timeout* seconds, then a - :exc:`TimeoutError` will be raised. *timeout* can be - an int or float. If *timeout* is not specified or ``None``, there is no - limit to the wait time. + Return the exception raised by the call. If the call hasn't yet + completed then this method will wait up to *timeout* seconds. If the + call hasn't completed in *timeout* seconds, then a + :exc:`TimeoutError` will be raised. *timeout* can be + an int or float. If *timeout* is not specified or ``None``, there is no + limit to the wait time. - If the future is cancelled before completing then :exc:`.CancelledError` - will be raised. + If the future is cancelled before completing then :exc:`.CancelledError` + will be raised. - If the call completed without raising, ``None`` is returned. + If the call completed without raising, ``None`` is returned. - .. method:: add_done_callback(fn) + .. method:: add_done_callback(fn) - Attaches the callable *fn* to the future. *fn* will be called, with the - future as its only argument, when the future is cancelled or finishes - running. + Attaches the callable *fn* to the future. *fn* will be called, with the + future as its only argument, when the future is cancelled or finishes + running. - Added callables are called in the order that they were added and are - always called in a thread belonging to the process that added them. If - the callable raises an :exc:`Exception` subclass, it will be logged and - ignored. If the callable raises a :exc:`BaseException` subclass, the - behavior is undefined. + Added callables are called in the order that they were added and are + always called in a thread belonging to the process that added them. If + the callable raises an :exc:`Exception` subclass, it will be logged and + ignored. If the callable raises a :exc:`BaseException` subclass, the + behavior is undefined. - If the future has already completed or been cancelled, *fn* will be - called immediately. + If the future has already completed or been cancelled, *fn* will be + called immediately. The following :class:`Future` methods are meant for use in unit tests and :class:`Executor` implementations. - .. method:: set_running_or_notify_cancel() + .. method:: set_running_or_notify_cancel() - This method should only be called by :class:`Executor` implementations - before executing the work associated with the :class:`Future` and by unit - tests. + This method should only be called by :class:`Executor` implementations + before executing the work associated with the :class:`Future` and by unit + tests. - If the method returns ``False`` then the :class:`Future` was cancelled, - i.e. :meth:`Future.cancel` was called and returned ``True``. Any threads - waiting on the :class:`Future` completing (i.e. through - :func:`as_completed` or :func:`wait`) will be woken up. + If the method returns ``False`` then the :class:`Future` was cancelled, + i.e. :meth:`Future.cancel` was called and returned ``True``. Any threads + waiting on the :class:`Future` completing (i.e. through + :func:`as_completed` or :func:`wait`) will be woken up. - If the method returns ``True`` then the :class:`Future` was not cancelled - and has been put in the running state, i.e. calls to - :meth:`Future.running` will return ``True``. + If the method returns ``True`` then the :class:`Future` was not cancelled + and has been put in the running state, i.e. calls to + :meth:`Future.running` will return ``True``. - This method can only be called once and cannot be called after - :meth:`Future.set_result` or :meth:`Future.set_exception` have been - called. + This method can only be called once and cannot be called after + :meth:`Future.set_result` or :meth:`Future.set_exception` have been + called. - .. method:: set_result(result) + .. method:: set_result(result) - Sets the result of the work associated with the :class:`Future` to - *result*. + Sets the result of the work associated with the :class:`Future` to + *result*. - This method should only be used by :class:`Executor` implementations and - unit tests. + This method should only be used by :class:`Executor` implementations and + unit tests. - .. versionchanged:: 3.8 - This method raises - :exc:`concurrent.futures.InvalidStateError` if the :class:`Future` is - already done. + .. versionchanged:: 3.8 + This method raises + :exc:`concurrent.futures.InvalidStateError` if the :class:`Future` is + already done. - .. method:: set_exception(exception) + .. method:: set_exception(exception) - Sets the result of the work associated with the :class:`Future` to the - :class:`Exception` *exception*. + Sets the result of the work associated with the :class:`Future` to the + :class:`Exception` *exception*. - This method should only be used by :class:`Executor` implementations and - unit tests. + This method should only be used by :class:`Executor` implementations and + unit tests. - .. versionchanged:: 3.8 - This method raises - :exc:`concurrent.futures.InvalidStateError` if the :class:`Future` is - already done. + .. versionchanged:: 3.8 + This method raises + :exc:`concurrent.futures.InvalidStateError` if the :class:`Future` is + already done. Module Functions ---------------- diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 51cffc029017b2..0088775d2fd2a7 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -1697,70 +1697,70 @@ See :ref:`ctypes-callback-functions` for examples. Function prototypes created by these factory functions can be instantiated in different ways, depending on the type and number of the parameters in the call: +.. function:: prototype(address) + :noindex: + :module: - .. function:: prototype(address) - :noindex: - :module: + Returns a foreign function at the specified address which must be an integer. - Returns a foreign function at the specified address which must be an integer. +.. function:: prototype(callable) + :noindex: + :module: - .. function:: prototype(callable) - :noindex: - :module: + Create a C callable function (a callback function) from a Python *callable*. - Create a C callable function (a callback function) from a Python *callable*. +.. function:: prototype(func_spec[, paramflags]) + :noindex: + :module: - .. function:: prototype(func_spec[, paramflags]) - :noindex: - :module: + Returns a foreign function exported by a shared library. *func_spec* must + be a 2-tuple ``(name_or_ordinal, library)``. The first item is the name of + the exported function as string, or the ordinal of the exported function + as small integer. The second item is the shared library instance. - Returns a foreign function exported by a shared library. *func_spec* must - be a 2-tuple ``(name_or_ordinal, library)``. The first item is the name of - the exported function as string, or the ordinal of the exported function - as small integer. The second item is the shared library instance. +.. function:: prototype(vtbl_index, name[, paramflags[, iid]]) + :noindex: + :module: - .. function:: prototype(vtbl_index, name[, paramflags[, iid]]) - :noindex: - :module: + Returns a foreign function that will call a COM method. *vtbl_index* is + the index into the virtual function table, a small non-negative + integer. *name* is name of the COM method. *iid* is an optional pointer to + the interface identifier which is used in extended error reporting. - Returns a foreign function that will call a COM method. *vtbl_index* is - the index into the virtual function table, a small non-negative - integer. *name* is name of the COM method. *iid* is an optional pointer to - the interface identifier which is used in extended error reporting. + COM methods use a special calling convention: They require a pointer to + the COM interface as first argument, in addition to those parameters that + are specified in the :attr:`!argtypes` tuple. - COM methods use a special calling convention: They require a pointer to - the COM interface as first argument, in addition to those parameters that - are specified in the :attr:`argtypes` tuple. +The optional *paramflags* parameter creates foreign function wrappers with much +more functionality than the features described above. - The optional *paramflags* parameter creates foreign function wrappers with much - more functionality than the features described above. +*paramflags* must be a tuple of the same length as :attr:`~_FuncPtr.argtypes`. - *paramflags* must be a tuple of the same length as :attr:`argtypes`. +Each item in this tuple contains further information about a parameter, it must +be a tuple containing one, two, or three items. - Each item in this tuple contains further information about a parameter, it must - be a tuple containing one, two, or three items. +The first item is an integer containing a combination of direction +flags for the parameter: - The first item is an integer containing a combination of direction - flags for the parameter: + 1 + Specifies an input parameter to the function. - 1 - Specifies an input parameter to the function. + 2 + Output parameter. The foreign function fills in a value. - 2 - Output parameter. The foreign function fills in a value. + 4 + Input parameter which defaults to the integer zero. - 4 - Input parameter which defaults to the integer zero. +The optional second item is the parameter name as string. If this is specified, +the foreign function can be called with named parameters. - The optional second item is the parameter name as string. If this is specified, - the foreign function can be called with named parameters. +The optional third item is the default value for this parameter. - The optional third item is the default value for this parameter. -This example demonstrates how to wrap the Windows ``MessageBoxW`` function so +The following example demonstrates how to wrap the Windows ``MessageBoxW`` function so that it supports default parameters and named arguments. The C declaration from the windows header file is this:: diff --git a/Doc/library/curses.rst b/Doc/library/curses.rst index a17d528793eff9..0d743efd28efff 100644 --- a/Doc/library/curses.rst +++ b/Doc/library/curses.rst @@ -1774,9 +1774,9 @@ The following table lists mouse button constants used by :meth:`getmouse`: | .. data:: BUTTON_ALT | Control was down during button state change | +----------------------------------+---------------------------------------------+ - .. versionchanged:: 3.10 - The ``BUTTON5_*`` constants are now exposed if they are provided by the - underlying curses library. +.. versionchanged:: 3.10 + The ``BUTTON5_*`` constants are now exposed if they are provided by the + underlying curses library. The following table lists the predefined colors: diff --git a/Doc/library/dialog.rst b/Doc/library/dialog.rst index 53f98c1018988f..191e0da12103fa 100644 --- a/Doc/library/dialog.rst +++ b/Doc/library/dialog.rst @@ -27,15 +27,15 @@ functions for creating simple modal dialogs to get a value from the user. The base class for custom dialogs. - .. method:: body(master) + .. method:: body(master) - Override to construct the dialog's interface and return the widget that - should have initial focus. + Override to construct the dialog's interface and return the widget that + should have initial focus. - .. method:: buttonbox() + .. method:: buttonbox() - Default behaviour adds OK and Cancel buttons. Override for custom button - layouts. + Default behaviour adds OK and Cancel buttons. Override for custom button + layouts. diff --git a/Doc/library/email.contentmanager.rst b/Doc/library/email.contentmanager.rst index 918fc55677e723..5b49339650f0e9 100644 --- a/Doc/library/email.contentmanager.rst +++ b/Doc/library/email.contentmanager.rst @@ -32,9 +32,9 @@ To find the handler, look for the following keys in the registry, stopping with the first one found: - * the string representing the full MIME type (``maintype/subtype``) - * the string representing the ``maintype`` - * the empty string + * the string representing the full MIME type (``maintype/subtype``) + * the string representing the ``maintype`` + * the empty string If none of these keys produce a handler, raise a :exc:`KeyError` for the full MIME type. @@ -55,11 +55,11 @@ look for the following keys in the registry, stopping with the first one found: - * the type itself (``typ``) - * the type's fully qualified name (``typ.__module__ + '.' + - typ.__qualname__``). - * the type's qualname (``typ.__qualname__``) - * the type's name (``typ.__name__``). + * the type itself (``typ``) + * the type's fully qualified name (``typ.__module__ + '.' + + typ.__qualname__``). + * the type's qualname (``typ.__qualname__``) + * the type's name (``typ.__name__``). If none of the above match, repeat all of the checks above for each of the types in the :term:`MRO` (``typ.__mro__``). Finally, if no other key @@ -132,15 +132,15 @@ Currently the email package provides only one concrete content manager, Add a :mailheader:`Content-Type` header with a ``maintype/subtype`` value. - * For ``str``, set the MIME ``maintype`` to ``text``, and set the - subtype to *subtype* if it is specified, or ``plain`` if it is not. - * For ``bytes``, use the specified *maintype* and *subtype*, or - raise a :exc:`TypeError` if they are not specified. - * For :class:`~email.message.EmailMessage` objects, set the maintype - to ``message``, and set the subtype to *subtype* if it is - specified or ``rfc822`` if it is not. If *subtype* is - ``partial``, raise an error (``bytes`` objects must be used to - construct ``message/partial`` parts). + * For ``str``, set the MIME ``maintype`` to ``text``, and set the + subtype to *subtype* if it is specified, or ``plain`` if it is not. + * For ``bytes``, use the specified *maintype* and *subtype*, or + raise a :exc:`TypeError` if they are not specified. + * For :class:`~email.message.EmailMessage` objects, set the maintype + to ``message``, and set the subtype to *subtype* if it is + specified or ``rfc822`` if it is not. If *subtype* is + ``partial``, raise an error (``bytes`` objects must be used to + construct ``message/partial`` parts). If *charset* is provided (which is valid only for ``str``), encode the string to bytes using the specified character set. The default is @@ -155,14 +155,14 @@ Currently the email package provides only one concrete content manager, ``7bit`` for an input that contains non-ASCII values), raise a :exc:`ValueError`. - * For ``str`` objects, if *cte* is not set use heuristics to - determine the most compact encoding. - * For :class:`~email.message.EmailMessage`, per :rfc:`2046`, raise - an error if a *cte* of ``quoted-printable`` or ``base64`` is - requested for *subtype* ``rfc822``, and for any *cte* other than - ``7bit`` for *subtype* ``external-body``. For - ``message/rfc822``, use ``8bit`` if *cte* is not specified. For - all other values of *subtype*, use ``7bit``. + * For ``str`` objects, if *cte* is not set use heuristics to + determine the most compact encoding. + * For :class:`~email.message.EmailMessage`, per :rfc:`2046`, raise + an error if a *cte* of ``quoted-printable`` or ``base64`` is + requested for *subtype* ``rfc822``, and for any *cte* other than + ``7bit`` for *subtype* ``external-body``. For + ``message/rfc822``, use ``8bit`` if *cte* is not specified. For + all other values of *subtype*, use ``7bit``. .. note:: A *cte* of ``binary`` does not actually work correctly yet. The ``EmailMessage`` object as modified by ``set_content`` is diff --git a/Doc/library/email.policy.rst b/Doc/library/email.policy.rst index bf53b9520fc723..9b462fab119561 100644 --- a/Doc/library/email.policy.rst +++ b/Doc/library/email.policy.rst @@ -556,17 +556,17 @@ more closely to the RFCs relevant to their domains. With all of these :class:`EmailPolicies <.EmailPolicy>`, the effective API of the email package is changed from the Python 3.2 API in the following ways: - * Setting a header on a :class:`~email.message.Message` results in that - header being parsed and a header object created. +* Setting a header on a :class:`~email.message.Message` results in that + header being parsed and a header object created. - * Fetching a header value from a :class:`~email.message.Message` results - in that header being parsed and a header object created and - returned. +* Fetching a header value from a :class:`~email.message.Message` results + in that header being parsed and a header object created and + returned. - * Any header object, or any header that is refolded due to the - policy settings, is folded using an algorithm that fully implements the - RFC folding algorithms, including knowing where encoded words are required - and allowed. +* Any header object, or any header that is refolded due to the + policy settings, is folded using an algorithm that fully implements the + RFC folding algorithms, including knowing where encoded words are required + and allowed. From the application view, this means that any header obtained through the :class:`~email.message.EmailMessage` is a header object with extra diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index 85da33156b1e38..8fb97ea9ed1c84 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -593,8 +593,8 @@ Data Types If a *Flag* operation is performed with an *IntFlag* member and: - * the result is a valid *IntFlag*: an *IntFlag* is returned - * the result is not a valid *IntFlag*: the result depends on the *FlagBoundary* setting + * the result is a valid *IntFlag*: an *IntFlag* is returned + * the result is not a valid *IntFlag*: the result depends on the *FlagBoundary* setting The *repr()* of unnamed zero-valued flags has changed. It is now: @@ -621,8 +621,8 @@ Data Types :class:`!ReprEnum` uses the :meth:`repr() ` of :class:`Enum`, but the :class:`str() ` of the mixed-in data type: - * :meth:`!int.__str__` for :class:`IntEnum` and :class:`IntFlag` - * :meth:`!str.__str__` for :class:`StrEnum` + * :meth:`!int.__str__` for :class:`IntEnum` and :class:`IntFlag` + * :meth:`!str.__str__` for :class:`StrEnum` Inherit from :class:`!ReprEnum` to keep the :class:`str() ` / :func:`format` of the mixed-in data type instead of using the @@ -781,13 +781,13 @@ Supported ``_sunder_`` names - ``_generate_next_value_`` -- used 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 last 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, regardless of the last value seen. + For :class:`Flag` classes the next value chosen will be the next highest + power-of-two, regardless of the last value seen. .. versionadded:: 3.6 ``_missing_``, ``_order_``, ``_generate_next_value_`` .. versionadded:: 3.7 ``_ignore_`` @@ -809,11 +809,11 @@ Utilities and Decorators *auto* instances are only resolved when at the top level of an assignment: - * ``FIRST = auto()`` will work (auto() is replaced with ``1``); - * ``SECOND = auto(), -2`` will work (auto is replaced with ``2``, so ``2, -2`` is - used to create the ``SECOND`` enum member; - * ``THREE = [auto(), -3]`` will *not* work (``, -3`` is used to - create the ``THREE`` enum member) + * ``FIRST = auto()`` will work (auto() is replaced with ``1``); + * ``SECOND = auto(), -2`` will work (auto is replaced with ``2``, so ``2, -2`` is + used to create the ``SECOND`` enum member; + * ``THREE = [auto(), -3]`` will *not* work (``, -3`` is used to + create the ``THREE`` enum member) .. versionchanged:: 3.11.1 diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 96cf5940d432c9..5e5fd467058edf 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -1157,8 +1157,8 @@ are always available. They are listed here in alphabetical order. See also :func:`format` for more information. - .. index:: - single: file object; open() built-in function +.. index:: + single: file object; open() built-in function .. function:: open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None) @@ -1359,28 +1359,28 @@ are always available. They are listed here in alphabetical order. .. versionchanged:: 3.3 - * The *opener* parameter was added. - * The ``'x'`` mode was added. - * :exc:`IOError` used to be raised, it is now an alias of :exc:`OSError`. - * :exc:`FileExistsError` is now raised if the file opened in exclusive - creation mode (``'x'``) already exists. + * The *opener* parameter was added. + * The ``'x'`` mode was added. + * :exc:`IOError` used to be raised, it is now an alias of :exc:`OSError`. + * :exc:`FileExistsError` is now raised if the file opened in exclusive + creation mode (``'x'``) already exists. .. versionchanged:: 3.4 - * The file is now non-inheritable. + * The file is now non-inheritable. .. versionchanged:: 3.5 - * If the system call is interrupted and the signal handler does not raise an - exception, the function now retries the system call instead of raising an - :exc:`InterruptedError` exception (see :pep:`475` for the rationale). - * The ``'namereplace'`` error handler was added. + * If the system call is interrupted and the signal handler does not raise an + exception, the function now retries the system call instead of raising an + :exc:`InterruptedError` exception (see :pep:`475` for the rationale). + * The ``'namereplace'`` error handler was added. .. versionchanged:: 3.6 - * Support added to accept objects implementing :class:`os.PathLike`. - * On Windows, opening a console buffer may return a subclass of - :class:`io.RawIOBase` other than :class:`io.FileIO`. + * Support added to accept objects implementing :class:`os.PathLike`. + * On Windows, opening a console buffer may return a subclass of + :class:`io.RawIOBase` other than :class:`io.FileIO`. .. versionchanged:: 3.11 The ``'U'`` mode has been removed. diff --git a/Doc/library/graphlib.rst b/Doc/library/graphlib.rst index fdd8f39ef4e1c4..5414d6370b78ce 100644 --- a/Doc/library/graphlib.rst +++ b/Doc/library/graphlib.rst @@ -37,14 +37,14 @@ In the general case, the steps required to perform the sorting of a given graph are as follows: - * Create an instance of the :class:`TopologicalSorter` with an optional - initial graph. - * Add additional nodes to the graph. - * Call :meth:`~TopologicalSorter.prepare` on the graph. - * While :meth:`~TopologicalSorter.is_active` is ``True``, iterate over - the nodes returned by :meth:`~TopologicalSorter.get_ready` and - process them. Call :meth:`~TopologicalSorter.done` on each node as it - finishes processing. + * Create an instance of the :class:`TopologicalSorter` with an optional + initial graph. + * Add additional nodes to the graph. + * Call :meth:`~TopologicalSorter.prepare` on the graph. + * While :meth:`~TopologicalSorter.is_active` is ``True``, iterate over + the nodes returned by :meth:`~TopologicalSorter.get_ready` and + process them. Call :meth:`~TopologicalSorter.done` on each node as it + finishes processing. In case just an immediate sorting of the nodes in the graph is required and no parallelism is involved, the convenience method diff --git a/Doc/library/idle.rst b/Doc/library/idle.rst index 3211da50dc745c..e710d0bacf3fee 100644 --- a/Doc/library/idle.rst +++ b/Doc/library/idle.rst @@ -439,24 +439,24 @@ the :kbd:`Command` key on macOS. * Some useful Emacs bindings are inherited from Tcl/Tk: - * :kbd:`C-a` beginning of line + * :kbd:`C-a` beginning of line - * :kbd:`C-e` end of line + * :kbd:`C-e` end of line - * :kbd:`C-k` kill line (but doesn't put it in clipboard) + * :kbd:`C-k` kill line (but doesn't put it in clipboard) - * :kbd:`C-l` center window around the insertion point + * :kbd:`C-l` center window around the insertion point - * :kbd:`C-b` go backward one character without deleting (usually you can - also use the cursor key for this) + * :kbd:`C-b` go backward one character without deleting (usually you can + also use the cursor key for this) - * :kbd:`C-f` go forward one character without deleting (usually you can - also use the cursor key for this) + * :kbd:`C-f` go forward one character without deleting (usually you can + also use the cursor key for this) - * :kbd:`C-p` go up one line (usually you can also use the cursor key for - this) + * :kbd:`C-p` go up one line (usually you can also use the cursor key for + this) - * :kbd:`C-d` delete next character + * :kbd:`C-d` delete next character Standard keybindings (like :kbd:`C-c` to copy and :kbd:`C-v` to paste) may work. Keybindings are selected in the Configure IDLE dialog. diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index 8b2bfb322529b1..22637a051d9fa6 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -1435,10 +1435,11 @@ generator to be determined easily. Get current state of a generator-iterator. Possible states are: - * GEN_CREATED: Waiting to start execution. - * GEN_RUNNING: Currently being executed by the interpreter. - * GEN_SUSPENDED: Currently suspended at a yield expression. - * GEN_CLOSED: Execution has completed. + + * GEN_CREATED: Waiting to start execution. + * GEN_RUNNING: Currently being executed by the interpreter. + * GEN_SUSPENDED: Currently suspended at a yield expression. + * GEN_CLOSED: Execution has completed. .. versionadded:: 3.2 @@ -1450,10 +1451,11 @@ generator to be determined easily. ``cr_frame`` attributes. Possible states are: - * CORO_CREATED: Waiting to start execution. - * CORO_RUNNING: Currently being executed by the interpreter. - * CORO_SUSPENDED: Currently suspended at an await expression. - * CORO_CLOSED: Execution has completed. + + * CORO_CREATED: Waiting to start execution. + * CORO_RUNNING: Currently being executed by the interpreter. + * CORO_SUSPENDED: Currently suspended at an await expression. + * CORO_CLOSED: Execution has completed. .. versionadded:: 3.5 diff --git a/Doc/library/io.rst b/Doc/library/io.rst index 01088879218cb4..6736aa9ee2b0ef 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -253,12 +253,12 @@ The implementation of I/O streams is organized as a hierarchy of classes. First specify the various categories of streams, then concrete classes providing the standard stream implementations. - .. note:: +.. note:: - The abstract base classes also provide default implementations of some - methods in order to help implementation of concrete stream classes. For - example, :class:`BufferedIOBase` provides unoptimized implementations of - :meth:`!readinto` and :meth:`!readline`. + The abstract base classes also provide default implementations of some + methods in order to help implementation of concrete stream classes. For + example, :class:`BufferedIOBase` provides unoptimized implementations of + :meth:`!readinto` and :meth:`!readline`. At the top of the I/O hierarchy is the abstract base class :class:`IOBase`. It defines the basic interface to a stream. Note, however, that there is no diff --git a/Doc/library/logging.config.rst b/Doc/library/logging.config.rst index 619e0406066809..45aa67a2e49f62 100644 --- a/Doc/library/logging.config.rst +++ b/Doc/library/logging.config.rst @@ -257,10 +257,10 @@ otherwise, the context is used to determine what to instantiate. which correspond to the arguments passed to create a :class:`~logging.Formatter` object: - * ``format`` - * ``datefmt`` - * ``style`` - * ``validate`` (since version >=3.8) + * ``format`` + * ``datefmt`` + * ``style`` + * ``validate`` (since version >=3.8) An optional ``class`` key indicates the name of the formatter's class (as a dotted module and class name). The instantiation @@ -543,9 +543,9 @@ valid keyword parameter name, and so will not clash with the names of the keyword arguments used in the call. The ``'()'`` also serves as a mnemonic that the corresponding value is a callable. - .. versionchanged:: 3.11 - The ``filters`` member of ``handlers`` and ``loggers`` can take - filter instances in addition to ids. +.. versionchanged:: 3.11 + The ``filters`` member of ``handlers`` and ``loggers`` can take + filter instances in addition to ids. You can also specify a special key ``'.'`` whose value is a dictionary is a mapping of attribute names to values. If found, the specified attributes will diff --git a/Doc/library/lzma.rst b/Doc/library/lzma.rst index 434e7ac9061186..0d69c3bc01d1e2 100644 --- a/Doc/library/lzma.rst +++ b/Doc/library/lzma.rst @@ -333,19 +333,22 @@ the key ``"id"``, and may contain additional keys to specify filter-dependent options. Valid filter IDs are as follows: * Compression filters: - * :const:`FILTER_LZMA1` (for use with :const:`FORMAT_ALONE`) - * :const:`FILTER_LZMA2` (for use with :const:`FORMAT_XZ` and :const:`FORMAT_RAW`) + + * :const:`FILTER_LZMA1` (for use with :const:`FORMAT_ALONE`) + * :const:`FILTER_LZMA2` (for use with :const:`FORMAT_XZ` and :const:`FORMAT_RAW`) * Delta filter: - * :const:`FILTER_DELTA` + + * :const:`FILTER_DELTA` * Branch-Call-Jump (BCJ) filters: - * :const:`FILTER_X86` - * :const:`FILTER_IA64` - * :const:`FILTER_ARM` - * :const:`FILTER_ARMTHUMB` - * :const:`FILTER_POWERPC` - * :const:`FILTER_SPARC` + + * :const:`FILTER_X86` + * :const:`FILTER_IA64` + * :const:`FILTER_ARM` + * :const:`FILTER_ARMTHUMB` + * :const:`FILTER_POWERPC` + * :const:`FILTER_SPARC` A filter chain can consist of up to 4 filters, and cannot be empty. The last filter in the chain must be a compression filter, and any other filters must be @@ -354,21 +357,21 @@ delta or BCJ filters. Compression filters support the following options (specified as additional entries in the dictionary representing the filter): - * ``preset``: A compression preset to use as a source of default values for - options that are not specified explicitly. - * ``dict_size``: Dictionary size in bytes. This should be between 4 KiB and - 1.5 GiB (inclusive). - * ``lc``: Number of literal context bits. - * ``lp``: Number of literal position bits. The sum ``lc + lp`` must be at - most 4. - * ``pb``: Number of position bits; must be at most 4. - * ``mode``: :const:`MODE_FAST` or :const:`MODE_NORMAL`. - * ``nice_len``: What should be considered a "nice length" for a match. - This should be 273 or less. - * ``mf``: What match finder to use -- :const:`MF_HC3`, :const:`MF_HC4`, - :const:`MF_BT2`, :const:`MF_BT3`, or :const:`MF_BT4`. - * ``depth``: Maximum search depth used by match finder. 0 (default) means to - select automatically based on other filter options. +* ``preset``: A compression preset to use as a source of default values for + options that are not specified explicitly. +* ``dict_size``: Dictionary size in bytes. This should be between 4 KiB and + 1.5 GiB (inclusive). +* ``lc``: Number of literal context bits. +* ``lp``: Number of literal position bits. The sum ``lc + lp`` must be at + most 4. +* ``pb``: Number of position bits; must be at most 4. +* ``mode``: :const:`MODE_FAST` or :const:`MODE_NORMAL`. +* ``nice_len``: What should be considered a "nice length" for a match. + This should be 273 or less. +* ``mf``: What match finder to use -- :const:`MF_HC3`, :const:`MF_HC4`, + :const:`MF_BT2`, :const:`MF_BT3`, or :const:`MF_BT4`. +* ``depth``: Maximum search depth used by match finder. 0 (default) means to + select automatically based on other filter options. The delta filter stores the differences between bytes, producing more repetitive input for the compressor in certain circumstances. It supports one option, diff --git a/Doc/library/multiprocessing.rst b/Doc/library/multiprocessing.rst index 236b408499e235..2162328be9adb2 100644 --- a/Doc/library/multiprocessing.rst +++ b/Doc/library/multiprocessing.rst @@ -2751,20 +2751,20 @@ worker threads rather than worker processes. Unlike :class:`Pool`, *maxtasksperchild* and *context* cannot be provided. - .. note:: - - A :class:`ThreadPool` shares the same interface as :class:`Pool`, which - is designed around a pool of processes and predates the introduction of - the :class:`concurrent.futures` module. As such, it inherits some - operations that don't make sense for a pool backed by threads, and it - has its own type for representing the status of asynchronous jobs, - :class:`AsyncResult`, that is not understood by any other libraries. - - Users should generally prefer to use - :class:`concurrent.futures.ThreadPoolExecutor`, which has a simpler - interface that was designed around threads from the start, and which - returns :class:`concurrent.futures.Future` instances that are - compatible with many other libraries, including :mod:`asyncio`. + .. note:: + + A :class:`ThreadPool` shares the same interface as :class:`Pool`, which + is designed around a pool of processes and predates the introduction of + the :class:`concurrent.futures` module. As such, it inherits some + operations that don't make sense for a pool backed by threads, and it + has its own type for representing the status of asynchronous jobs, + :class:`AsyncResult`, that is not understood by any other libraries. + + Users should generally prefer to use + :class:`concurrent.futures.ThreadPoolExecutor`, which has a simpler + interface that was designed around threads from the start, and which + returns :class:`concurrent.futures.Future` instances that are + compatible with many other libraries, including :mod:`asyncio`. .. _multiprocessing-programming: diff --git a/Doc/library/numbers.rst b/Doc/library/numbers.rst index b3dce151aee289..2a05b56db051f9 100644 --- a/Doc/library/numbers.rst +++ b/Doc/library/numbers.rst @@ -160,23 +160,23 @@ refer to ``MyIntegral`` and ``OtherTypeIKnowAbout`` as of :class:`Complex` (``a : A <: Complex``), and ``b : B <: Complex``. I'll consider ``a + b``: - 1. If ``A`` defines an :meth:`__add__` which accepts ``b``, all is - well. - 2. If ``A`` falls back to the boilerplate code, and it were to - return a value from :meth:`__add__`, we'd miss the possibility - that ``B`` defines a more intelligent :meth:`__radd__`, so the - boilerplate should return :const:`NotImplemented` from - :meth:`__add__`. (Or ``A`` may not implement :meth:`__add__` at - all.) - 3. Then ``B``'s :meth:`__radd__` gets a chance. If it accepts - ``a``, all is well. - 4. If it falls back to the boilerplate, there are no more possible - methods to try, so this is where the default implementation - should live. - 5. If ``B <: A``, Python tries ``B.__radd__`` before - ``A.__add__``. This is ok, because it was implemented with - knowledge of ``A``, so it can handle those instances before - delegating to :class:`Complex`. +1. If ``A`` defines an :meth:`__add__` which accepts ``b``, all is + well. +2. If ``A`` falls back to the boilerplate code, and it were to + return a value from :meth:`__add__`, we'd miss the possibility + that ``B`` defines a more intelligent :meth:`__radd__`, so the + boilerplate should return :const:`NotImplemented` from + :meth:`__add__`. (Or ``A`` may not implement :meth:`__add__` at + all.) +3. Then ``B``'s :meth:`__radd__` gets a chance. If it accepts + ``a``, all is well. +4. If it falls back to the boilerplate, there are no more possible + methods to try, so this is where the default implementation + should live. +5. If ``B <: A``, Python tries ``B.__radd__`` before + ``A.__add__``. This is ok, because it was implemented with + knowledge of ``A``, so it can handle those instances before + delegating to :class:`Complex`. If ``A <: Complex`` and ``B <: Real`` without sharing any other knowledge, then the appropriate shared operation is the one involving the built diff --git a/Doc/library/profile.rst b/Doc/library/profile.rst index 69274b0c354a25..4c60a1e0d781b0 100644 --- a/Doc/library/profile.rst +++ b/Doc/library/profile.rst @@ -135,11 +135,11 @@ the output by. This only applies when ``-o`` is not supplied. ``-m`` specifies that a module is being profiled instead of a script. - .. versionadded:: 3.7 - Added the ``-m`` option to :mod:`cProfile`. +.. versionadded:: 3.7 + Added the ``-m`` option to :mod:`cProfile`. - .. versionadded:: 3.8 - Added the ``-m`` option to :mod:`profile`. +.. versionadded:: 3.8 + Added the ``-m`` option to :mod:`profile`. The :mod:`pstats` module's :class:`~pstats.Stats` class has a variety of methods for manipulating and printing the data saved into a profile results file:: diff --git a/Doc/library/re.rst b/Doc/library/re.rst index 5bea6166a1e4ef..83fff79d2884e5 100644 --- a/Doc/library/re.rst +++ b/Doc/library/re.rst @@ -176,7 +176,7 @@ The special characters are: ``x*+``, ``x++`` and ``x?+`` are equivalent to ``(?>x*)``, ``(?>x+)`` and ``(?>x?)`` correspondingly. - .. versionadded:: 3.11 + .. versionadded:: 3.11 .. index:: single: {} (curly brackets); in regular expressions diff --git a/Doc/library/shelve.rst b/Doc/library/shelve.rst index 01314f491f47a7..219219af6fd87f 100644 --- a/Doc/library/shelve.rst +++ b/Doc/library/shelve.rst @@ -94,9 +94,9 @@ Two additional methods are supported: Restrictions ------------ - .. index:: - pair: module; dbm.ndbm - pair: module; dbm.gnu +.. index:: + pair: module; dbm.ndbm + pair: module; dbm.gnu * The choice of which database package will be used (such as :mod:`dbm.ndbm` or :mod:`dbm.gnu`) depends on which interface is available. Therefore it is not diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index 32afacbbd231a7..c7274f59f43d1a 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -204,14 +204,14 @@ created. Socket addresses are represented as follows: - *addr* - Optional bytes-like object specifying the hardware physical address, whose interpretation depends on the device. - .. availability:: Linux >= 2.2. + .. availability:: Linux >= 2.2. - :const:`AF_QIPCRTR` is a Linux-only socket based interface for communicating with services running on co-processors in Qualcomm platforms. The address family is represented as a ``(node, port)`` tuple where the *node* and *port* are non-negative integers. - .. availability:: Linux >= 4.7. + .. availability:: Linux >= 4.7. .. versionadded:: 3.8 diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index c85f4fc49c070e..ef92bad596ef15 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -235,11 +235,11 @@ inserted data and retrieved values from it in multiple ways. * :ref:`sqlite3-howtos` for further reading: - * :ref:`sqlite3-placeholders` - * :ref:`sqlite3-adapters` - * :ref:`sqlite3-converters` - * :ref:`sqlite3-connection-context-manager` - * :ref:`sqlite3-howto-row-factory` + * :ref:`sqlite3-placeholders` + * :ref:`sqlite3-adapters` + * :ref:`sqlite3-converters` + * :ref:`sqlite3-connection-context-manager` + * :ref:`sqlite3-howto-row-factory` * :ref:`sqlite3-explanation` for in-depth background on transaction control. @@ -498,13 +498,13 @@ Module constants the default `threading mode `_ the underlying SQLite library is compiled with. The SQLite threading modes are: - 1. **Single-thread**: In this mode, all mutexes are disabled and SQLite is - unsafe to use in more than a single thread at once. - 2. **Multi-thread**: In this mode, SQLite can be safely used by multiple - threads provided that no single database connection is used - simultaneously in two or more threads. - 3. **Serialized**: In serialized mode, SQLite can be safely used by - multiple threads with no restriction. + 1. **Single-thread**: In this mode, all mutexes are disabled and SQLite is + unsafe to use in more than a single thread at once. + 2. **Multi-thread**: In this mode, SQLite can be safely used by multiple + threads provided that no single database connection is used + simultaneously in two or more threads. + 3. **Serialized**: In serialized mode, SQLite can be safely used by + multiple threads with no restriction. The mappings from SQLite threading modes to DB-API 2.0 threadsafety levels are as follows: diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 25369969f544b5..e7694d70163f76 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -1474,18 +1474,18 @@ to speed up repeated connections from the same clients. Here's a table showing which versions in a client (down the side) can connect to which versions in a server (along the top): - .. table:: - - ======================== ============ ============ ============= ========= =========== =========== - *client* / **server** **SSLv2** **SSLv3** **TLS** [3]_ **TLSv1** **TLSv1.1** **TLSv1.2** - ------------------------ ------------ ------------ ------------- --------- ----------- ----------- - *SSLv2* yes no no [1]_ no no no - *SSLv3* no yes no [2]_ no no no - *TLS* (*SSLv23*) [3]_ no [1]_ no [2]_ yes yes yes yes - *TLSv1* no no yes yes no no - *TLSv1.1* no no yes no yes no - *TLSv1.2* no no yes no no yes - ======================== ============ ============ ============= ========= =========== =========== + .. table:: + + ======================== ============ ============ ============= ========= =========== =========== + *client* / **server** **SSLv2** **SSLv3** **TLS** [3]_ **TLSv1** **TLSv1.1** **TLSv1.2** + ------------------------ ------------ ------------ ------------- --------- ----------- ----------- + *SSLv2* yes no no [1]_ no no no + *SSLv3* no yes no [2]_ no no no + *TLS* (*SSLv23*) [3]_ no [1]_ no [2]_ yes yes yes yes + *TLSv1* no no yes yes no no + *TLSv1.1* no no yes no yes no + *TLSv1.2* no no yes no no yes + ======================== ============ ============ ============= ========= =========== =========== .. rubric:: Footnotes .. [1] :class:`SSLContext` disables SSLv2 with :data:`OP_NO_SSLv2` by default. diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 6ba2dd0930ed7d..e9ecd7821ec837 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -48,9 +48,9 @@ By default, an object is considered true unless its class defines either a returns zero, when called with the object. [1]_ Here are most of the built-in objects considered false: - .. index:: - single: None (Built-in object) - single: False (Built-in object) +.. index:: + single: None (Built-in object) + single: False (Built-in object) * constants defined to be false: ``None`` and ``False`` diff --git a/Doc/library/string.rst b/Doc/library/string.rst index b2ea9c1781b870..5fbd82a5ec852e 100644 --- a/Doc/library/string.rst +++ b/Doc/library/string.rst @@ -206,15 +206,15 @@ literal text, it can be escaped by doubling: ``{{`` and ``}}``. The grammar for a replacement field is as follows: - .. productionlist:: format-string - replacement_field: "{" [`field_name`] ["!" `conversion`] [":" `format_spec`] "}" - field_name: arg_name ("." `attribute_name` | "[" `element_index` "]")* - arg_name: [`identifier` | `digit`+] - attribute_name: `identifier` - element_index: `digit`+ | `index_string` - index_string: + - conversion: "r" | "s" | "a" - format_spec: +.. productionlist:: format-string + replacement_field: "{" [`field_name`] ["!" `conversion`] [":" `format_spec`] "}" + field_name: arg_name ("." `attribute_name` | "[" `element_index` "]")* + arg_name: [`identifier` | `digit`+] + attribute_name: `identifier` + element_index: `digit`+ | `index_string` + index_string: + + conversion: "r" | "s" | "a" + format_spec: In less formal terms, the replacement field can start with a *field_name* that specifies the object whose value is to be formatted and inserted @@ -332,30 +332,30 @@ affect the :func:`format` function. The meaning of the various alignment options is as follows: - .. index:: - single: < (less); in string formatting - single: > (greater); in string formatting - single: = (equals); in string formatting - single: ^ (caret); in string formatting - - +---------+----------------------------------------------------------+ - | Option | Meaning | - +=========+==========================================================+ - | ``'<'`` | Forces the field to be left-aligned within the available | - | | space (this is the default for most objects). | - +---------+----------------------------------------------------------+ - | ``'>'`` | Forces the field to be right-aligned within the | - | | available space (this is the default for numbers). | - +---------+----------------------------------------------------------+ - | ``'='`` | Forces the padding to be placed after the sign (if any) | - | | but before the digits. This is used for printing fields | - | | in the form '+000000120'. This alignment option is only | - | | valid for numeric types. It becomes the default for | - | | numbers when '0' immediately precedes the field width. | - +---------+----------------------------------------------------------+ - | ``'^'`` | Forces the field to be centered within the available | - | | space. | - +---------+----------------------------------------------------------+ +.. index:: + single: < (less); in string formatting + single: > (greater); in string formatting + single: = (equals); in string formatting + single: ^ (caret); in string formatting + ++---------+----------------------------------------------------------+ +| Option | Meaning | ++=========+==========================================================+ +| ``'<'`` | Forces the field to be left-aligned within the available | +| | space (this is the default for most objects). | ++---------+----------------------------------------------------------+ +| ``'>'`` | Forces the field to be right-aligned within the | +| | available space (this is the default for numbers). | ++---------+----------------------------------------------------------+ +| ``'='`` | Forces the padding to be placed after the sign (if any) | +| | but before the digits. This is used for printing fields | +| | in the form '+000000120'. This alignment option is only | +| | valid for numeric types. It becomes the default for | +| | numbers when '0' immediately precedes the field width. | ++---------+----------------------------------------------------------+ +| ``'^'`` | Forces the field to be centered within the available | +| | space. | ++---------+----------------------------------------------------------+ Note that unless a minimum field width is defined, the field width will always be the same size as the data to fill it, so that the alignment option has no @@ -364,23 +364,23 @@ meaning in this case. The *sign* option is only valid for number types, and can be one of the following: - .. index:: - single: + (plus); in string formatting - single: - (minus); in string formatting - single: space; in string formatting - - +---------+----------------------------------------------------------+ - | Option | Meaning | - +=========+==========================================================+ - | ``'+'`` | indicates that a sign should be used for both | - | | positive as well as negative numbers. | - +---------+----------------------------------------------------------+ - | ``'-'`` | indicates that a sign should be used only for negative | - | | numbers (this is the default behavior). | - +---------+----------------------------------------------------------+ - | space | indicates that a leading space should be used on | - | | positive numbers, and a minus sign on negative numbers. | - +---------+----------------------------------------------------------+ +.. index:: + single: + (plus); in string formatting + single: - (minus); in string formatting + single: space; in string formatting + ++---------+----------------------------------------------------------+ +| Option | Meaning | ++=========+==========================================================+ +| ``'+'`` | indicates that a sign should be used for both | +| | positive as well as negative numbers. | ++---------+----------------------------------------------------------+ +| ``'-'`` | indicates that a sign should be used only for negative | +| | numbers (this is the default behavior). | ++---------+----------------------------------------------------------+ +| space | indicates that a leading space should be used on | +| | positive numbers, and a minus sign on negative numbers. | ++---------+----------------------------------------------------------+ .. index:: single: z; in string formatting diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst index 377f6938701da2..e5e992c8c87fd4 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -668,18 +668,18 @@ functions. passed to the underlying ``CreateProcess`` function. *creationflags*, if given, can be one or more of the following flags: - * :data:`CREATE_NEW_CONSOLE` - * :data:`CREATE_NEW_PROCESS_GROUP` - * :data:`ABOVE_NORMAL_PRIORITY_CLASS` - * :data:`BELOW_NORMAL_PRIORITY_CLASS` - * :data:`HIGH_PRIORITY_CLASS` - * :data:`IDLE_PRIORITY_CLASS` - * :data:`NORMAL_PRIORITY_CLASS` - * :data:`REALTIME_PRIORITY_CLASS` - * :data:`CREATE_NO_WINDOW` - * :data:`DETACHED_PROCESS` - * :data:`CREATE_DEFAULT_ERROR_MODE` - * :data:`CREATE_BREAKAWAY_FROM_JOB` + * :data:`CREATE_NEW_CONSOLE` + * :data:`CREATE_NEW_PROCESS_GROUP` + * :data:`ABOVE_NORMAL_PRIORITY_CLASS` + * :data:`BELOW_NORMAL_PRIORITY_CLASS` + * :data:`HIGH_PRIORITY_CLASS` + * :data:`IDLE_PRIORITY_CLASS` + * :data:`NORMAL_PRIORITY_CLASS` + * :data:`REALTIME_PRIORITY_CLASS` + * :data:`CREATE_NO_WINDOW` + * :data:`DETACHED_PROCESS` + * :data:`CREATE_DEFAULT_ERROR_MODE` + * :data:`CREATE_BREAKAWAY_FROM_JOB` *pipesize* can be used to change the size of the pipe when :data:`PIPE` is used for *stdin*, *stdout* or *stderr*. The size of the pipe @@ -744,8 +744,8 @@ the timeout expires before the process exits. Exceptions defined in this module all inherit from :exc:`SubprocessError`. - .. versionadded:: 3.3 - The :exc:`SubprocessError` base class was added. +.. versionadded:: 3.3 + The :exc:`SubprocessError` base class was added. .. _subprocess-security: diff --git a/Doc/library/tkinter.rst b/Doc/library/tkinter.rst index 246abf374b0219..ee34f2659cf3d8 100644 --- a/Doc/library/tkinter.rst +++ b/Doc/library/tkinter.rst @@ -533,24 +533,24 @@ interpreter will fail. A number of special cases exist: - * Tcl/Tk libraries can be built so they are not thread-aware. In this case, - :mod:`tkinter` calls the library from the originating Python thread, even - if this is different than the thread that created the Tcl interpreter. A global - lock ensures only one call occurs at a time. - - * While :mod:`tkinter` allows you to create more than one instance of a :class:`Tk` - object (with its own interpreter), all interpreters that are part of the same - thread share a common event queue, which gets ugly fast. In practice, don't create - more than one instance of :class:`Tk` at a time. Otherwise, it's best to create - them in separate threads and ensure you're running a thread-aware Tcl/Tk build. - - * Blocking event handlers are not the only way to prevent the Tcl interpreter from - reentering the event loop. It is even possible to run multiple nested event loops - or abandon the event loop entirely. If you're doing anything tricky when it comes - to events or threads, be aware of these possibilities. - - * There are a few select :mod:`tkinter` functions that presently work only when - called from the thread that created the Tcl interpreter. +* Tcl/Tk libraries can be built so they are not thread-aware. In this case, + :mod:`tkinter` calls the library from the originating Python thread, even + if this is different than the thread that created the Tcl interpreter. A global + lock ensures only one call occurs at a time. + +* While :mod:`tkinter` allows you to create more than one instance of a :class:`Tk` + object (with its own interpreter), all interpreters that are part of the same + thread share a common event queue, which gets ugly fast. In practice, don't create + more than one instance of :class:`Tk` at a time. Otherwise, it's best to create + them in separate threads and ensure you're running a thread-aware Tcl/Tk build. + +* Blocking event handlers are not the only way to prevent the Tcl interpreter from + reentering the event loop. It is even possible to run multiple nested event loops + or abandon the event loop entirely. If you're doing anything tricky when it comes + to events or threads, be aware of these possibilities. + +* There are a few select :mod:`tkinter` functions that presently work only when + called from the thread that created the Tcl interpreter. Handy Reference diff --git a/Doc/library/tkinter.ttk.rst b/Doc/library/tkinter.ttk.rst index 9f2f9eb858afd4..dc31a1a4c1850a 100644 --- a/Doc/library/tkinter.ttk.rst +++ b/Doc/library/tkinter.ttk.rst @@ -104,33 +104,33 @@ Standard Options All the :mod:`ttk` Widgets accept the following options: - .. tabularcolumns:: |l|L| - - +-----------+--------------------------------------------------------------+ - | Option | Description | - +===========+==============================================================+ - | class | Specifies the window class. The class is used when querying | - | | the option database for the window's other options, to | - | | determine the default bindtags for the window, and to select | - | | the widget's default layout and style. This option is | - | | read-only, and may only be specified when the window is | - | | created. | - +-----------+--------------------------------------------------------------+ - | cursor | Specifies the mouse cursor to be used for the widget. If set | - | | to the empty string (the default), the cursor is inherited | - | | for the parent widget. | - +-----------+--------------------------------------------------------------+ - | takefocus | Determines whether the window accepts the focus during | - | | keyboard traversal. 0, 1 or an empty string is returned. | - | | If 0 is returned, it means that the window should be skipped | - | | entirely during keyboard traversal. If 1, it means that the | - | | window should receive the input focus as long as it is | - | | viewable. And an empty string means that the traversal | - | | scripts make the decision about whether or not to focus | - | | on the window. | - +-----------+--------------------------------------------------------------+ - | style | May be used to specify a custom widget style. | - +-----------+--------------------------------------------------------------+ +.. tabularcolumns:: |l|L| + ++-----------+--------------------------------------------------------------+ +| Option | Description | ++===========+==============================================================+ +| class | Specifies the window class. The class is used when querying | +| | the option database for the window's other options, to | +| | determine the default bindtags for the window, and to select | +| | the widget's default layout and style. This option is | +| | read-only, and may only be specified when the window is | +| | created. | ++-----------+--------------------------------------------------------------+ +| cursor | Specifies the mouse cursor to be used for the widget. If set | +| | to the empty string (the default), the cursor is inherited | +| | for the parent widget. | ++-----------+--------------------------------------------------------------+ +| takefocus | Determines whether the window accepts the focus during | +| | keyboard traversal. 0, 1 or an empty string is returned. | +| | If 0 is returned, it means that the window should be skipped | +| | entirely during keyboard traversal. If 1, it means that the | +| | window should receive the input focus as long as it is | +| | viewable. And an empty string means that the traversal | +| | scripts make the decision about whether or not to focus | +| | on the window. | ++-----------+--------------------------------------------------------------+ +| style | May be used to specify a custom widget style. | ++-----------+--------------------------------------------------------------+ Scrollable Widget Options @@ -139,24 +139,24 @@ Scrollable Widget Options The following options are supported by widgets that are controlled by a scrollbar. - .. tabularcolumns:: |l|L| - - +----------------+---------------------------------------------------------+ - | Option | Description | - +================+=========================================================+ - | xscrollcommand | Used to communicate with horizontal scrollbars. | - | | | - | | When the view in the widget's window change, the widget | - | | will generate a Tcl command based on the scrollcommand. | - | | | - | | Usually this option consists of the method | - | | :meth:`Scrollbar.set` of some scrollbar. This will cause| - | | the scrollbar to be updated whenever the view in the | - | | window changes. | - +----------------+---------------------------------------------------------+ - | yscrollcommand | Used to communicate with vertical scrollbars. | - | | For some more information, see above. | - +----------------+---------------------------------------------------------+ +.. tabularcolumns:: |l|L| + ++----------------+---------------------------------------------------------+ +| Option | Description | ++================+=========================================================+ +| xscrollcommand | Used to communicate with horizontal scrollbars. | +| | | +| | When the view in the widget's window change, the widget | +| | will generate a Tcl command based on the scrollcommand. | +| | | +| | Usually this option consists of the method | +| | :meth:`Scrollbar.set` of some scrollbar. This will cause| +| | the scrollbar to be updated whenever the view in the | +| | window changes. | ++----------------+---------------------------------------------------------+ +| yscrollcommand | Used to communicate with vertical scrollbars. | +| | For some more information, see above. | ++----------------+---------------------------------------------------------+ Label Options @@ -165,93 +165,93 @@ Label Options The following options are supported by labels, buttons and other button-like widgets. - .. tabularcolumns:: |l|p{0.7\linewidth}| - - +--------------+-----------------------------------------------------------+ - | Option | Description | - +==============+===========================================================+ - | text | Specifies a text string to be displayed inside the widget.| - +--------------+-----------------------------------------------------------+ - | textvariable | Specifies a name whose value will be used in place of the | - | | text option resource. | - +--------------+-----------------------------------------------------------+ - | underline | If set, specifies the index (0-based) of a character to | - | | underline in the text string. The underline character is | - | | used for mnemonic activation. | - +--------------+-----------------------------------------------------------+ - | image | Specifies an image to display. This is a list of 1 or more| - | | elements. The first element is the default image name. The| - | | rest of the list if a sequence of statespec/value pairs as| - | | defined by :meth:`Style.map`, specifying different images | - | | to use when the widget is in a particular state or a | - | | combination of states. All images in the list should have | - | | the same size. | - +--------------+-----------------------------------------------------------+ - | compound | Specifies how to display the image relative to the text, | - | | in the case both text and images options are present. | - | | Valid values are: | - | | | - | | * text: display text only | - | | * image: display image only | - | | * top, bottom, left, right: display image above, below, | - | | left of, or right of the text, respectively. | - | | * none: the default. display the image if present, | - | | otherwise the text. | - +--------------+-----------------------------------------------------------+ - | width | If greater than zero, specifies how much space, in | - | | character widths, to allocate for the text label, if less | - | | than zero, specifies a minimum width. If zero or | - | | unspecified, the natural width of the text label is used. | - +--------------+-----------------------------------------------------------+ +.. tabularcolumns:: |l|p{0.7\linewidth}| + ++--------------+-----------------------------------------------------------+ +| Option | Description | ++==============+===========================================================+ +| text | Specifies a text string to be displayed inside the widget.| ++--------------+-----------------------------------------------------------+ +| textvariable | Specifies a name whose value will be used in place of the | +| | text option resource. | ++--------------+-----------------------------------------------------------+ +| underline | If set, specifies the index (0-based) of a character to | +| | underline in the text string. The underline character is | +| | used for mnemonic activation. | ++--------------+-----------------------------------------------------------+ +| image | Specifies an image to display. This is a list of 1 or more| +| | elements. The first element is the default image name. The| +| | rest of the list if a sequence of statespec/value pairs as| +| | defined by :meth:`Style.map`, specifying different images | +| | to use when the widget is in a particular state or a | +| | combination of states. All images in the list should have | +| | the same size. | ++--------------+-----------------------------------------------------------+ +| compound | Specifies how to display the image relative to the text, | +| | in the case both text and images options are present. | +| | Valid values are: | +| | | +| | * text: display text only | +| | * image: display image only | +| | * top, bottom, left, right: display image above, below, | +| | left of, or right of the text, respectively. | +| | * none: the default. display the image if present, | +| | otherwise the text. | ++--------------+-----------------------------------------------------------+ +| width | If greater than zero, specifies how much space, in | +| | character widths, to allocate for the text label, if less | +| | than zero, specifies a minimum width. If zero or | +| | unspecified, the natural width of the text label is used. | ++--------------+-----------------------------------------------------------+ Compatibility Options ^^^^^^^^^^^^^^^^^^^^^ - .. tabularcolumns:: |l|L| +.. tabularcolumns:: |l|L| - +--------+----------------------------------------------------------------+ - | Option | Description | - +========+================================================================+ - | state | May be set to "normal" or "disabled" to control the "disabled" | - | | state bit. This is a write-only option: setting it changes the | - | | widget state, but the :meth:`Widget.state` method does not | - | | affect this option. | - +--------+----------------------------------------------------------------+ ++--------+----------------------------------------------------------------+ +| Option | Description | ++========+================================================================+ +| state | May be set to "normal" or "disabled" to control the "disabled" | +| | state bit. This is a write-only option: setting it changes the | +| | widget state, but the :meth:`Widget.state` method does not | +| | affect this option. | ++--------+----------------------------------------------------------------+ Widget States ^^^^^^^^^^^^^ The widget state is a bitmap of independent state flags. - .. tabularcolumns:: |l|L| - - +------------+-------------------------------------------------------------+ - | Flag | Description | - +============+=============================================================+ - | active | The mouse cursor is over the widget and pressing a mouse | - | | button will cause some action to occur | - +------------+-------------------------------------------------------------+ - | disabled | Widget is disabled under program control | - +------------+-------------------------------------------------------------+ - | focus | Widget has keyboard focus | - +------------+-------------------------------------------------------------+ - | pressed | Widget is being pressed | - +------------+-------------------------------------------------------------+ - | selected | "On", "true", or "current" for things like Checkbuttons and | - | | radiobuttons | - +------------+-------------------------------------------------------------+ - | background | Windows and Mac have a notion of an "active" or foreground | - | | window. The *background* state is set for widgets in a | - | | background window, and cleared for those in the foreground | - | | window | - +------------+-------------------------------------------------------------+ - | readonly | Widget should not allow user modification | - +------------+-------------------------------------------------------------+ - | alternate | A widget-specific alternate display format | - +------------+-------------------------------------------------------------+ - | invalid | The widget's value is invalid | - +------------+-------------------------------------------------------------+ +.. tabularcolumns:: |l|L| + ++------------+-------------------------------------------------------------+ +| Flag | Description | ++============+=============================================================+ +| active | The mouse cursor is over the widget and pressing a mouse | +| | button will cause some action to occur | ++------------+-------------------------------------------------------------+ +| disabled | Widget is disabled under program control | ++------------+-------------------------------------------------------------+ +| focus | Widget has keyboard focus | ++------------+-------------------------------------------------------------+ +| pressed | Widget is being pressed | ++------------+-------------------------------------------------------------+ +| selected | "On", "true", or "current" for things like Checkbuttons and | +| | radiobuttons | ++------------+-------------------------------------------------------------+ +| background | Windows and Mac have a notion of an "active" or foreground | +| | window. The *background* state is set for widgets in a | +| | background window, and cleared for those in the foreground | +| | window | ++------------+-------------------------------------------------------------+ +| readonly | Widget should not allow user modification | ++------------+-------------------------------------------------------------+ +| alternate | A widget-specific alternate display format | ++------------+-------------------------------------------------------------+ +| invalid | The widget's value is invalid | ++------------+-------------------------------------------------------------+ A state specification is a sequence of state names, optionally prefixed with an exclamation point indicating that the bit is off. @@ -311,43 +311,43 @@ Options This widget accepts the following specific options: - .. tabularcolumns:: |l|L| - - +-----------------+--------------------------------------------------------+ - | Option | Description | - +=================+========================================================+ - | exportselection | Boolean value. If set, the widget selection is linked | - | | to the Window Manager selection (which can be returned | - | | by invoking Misc.selection_get, for example). | - +-----------------+--------------------------------------------------------+ - | justify | Specifies how the text is aligned within the widget. | - | | One of "left", "center", or "right". | - +-----------------+--------------------------------------------------------+ - | height | Specifies the height of the pop-down listbox, in rows. | - +-----------------+--------------------------------------------------------+ - | postcommand | A script (possibly registered with Misc.register) that | - | | is called immediately before displaying the values. It | - | | may specify which values to display. | - +-----------------+--------------------------------------------------------+ - | state | One of "normal", "readonly", or "disabled". In the | - | | "readonly" state, the value may not be edited directly,| - | | and the user can only selection of the values from the | - | | dropdown list. In the "normal" state, the text field is| - | | directly editable. In the "disabled" state, no | - | | interaction is possible. | - +-----------------+--------------------------------------------------------+ - | textvariable | Specifies a name whose value is linked to the widget | - | | value. Whenever the value associated with that name | - | | changes, the widget value is updated, and vice versa. | - | | See :class:`tkinter.StringVar`. | - +-----------------+--------------------------------------------------------+ - | values | Specifies the list of values to display in the | - | | drop-down listbox. | - +-----------------+--------------------------------------------------------+ - | width | Specifies an integer value indicating the desired width| - | | of the entry window, in average-size characters of the | - | | widget's font. | - +-----------------+--------------------------------------------------------+ +.. tabularcolumns:: |l|L| + ++-----------------+--------------------------------------------------------+ +| Option | Description | ++=================+========================================================+ +| exportselection | Boolean value. If set, the widget selection is linked | +| | to the Window Manager selection (which can be returned | +| | by invoking Misc.selection_get, for example). | ++-----------------+--------------------------------------------------------+ +| justify | Specifies how the text is aligned within the widget. | +| | One of "left", "center", or "right". | ++-----------------+--------------------------------------------------------+ +| height | Specifies the height of the pop-down listbox, in rows. | ++-----------------+--------------------------------------------------------+ +| postcommand | A script (possibly registered with Misc.register) that | +| | is called immediately before displaying the values. It | +| | may specify which values to display. | ++-----------------+--------------------------------------------------------+ +| state | One of "normal", "readonly", or "disabled". In the | +| | "readonly" state, the value may not be edited directly,| +| | and the user can only selection of the values from the | +| | dropdown list. In the "normal" state, the text field is| +| | directly editable. In the "disabled" state, no | +| | interaction is possible. | ++-----------------+--------------------------------------------------------+ +| textvariable | Specifies a name whose value is linked to the widget | +| | value. Whenever the value associated with that name | +| | changes, the widget value is updated, and vice versa. | +| | See :class:`tkinter.StringVar`. | ++-----------------+--------------------------------------------------------+ +| values | Specifies the list of values to display in the | +| | drop-down listbox. | ++-----------------+--------------------------------------------------------+ +| width | Specifies an integer value indicating the desired width| +| | of the entry window, in average-size characters of the | +| | widget's font. | ++-----------------+--------------------------------------------------------+ Virtual events @@ -397,7 +397,7 @@ Options This widget accepts the following specific options: - .. tabularcolumns:: |l|L| +.. tabularcolumns:: |l|L| +----------------------+------------------------------------------------------+ | Option | Description | @@ -473,25 +473,25 @@ Options This widget accepts the following specific options: - .. tabularcolumns:: |l|L| - - +---------+----------------------------------------------------------------+ - | Option | Description | - +=========+================================================================+ - | height | If present and greater than zero, specifies the desired height | - | | of the pane area (not including internal padding or tabs). | - | | Otherwise, the maximum height of all panes is used. | - +---------+----------------------------------------------------------------+ - | padding | Specifies the amount of extra space to add around the outside | - | | of the notebook. The padding is a list up to four length | - | | specifications left top right bottom. If fewer than four | - | | elements are specified, bottom defaults to top, right defaults | - | | to left, and top defaults to left. | - +---------+----------------------------------------------------------------+ - | width | If present and greater than zero, specified the desired width | - | | of the pane area (not including internal padding). Otherwise, | - | | the maximum width of all panes is used. | - +---------+----------------------------------------------------------------+ +.. tabularcolumns:: |l|L| + ++---------+----------------------------------------------------------------+ +| Option | Description | ++=========+================================================================+ +| height | If present and greater than zero, specifies the desired height | +| | of the pane area (not including internal padding or tabs). | +| | Otherwise, the maximum height of all panes is used. | ++---------+----------------------------------------------------------------+ +| padding | Specifies the amount of extra space to add around the outside | +| | of the notebook. The padding is a list up to four length | +| | specifications left top right bottom. If fewer than four | +| | elements are specified, bottom defaults to top, right defaults | +| | to left, and top defaults to left. | ++---------+----------------------------------------------------------------+ +| width | If present and greater than zero, specified the desired width | +| | of the pane area (not including internal padding). Otherwise, | +| | the maximum width of all panes is used. | ++---------+----------------------------------------------------------------+ Tab Options @@ -499,39 +499,39 @@ Tab Options There are also specific options for tabs: - .. tabularcolumns:: |l|L| - - +-----------+--------------------------------------------------------------+ - | Option | Description | - +===========+==============================================================+ - | state | Either "normal", "disabled" or "hidden". If "disabled", then | - | | the tab is not selectable. If "hidden", then the tab is not | - | | shown. | - +-----------+--------------------------------------------------------------+ - | sticky | Specifies how the child window is positioned within the pane | - | | area. Value is a string containing zero or more of the | - | | characters "n", "s", "e" or "w". Each letter refers to a | - | | side (north, south, east or west) that the child window will | - | | stick to, as per the :meth:`grid` geometry manager. | - +-----------+--------------------------------------------------------------+ - | padding | Specifies the amount of extra space to add between the | - | | notebook and this pane. Syntax is the same as for the option | - | | padding used by this widget. | - +-----------+--------------------------------------------------------------+ - | text | Specifies a text to be displayed in the tab. | - +-----------+--------------------------------------------------------------+ - | image | Specifies an image to display in the tab. See the option | - | | image described in :class:`Widget`. | - +-----------+--------------------------------------------------------------+ - | compound | Specifies how to display the image relative to the text, in | - | | the case both options text and image are present. See | - | | `Label Options`_ for legal values. | - +-----------+--------------------------------------------------------------+ - | underline | Specifies the index (0-based) of a character to underline in | - | | the text string. The underlined character is used for | - | | mnemonic activation if :meth:`Notebook.enable_traversal` is | - | | called. | - +-----------+--------------------------------------------------------------+ +.. tabularcolumns:: |l|L| + ++-----------+--------------------------------------------------------------+ +| Option | Description | ++===========+==============================================================+ +| state | Either "normal", "disabled" or "hidden". If "disabled", then | +| | the tab is not selectable. If "hidden", then the tab is not | +| | shown. | ++-----------+--------------------------------------------------------------+ +| sticky | Specifies how the child window is positioned within the pane | +| | area. Value is a string containing zero or more of the | +| | characters "n", "s", "e" or "w". Each letter refers to a | +| | side (north, south, east or west) that the child window will | +| | stick to, as per the :meth:`grid` geometry manager. | ++-----------+--------------------------------------------------------------+ +| padding | Specifies the amount of extra space to add between the | +| | notebook and this pane. Syntax is the same as for the option | +| | padding used by this widget. | ++-----------+--------------------------------------------------------------+ +| text | Specifies a text to be displayed in the tab. | ++-----------+--------------------------------------------------------------+ +| image | Specifies an image to display in the tab. See the option | +| | image described in :class:`Widget`. | ++-----------+--------------------------------------------------------------+ +| compound | Specifies how to display the image relative to the text, in | +| | the case both options text and image are present. See | +| | `Label Options`_ for legal values. | ++-----------+--------------------------------------------------------------+ +| underline | Specifies the index (0-based) of a character to underline in | +| | the text string. The underlined character is used for | +| | mnemonic activation if :meth:`Notebook.enable_traversal` is | +| | called. | ++-----------+--------------------------------------------------------------+ Tab Identifiers @@ -663,36 +663,36 @@ Options This widget accepts the following specific options: - .. tabularcolumns:: |l|L| - - +----------+---------------------------------------------------------------+ - | Option | Description | - +==========+===============================================================+ - | orient | One of "horizontal" or "vertical". Specifies the orientation | - | | of the progress bar. | - +----------+---------------------------------------------------------------+ - | length | Specifies the length of the long axis of the progress bar | - | | (width if horizontal, height if vertical). | - +----------+---------------------------------------------------------------+ - | mode | One of "determinate" or "indeterminate". | - +----------+---------------------------------------------------------------+ - | maximum | A number specifying the maximum value. Defaults to 100. | - +----------+---------------------------------------------------------------+ - | value | The current value of the progress bar. In "determinate" mode, | - | | this represents the amount of work completed. In | - | | "indeterminate" mode, it is interpreted as modulo *maximum*; | - | | that is, the progress bar completes one "cycle" when its value| - | | increases by *maximum*. | - +----------+---------------------------------------------------------------+ - | variable | A name which is linked to the option value. If specified, the | - | | value of the progress bar is automatically set to the value of| - | | this name whenever the latter is modified. | - +----------+---------------------------------------------------------------+ - | phase | Read-only option. The widget periodically increments the value| - | | of this option whenever its value is greater than 0 and, in | - | | determinate mode, less than maximum. This option may be used | - | | by the current theme to provide additional animation effects. | - +----------+---------------------------------------------------------------+ +.. tabularcolumns:: |l|L| + ++----------+---------------------------------------------------------------+ +| Option | Description | ++==========+===============================================================+ +| orient | One of "horizontal" or "vertical". Specifies the orientation | +| | of the progress bar. | ++----------+---------------------------------------------------------------+ +| length | Specifies the length of the long axis of the progress bar | +| | (width if horizontal, height if vertical). | ++----------+---------------------------------------------------------------+ +| mode | One of "determinate" or "indeterminate". | ++----------+---------------------------------------------------------------+ +| maximum | A number specifying the maximum value. Defaults to 100. | ++----------+---------------------------------------------------------------+ +| value | The current value of the progress bar. In "determinate" mode, | +| | this represents the amount of work completed. In | +| | "indeterminate" mode, it is interpreted as modulo *maximum*; | +| | that is, the progress bar completes one "cycle" when its value| +| | increases by *maximum*. | ++----------+---------------------------------------------------------------+ +| variable | A name which is linked to the option value. If specified, the | +| | value of the progress bar is automatically set to the value of| +| | this name whenever the latter is modified. | ++----------+---------------------------------------------------------------+ +| phase | Read-only option. The widget periodically increments the value| +| | of this option whenever its value is greater than 0 and, in | +| | determinate mode, less than maximum. This option may be used | +| | by the current theme to provide additional animation effects. | ++----------+---------------------------------------------------------------+ ttk.Progressbar @@ -734,14 +734,14 @@ Options This widget accepts the following specific option: - .. tabularcolumns:: |l|L| +.. tabularcolumns:: |l|L| - +--------+----------------------------------------------------------------+ - | Option | Description | - +========+================================================================+ - | orient | One of "horizontal" or "vertical". Specifies the orientation of| - | | the separator. | - +--------+----------------------------------------------------------------+ ++--------+----------------------------------------------------------------+ +| Option | Description | ++========+================================================================+ +| orient | One of "horizontal" or "vertical". Specifies the orientation of| +| | the separator. | ++--------+----------------------------------------------------------------+ Sizegrip @@ -802,49 +802,49 @@ Options This widget accepts the following specific options: - .. tabularcolumns:: |l|p{0.7\linewidth}| - - +----------------+--------------------------------------------------------+ - | Option | Description | - +================+========================================================+ - | columns | A list of column identifiers, specifying the number of | - | | columns and their names. | - +----------------+--------------------------------------------------------+ - | displaycolumns | A list of column identifiers (either symbolic or | - | | integer indices) specifying which data columns are | - | | displayed and the order in which they appear, or the | - | | string "#all". | - +----------------+--------------------------------------------------------+ - | height | Specifies the number of rows which should be visible. | - | | Note: the requested width is determined from the sum | - | | of the column widths. | - +----------------+--------------------------------------------------------+ - | padding | Specifies the internal padding for the widget. The | - | | padding is a list of up to four length specifications. | - +----------------+--------------------------------------------------------+ - | selectmode | Controls how the built-in class bindings manage the | - | | selection. One of "extended", "browse" or "none". | - | | If set to "extended" (the default), multiple items may | - | | be selected. If "browse", only a single item will be | - | | selected at a time. If "none", the selection will not | - | | be changed. | - | | | - | | Note that the application code and tag bindings can set| - | | the selection however they wish, regardless of the | - | | value of this option. | - +----------------+--------------------------------------------------------+ - | show | A list containing zero or more of the following values,| - | | specifying which elements of the tree to display. | - | | | - | | * tree: display tree labels in column #0. | - | | * headings: display the heading row. | - | | | - | | The default is "tree headings", i.e., show all | - | | elements. | - | | | - | | **Note**: Column #0 always refers to the tree column, | - | | even if show="tree" is not specified. | - +----------------+--------------------------------------------------------+ +.. tabularcolumns:: |l|p{0.7\linewidth}| + ++----------------+--------------------------------------------------------+ +| Option | Description | ++================+========================================================+ +| columns | A list of column identifiers, specifying the number of | +| | columns and their names. | ++----------------+--------------------------------------------------------+ +| displaycolumns | A list of column identifiers (either symbolic or | +| | integer indices) specifying which data columns are | +| | displayed and the order in which they appear, or the | +| | string "#all". | ++----------------+--------------------------------------------------------+ +| height | Specifies the number of rows which should be visible. | +| | Note: the requested width is determined from the sum | +| | of the column widths. | ++----------------+--------------------------------------------------------+ +| padding | Specifies the internal padding for the widget. The | +| | padding is a list of up to four length specifications. | ++----------------+--------------------------------------------------------+ +| selectmode | Controls how the built-in class bindings manage the | +| | selection. One of "extended", "browse" or "none". | +| | If set to "extended" (the default), multiple items may | +| | be selected. If "browse", only a single item will be | +| | selected at a time. If "none", the selection will not | +| | be changed. | +| | | +| | Note that the application code and tag bindings can set| +| | the selection however they wish, regardless of the | +| | value of this option. | ++----------------+--------------------------------------------------------+ +| show | A list containing zero or more of the following values,| +| | specifying which elements of the tree to display. | +| | | +| | * tree: display tree labels in column #0. | +| | * headings: display the heading row. | +| | | +| | The default is "tree headings", i.e., show all | +| | elements. | +| | | +| | **Note**: Column #0 always refers to the tree column, | +| | even if show="tree" is not specified. | ++----------------+--------------------------------------------------------+ Item Options @@ -853,27 +853,27 @@ Item Options The following item options may be specified for items in the insert and item widget commands. - .. tabularcolumns:: |l|L| - - +--------+---------------------------------------------------------------+ - | Option | Description | - +========+===============================================================+ - | text | The textual label to display for the item. | - +--------+---------------------------------------------------------------+ - | image | A Tk Image, displayed to the left of the label. | - +--------+---------------------------------------------------------------+ - | values | The list of values associated with the item. | - | | | - | | Each item should have the same number of values as the widget | - | | option columns. If there are fewer values than columns, the | - | | remaining values are assumed empty. If there are more values | - | | than columns, the extra values are ignored. | - +--------+---------------------------------------------------------------+ - | open | ``True``/``False`` value indicating whether the item's | - | | children should be displayed or hidden. | - +--------+---------------------------------------------------------------+ - | tags | A list of tags associated with this item. | - +--------+---------------------------------------------------------------+ +.. tabularcolumns:: |l|L| + ++--------+---------------------------------------------------------------+ +| Option | Description | ++========+===============================================================+ +| text | The textual label to display for the item. | ++--------+---------------------------------------------------------------+ +| image | A Tk Image, displayed to the left of the label. | ++--------+---------------------------------------------------------------+ +| values | The list of values associated with the item. | +| | | +| | Each item should have the same number of values as the widget | +| | option columns. If there are fewer values than columns, the | +| | remaining values are assumed empty. If there are more values | +| | than columns, the extra values are ignored. | ++--------+---------------------------------------------------------------+ +| open | ``True``/``False`` value indicating whether the item's | +| | children should be displayed or hidden. | ++--------+---------------------------------------------------------------+ +| tags | A list of tags associated with this item. | ++--------+---------------------------------------------------------------+ Tag Options @@ -881,20 +881,20 @@ Tag Options The following options may be specified on tags: - .. tabularcolumns:: |l|L| +.. tabularcolumns:: |l|L| - +------------+-----------------------------------------------------------+ - | Option | Description | - +============+===========================================================+ - | foreground | Specifies the text foreground color. | - +------------+-----------------------------------------------------------+ - | background | Specifies the cell or item background color. | - +------------+-----------------------------------------------------------+ - | font | Specifies the font to use when drawing text. | - +------------+-----------------------------------------------------------+ - | image | Specifies the item image, in case the item's image option | - | | is empty. | - +------------+-----------------------------------------------------------+ ++------------+-----------------------------------------------------------+ +| Option | Description | ++============+===========================================================+ +| foreground | Specifies the text foreground color. | ++------------+-----------------------------------------------------------+ +| background | Specifies the cell or item background color. | ++------------+-----------------------------------------------------------+ +| font | Specifies the font to use when drawing text. | ++------------+-----------------------------------------------------------+ +| image | Specifies the item image, in case the item's image option | +| | is empty. | ++------------+-----------------------------------------------------------+ Column Identifiers @@ -926,19 +926,19 @@ Virtual Events The Treeview widget generates the following virtual events. - .. tabularcolumns:: |l|L| +.. tabularcolumns:: |l|L| - +--------------------+--------------------------------------------------+ - | Event | Description | - +====================+==================================================+ - | <> | Generated whenever the selection changes. | - +--------------------+--------------------------------------------------+ - | <> | Generated just before settings the focus item to | - | | open=True. | - +--------------------+--------------------------------------------------+ - | <> | Generated just after setting the focus item to | - | | open=False. | - +--------------------+--------------------------------------------------+ ++--------------------+--------------------------------------------------+ +| Event | Description | ++====================+==================================================+ +| <> | Generated whenever the selection changes. | ++--------------------+--------------------------------------------------+ +| <> | Generated just before settings the focus item to | +| | open=True. | ++--------------------+--------------------------------------------------+ +| <> | Generated just after setting the focus item to | +| | open=False. | ++--------------------+--------------------------------------------------+ The :meth:`Treeview.focus` and :meth:`Treeview.selection` methods can be used to determine the affected item or items. @@ -986,19 +986,19 @@ ttk.Treeview The valid options/values are: - * id + id Returns the column name. This is a read-only option. - * anchor: One of the standard Tk anchor values. + anchor: One of the standard Tk anchor values. Specifies how the text in this column should be aligned with respect to the cell. - * minwidth: width + minwidth: width The minimum width of the column in pixels. The treeview widget will not make the column any smaller than specified by this option when the widget is resized or the user drags a column. - * stretch: ``True``/``False`` + stretch: ``True``/``False`` Specifies whether the column's width should be adjusted when the widget is resized. - * width: width + width: width The width of the column in pixels. To configure the tree column, call this with column = "#0" @@ -1041,14 +1041,14 @@ ttk.Treeview The valid options/values are: - * text: text + text: text The text to display in the column heading. - * image: imageName + image: imageName Specifies an image to display to the right of the column heading. - * anchor: anchor + anchor: anchor Specifies how the heading text should be aligned. One of the standard Tk anchor values. - * command: callback + command: callback A callback to be invoked when the heading label is pressed. To configure the tree column heading, call this with column = "#0". @@ -1398,25 +1398,25 @@ option. If you don't know the class name of a widget, use the method by statespec/value pairs (this is the imagespec), and *kw* may have the following options: - * border=padding - padding is a list of up to four integers, specifying the left, top, - right, and bottom borders, respectively. + border=padding + padding is a list of up to four integers, specifying the left, top, + right, and bottom borders, respectively. - * height=height - Specifies a minimum height for the element. If less than zero, the - base image's height is used as a default. + height=height + Specifies a minimum height for the element. If less than zero, the + base image's height is used as a default. - * padding=padding - Specifies the element's interior padding. Defaults to border's value - if not specified. + padding=padding + Specifies the element's interior padding. Defaults to border's value + if not specified. - * sticky=spec - Specifies how the image is placed within the final parcel. spec - contains zero or more characters "n", "s", "w", or "e". + sticky=spec + Specifies how the image is placed within the final parcel. spec + contains zero or more characters "n", "s", "w", or "e". - * width=width - Specifies a minimum width for the element. If less than zero, the - base image's width is used as a default. + width=width + Specifies a minimum width for the element. If less than zero, the + base image's width is used as a default. If "from" is used as the value of *etype*, :meth:`element_create` will clone an existing @@ -1504,22 +1504,22 @@ uses a simplified version of the pack geometry manager: given an initial cavity, each element is allocated a parcel. Valid options/values are: - * side: whichside - Specifies which side of the cavity to place the element; one of - top, right, bottom or left. If omitted, the element occupies the - entire cavity. +side: whichside + Specifies which side of the cavity to place the element; one of + top, right, bottom or left. If omitted, the element occupies the + entire cavity. - * sticky: nswe - Specifies where the element is placed inside its allocated parcel. +sticky: nswe + Specifies where the element is placed inside its allocated parcel. - * unit: 0 or 1 - If set to 1, causes the element and all of its descendants to be treated as - a single element for the purposes of :meth:`Widget.identify` et al. It's - used for things like scrollbar thumbs with grips. +unit: 0 or 1 + If set to 1, causes the element and all of its descendants to be treated as + a single element for the purposes of :meth:`Widget.identify` et al. It's + used for things like scrollbar thumbs with grips. - * children: [sublayout... ] - Specifies a list of elements to place inside the element. Each - element is a tuple (or other sequence type) where the first item is - the layout name, and the other is a `Layout`_. +children: [sublayout... ] + Specifies a list of elements to place inside the element. Each + element is a tuple (or other sequence type) where the first item is + the layout name, and the other is a `Layout`_. .. _Layout: `Layouts`_ diff --git a/Doc/library/unittest.mock.rst b/Doc/library/unittest.mock.rst index 8d877165a09740..d3398e9407d40f 100644 --- a/Doc/library/unittest.mock.rst +++ b/Doc/library/unittest.mock.rst @@ -813,8 +813,8 @@ This applies to :meth:`~Mock.assert_called_with`, :meth:`~Mock.assert_any_call`. When :ref:`auto-speccing`, it will also apply to method calls on the mock object. - .. versionchanged:: 3.4 - Added signature introspection on specced and autospecced mock objects. +.. versionchanged:: 3.4 + Added signature introspection on specced and autospecced mock objects. .. class:: PropertyMock(*args, **kwargs) @@ -1387,9 +1387,9 @@ patch .. note:: - .. versionchanged:: 3.5 - If you are patching builtins in a module then you don't - need to pass ``create=True``, it will be added by default. + .. versionchanged:: 3.5 + If you are patching builtins in a module then you don't + need to pass ``create=True``, it will be added by default. Patch can be used as a :class:`TestCase` class decorator. It works by decorating each test method in the class. This reduces the boilerplate diff --git a/Doc/library/urllib.request.rst b/Doc/library/urllib.request.rst index 35b8f5b471dd96..002dab8a65195c 100644 --- a/Doc/library/urllib.request.rst +++ b/Doc/library/urllib.request.rst @@ -315,10 +315,10 @@ The following classes are provided: list of hostname suffixes, optionally with ``:port`` appended, for example ``cern.ch,ncsa.uiuc.edu,some.host:8080``. - .. note:: + .. note:: - ``HTTP_PROXY`` will be ignored if a variable ``REQUEST_METHOD`` is set; - see the documentation on :func:`~urllib.request.getproxies`. + ``HTTP_PROXY`` will be ignored if a variable ``REQUEST_METHOD`` is set; + see the documentation on :func:`~urllib.request.getproxies`. .. class:: HTTPPasswordMgr() @@ -1536,9 +1536,9 @@ some point in the future. :mod:`urllib.request` Restrictions ---------------------------------- - .. index:: - pair: HTTP; protocol - pair: FTP; protocol +.. index:: + pair: HTTP; protocol + pair: FTP; protocol * Currently, only the following protocols are supported: HTTP (versions 0.9 and 1.0), FTP, local files, and data URLs. From 90b2620b6e7535e94bab802e9084a64c8670fcee Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Wed, 11 Oct 2023 23:12:53 +0200 Subject: [PATCH 464/632] [3.11] gh-110631: Fix reST indentation (GH-110724) (#110739) * Fix wrong indentation in the other dirs. * Fix more wrong indentation.. (cherry picked from commit 718391f475f2550d99dd794069ca76312f7f6aa6) --- Doc/c-api/memory.rst | 24 ++++++++++++------------ Doc/howto/enum.rst | 15 ++++++++------- Doc/howto/instrumentation.rst | 14 ++++++-------- Doc/library/csv.rst | 6 +++--- Doc/library/dataclasses.rst | 12 +++++------- Doc/library/decimal.rst | 8 ++++---- Doc/using/windows.rst | 25 +++++++++++++------------ 7 files changed, 51 insertions(+), 53 deletions(-) diff --git a/Doc/c-api/memory.rst b/Doc/c-api/memory.rst index 8968b26b64320a..87b8603c4c0b10 100644 --- a/Doc/c-api/memory.rst +++ b/Doc/c-api/memory.rst @@ -487,18 +487,18 @@ Customize Memory Allocators :c:func:`PyMem_SetAllocator` does have the following contract: - * It can be called after :c:func:`Py_PreInitialize` and before - :c:func:`Py_InitializeFromConfig` to install a custom memory - allocator. There are no restrictions over the installed allocator - other than the ones imposed by the domain (for instance, the Raw - Domain allows the allocator to be called without the GIL held). See - :ref:`the section on allocator domains ` for more - information. - - * If called after Python has finish initializing (after - :c:func:`Py_InitializeFromConfig` has been called) the allocator - **must** wrap the existing allocator. Substituting the current - allocator for some other arbitrary one is **not supported**. + * It can be called after :c:func:`Py_PreInitialize` and before + :c:func:`Py_InitializeFromConfig` to install a custom memory + allocator. There are no restrictions over the installed allocator + other than the ones imposed by the domain (for instance, the Raw + Domain allows the allocator to be called without the GIL held). See + :ref:`the section on allocator domains ` for more + information. + + * If called after Python has finish initializing (after + :c:func:`Py_InitializeFromConfig` has been called) the allocator + **must** wrap the existing allocator. Substituting the current + allocator for some other arbitrary one is **not supported**. diff --git a/Doc/howto/enum.rst b/Doc/howto/enum.rst index 465be653b51adb..58849429539cca 100644 --- a/Doc/howto/enum.rst +++ b/Doc/howto/enum.rst @@ -1119,13 +1119,14 @@ the following are true: There is a new boundary mechanism that controls how out-of-range / invalid bits are handled: ``STRICT``, ``CONFORM``, ``EJECT``, and ``KEEP``: - * STRICT --> raises an exception when presented with invalid values - * CONFORM --> discards any invalid bits - * EJECT --> lose Flag status and become a normal int with the given value - * KEEP --> keep the extra bits - - keeps Flag status and extra bits - - extra bits do not show up in iteration - - extra bits do show up in repr() and str() +* STRICT --> raises an exception when presented with invalid values +* CONFORM --> discards any invalid bits +* EJECT --> lose Flag status and become a normal int with the given value +* KEEP --> keep the extra bits + + - keeps Flag status and extra bits + - extra bits do not show up in iteration + - extra bits do show up in repr() and str() The default for Flag is ``STRICT``, the default for ``IntFlag`` is ``EJECT``, and the default for ``_convert_`` is ``KEEP`` (see ``ssl.Options`` for an diff --git a/Doc/howto/instrumentation.rst b/Doc/howto/instrumentation.rst index 875f846aad0051..9c99fcecce1fcb 100644 --- a/Doc/howto/instrumentation.rst +++ b/Doc/howto/instrumentation.rst @@ -13,9 +13,9 @@ DTrace and SystemTap are monitoring tools, each providing a way to inspect what the processes on a computer system are doing. They both use domain-specific languages allowing a user to write scripts which: - - filter which processes are to be observed - - gather data from the processes of interest - - generate reports on the data +- filter which processes are to be observed +- gather data from the processes of interest +- generate reports on the data As of Python 3.6, CPython can be built with embedded "markers", also known as "probes", that can be observed by a DTrace or SystemTap script, @@ -246,11 +246,9 @@ The output looks like this: where the columns are: - - time in microseconds since start of script - - - name of executable - - - PID of process +- time in microseconds since start of script +- name of executable +- PID of process and the remainder indicates the call/return hierarchy as the script executes. diff --git a/Doc/library/csv.rst b/Doc/library/csv.rst index 0aad2a0d523cb0..52f76304a10529 100644 --- a/Doc/library/csv.rst +++ b/Doc/library/csv.rst @@ -284,9 +284,9 @@ The :mod:`csv` module defines the following classes: Inspecting each column, one of two key criteria will be considered to estimate if the sample contains a header: - - the second through n-th rows contain numeric values - - the second through n-th rows contain strings where at least one value's - length differs from that of the putative header of that column. + - the second through n-th rows contain numeric values + - the second through n-th rows contain strings where at least one value's + length differs from that of the putative header of that column. Twenty rows after the first row are sampled; if more than half of columns + rows meet the criteria, :const:`True` is returned. diff --git a/Doc/library/dataclasses.rst b/Doc/library/dataclasses.rst index 09f4ce812dc83a..915ff05d98a711 100644 --- a/Doc/library/dataclasses.rst +++ b/Doc/library/dataclasses.rst @@ -319,13 +319,11 @@ Module contents module-level method (see below). Users should never instantiate a :class:`Field` object directly. Its documented attributes are: - - ``name``: The name of the field. - - - ``type``: The type of the field. - - - ``default``, ``default_factory``, ``init``, ``repr``, ``hash``, - ``compare``, ``metadata``, and ``kw_only`` have the identical - meaning and values as they do in the :func:`field` function. + - ``name``: The name of the field. + - ``type``: The type of the field. + - ``default``, ``default_factory``, ``init``, ``repr``, ``hash``, + ``compare``, ``metadata``, and ``kw_only`` have the identical + meaning and values as they do in the :func:`field` function. Other attributes may exist, but they are private and must not be inspected or relied on. diff --git a/Doc/library/decimal.rst b/Doc/library/decimal.rst index d3e920da5df368..b4e3430b1008f1 100644 --- a/Doc/library/decimal.rst +++ b/Doc/library/decimal.rst @@ -1396,10 +1396,10 @@ In addition to the three supplied contexts, new contexts can be created with the With three arguments, compute ``(x**y) % modulo``. For the three argument form, the following restrictions on the arguments hold: - - all three arguments must be integral - - ``y`` must be nonnegative - - at least one of ``x`` or ``y`` must be nonzero - - ``modulo`` must be nonzero and have at most 'precision' digits + - all three arguments must be integral + - ``y`` must be nonnegative + - at least one of ``x`` or ``y`` must be nonzero + - ``modulo`` must be nonzero and have at most 'precision' digits The value resulting from ``Context.power(x, y, modulo)`` is equal to the value that would be obtained by computing ``(x**y) diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst index 4c71b1db82cc6f..72c3a2b230a326 100644 --- a/Doc/using/windows.rst +++ b/Doc/using/windows.rst @@ -1174,21 +1174,22 @@ Otherwise, your users may experience problems using your application. Note that the first suggestion is the best, as the others may still be susceptible to non-standard paths in the registry and user site-packages. -.. versionchanged:: - 3.6 +.. versionchanged:: 3.6 + + Add ``._pth`` file support and removes ``applocal`` option from + ``pyvenv.cfg``. + +.. versionchanged:: 3.6 - * Adds ``._pth`` file support and removes ``applocal`` option from - ``pyvenv.cfg``. - * Adds :file:`python{XX}.zip` as a potential landmark when directly adjacent - to the executable. + Add :file:`python{XX}.zip` as a potential landmark when directly adjacent + to the executable. -.. deprecated:: - 3.6 +.. deprecated:: 3.6 - Modules specified in the registry under ``Modules`` (not ``PythonPath``) - may be imported by :class:`importlib.machinery.WindowsRegistryFinder`. - This finder is enabled on Windows in 3.6.0 and earlier, but may need to - be explicitly added to :data:`sys.meta_path` in the future. + Modules specified in the registry under ``Modules`` (not ``PythonPath``) + may be imported by :class:`importlib.machinery.WindowsRegistryFinder`. + This finder is enabled on Windows in 3.6.0 and earlier, but may need to + be explicitly added to :data:`sys.meta_path` in the future. Additional modules ================== From eb492d32b688534dfa0f956c0e7fa157dfacc2c3 Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Thu, 12 Oct 2023 02:01:48 +0200 Subject: [PATCH 465/632] [3.11] gh-110631: Fix reST indentation in `Doc/reference` (GH-110708) (#110741) Fix wrong indentation in the Doc/reference dir.. (cherry picked from commit 41d8ec5a1bae1e5d4452da0a1a0649ace4ecb7b0) --- Doc/reference/compound_stmts.rst | 48 +++++++-------- Doc/reference/expressions.rst | 4 +- Doc/reference/import.rst | 94 +++++++++++++++--------------- Doc/reference/lexical_analysis.rst | 8 +-- 4 files changed, 78 insertions(+), 76 deletions(-) diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index fb0daf89357f90..19df7f63f12536 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -644,14 +644,14 @@ Here's an overview of the logical flow of a match statement: specified below. **Name bindings made during a successful pattern match outlive the executed block and can be used after the match statement**. - .. note:: + .. note:: - During failed pattern matches, some subpatterns may succeed. Do not - rely on bindings being made for a failed match. Conversely, do not - rely on variables remaining unchanged after a failed match. The exact - behavior is dependent on implementation and may vary. This is an - intentional decision made to allow different implementations to add - optimizations. + During failed pattern matches, some subpatterns may succeed. Do not + rely on bindings being made for a failed match. Conversely, do not + rely on variables remaining unchanged after a failed match. The exact + behavior is dependent on implementation and may vary. This is an + intentional decision made to allow different implementations to add + optimizations. #. If the pattern succeeds, the corresponding guard (if present) is evaluated. In this case all name bindings are guaranteed to have happened. @@ -1172,8 +1172,10 @@ In simple terms ``CLS(P1, attr=P2)`` matches only if the following happens: * ``isinstance(, CLS)`` * convert ``P1`` to a keyword pattern using ``CLS.__match_args__`` * For each keyword argument ``attr=P2``: - * ``hasattr(, "attr")`` - * ``P2`` matches ``.attr`` + + * ``hasattr(, "attr")`` + * ``P2`` matches ``.attr`` + * ... and so on for the corresponding keyword argument/pattern pair. .. seealso:: @@ -1600,29 +1602,29 @@ body of a coroutine function. .. [#] In pattern matching, a sequence is defined as one of the following: - * a class that inherits from :class:`collections.abc.Sequence` - * a Python class that has been registered as :class:`collections.abc.Sequence` - * a builtin class that has its (CPython) :c:macro:`Py_TPFLAGS_SEQUENCE` bit set - * a class that inherits from any of the above + * a class that inherits from :class:`collections.abc.Sequence` + * a Python class that has been registered as :class:`collections.abc.Sequence` + * a builtin class that has its (CPython) :c:macro:`Py_TPFLAGS_SEQUENCE` bit set + * a class that inherits from any of the above The following standard library classes are sequences: - * :class:`array.array` - * :class:`collections.deque` - * :class:`list` - * :class:`memoryview` - * :class:`range` - * :class:`tuple` + * :class:`array.array` + * :class:`collections.deque` + * :class:`list` + * :class:`memoryview` + * :class:`range` + * :class:`tuple` .. note:: Subject values of type ``str``, ``bytes``, and ``bytearray`` do not match sequence patterns. .. [#] In pattern matching, a mapping is defined as one of the following: - * a class that inherits from :class:`collections.abc.Mapping` - * a Python class that has been registered as :class:`collections.abc.Mapping` - * a builtin class that has its (CPython) :c:macro:`Py_TPFLAGS_MAPPING` bit set - * a class that inherits from any of the above + * a class that inherits from :class:`collections.abc.Mapping` + * a Python class that has been registered as :class:`collections.abc.Mapping` + * a builtin class that has its (CPython) :c:macro:`Py_TPFLAGS_MAPPING` bit set + * a class that inherits from any of the above The standard library classes :class:`dict` and :class:`types.MappingProxyType` are mappings. diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index 8320cd0ae75193..3c4706714f306c 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -499,8 +499,8 @@ the yield expression. It can be either set explicitly when raising :exc:`StopIteration`, or automatically when the subiterator is a generator (by returning a value from the subgenerator). - .. versionchanged:: 3.3 - Added ``yield from `` to delegate control flow to a subiterator. +.. versionchanged:: 3.3 + Added ``yield from `` to delegate control flow to a subiterator. The parentheses may be omitted when the yield expression is the sole expression on the right hand side of an assignment statement. diff --git a/Doc/reference/import.rst b/Doc/reference/import.rst index 70d946ab4db9e2..c44fb9815e9f99 100644 --- a/Doc/reference/import.rst +++ b/Doc/reference/import.rst @@ -373,32 +373,32 @@ of what happens during the loading portion of import:: Note the following details: - * If there is an existing module object with the given name in - :data:`sys.modules`, import will have already returned it. +* If there is an existing module object with the given name in + :data:`sys.modules`, import will have already returned it. - * The module will exist in :data:`sys.modules` before the loader - executes the module code. This is crucial because the module code may - (directly or indirectly) import itself; adding it to :data:`sys.modules` - beforehand prevents unbounded recursion in the worst case and multiple - loading in the best. +* The module will exist in :data:`sys.modules` before the loader + executes the module code. This is crucial because the module code may + (directly or indirectly) import itself; adding it to :data:`sys.modules` + beforehand prevents unbounded recursion in the worst case and multiple + loading in the best. - * If loading fails, the failing module -- and only the failing module -- - gets removed from :data:`sys.modules`. Any module already in the - :data:`sys.modules` cache, and any module that was successfully loaded - as a side-effect, must remain in the cache. This contrasts with - reloading where even the failing module is left in :data:`sys.modules`. +* If loading fails, the failing module -- and only the failing module -- + gets removed from :data:`sys.modules`. Any module already in the + :data:`sys.modules` cache, and any module that was successfully loaded + as a side-effect, must remain in the cache. This contrasts with + reloading where even the failing module is left in :data:`sys.modules`. - * After the module is created but before execution, the import machinery - sets the import-related module attributes ("_init_module_attrs" in - the pseudo-code example above), as summarized in a - :ref:`later section `. +* After the module is created but before execution, the import machinery + sets the import-related module attributes ("_init_module_attrs" in + the pseudo-code example above), as summarized in a + :ref:`later section `. - * Module execution is the key moment of loading in which the module's - namespace gets populated. Execution is entirely delegated to the - loader, which gets to decide what gets populated and how. +* Module execution is the key moment of loading in which the module's + namespace gets populated. Execution is entirely delegated to the + loader, which gets to decide what gets populated and how. - * The module created during loading and passed to exec_module() may - not be the one returned at the end of import [#fnlo]_. +* The module created during loading and passed to exec_module() may + not be the one returned at the end of import [#fnlo]_. .. versionchanged:: 3.4 The import system has taken over the boilerplate responsibilities of @@ -415,13 +415,13 @@ returned from :meth:`~importlib.abc.Loader.exec_module` is ignored. Loaders must satisfy the following requirements: - * If the module is a Python module (as opposed to a built-in module or a - dynamically loaded extension), the loader should execute the module's code - in the module's global name space (``module.__dict__``). +* If the module is a Python module (as opposed to a built-in module or a + dynamically loaded extension), the loader should execute the module's code + in the module's global name space (``module.__dict__``). - * If the loader cannot execute the module, it should raise an - :exc:`ImportError`, although any other exception raised during - :meth:`~importlib.abc.Loader.exec_module` will be propagated. +* If the loader cannot execute the module, it should raise an + :exc:`ImportError`, although any other exception raised during + :meth:`~importlib.abc.Loader.exec_module` will be propagated. In many cases, the finder and loader can be the same object; in such cases the :meth:`~importlib.abc.MetaPathFinder.find_spec` method would just return a @@ -451,20 +451,20 @@ import machinery will create the new module itself. functionality described above in addition to executing the module. All the same constraints apply, with some additional clarification: - * If there is an existing module object with the given name in - :data:`sys.modules`, the loader must use that existing module. - (Otherwise, :func:`importlib.reload` will not work correctly.) If the - named module does not exist in :data:`sys.modules`, the loader - must create a new module object and add it to :data:`sys.modules`. + * If there is an existing module object with the given name in + :data:`sys.modules`, the loader must use that existing module. + (Otherwise, :func:`importlib.reload` will not work correctly.) If the + named module does not exist in :data:`sys.modules`, the loader + must create a new module object and add it to :data:`sys.modules`. - * The module *must* exist in :data:`sys.modules` before the loader - executes the module code, to prevent unbounded recursion or multiple - loading. + * The module *must* exist in :data:`sys.modules` before the loader + executes the module code, to prevent unbounded recursion or multiple + loading. - * If loading fails, the loader must remove any modules it has inserted - into :data:`sys.modules`, but it must remove **only** the failing - module(s), and only if the loader itself has loaded the module(s) - explicitly. + * If loading fails, the loader must remove any modules it has inserted + into :data:`sys.modules`, but it must remove **only** the failing + module(s), and only if the loader itself has loaded the module(s) + explicitly. .. versionchanged:: 3.5 A :exc:`DeprecationWarning` is raised when ``exec_module()`` is defined but @@ -664,17 +664,17 @@ with defaults for whatever information is missing. Here are the exact rules used: - * If the module has a ``__spec__`` attribute, the information in the spec - is used to generate the repr. The "name", "loader", "origin", and - "has_location" attributes are consulted. +* If the module has a ``__spec__`` attribute, the information in the spec + is used to generate the repr. The "name", "loader", "origin", and + "has_location" attributes are consulted. - * If the module has a ``__file__`` attribute, this is used as part of the - module's repr. +* If the module has a ``__file__`` attribute, this is used as part of the + module's repr. - * If the module has no ``__file__`` but does have a ``__loader__`` that is not - ``None``, then the loader's repr is used as part of the module's repr. +* If the module has no ``__file__`` but does have a ``__loader__`` that is not + ``None``, then the loader's repr is used as part of the module's repr. - * Otherwise, just use the module's ``__name__`` in the repr. +* Otherwise, just use the module's ``__name__`` in the repr. .. versionchanged:: 3.4 Use of :meth:`loader.module_repr() ` diff --git a/Doc/reference/lexical_analysis.rst b/Doc/reference/lexical_analysis.rst index 4fadae3ea2b045..b25d2c862927fe 100644 --- a/Doc/reference/lexical_analysis.rst +++ b/Doc/reference/lexical_analysis.rst @@ -641,10 +641,10 @@ is more easily recognized as broken.) It is also important to note that the escape sequences only recognized in string literals fall into the category of unrecognized escapes for bytes literals. - .. versionchanged:: 3.6 - Unrecognized escape sequences produce a :exc:`DeprecationWarning`. In - a future Python version they will be a :exc:`SyntaxWarning` and - eventually a :exc:`SyntaxError`. +.. versionchanged:: 3.6 + Unrecognized escape sequences produce a :exc:`DeprecationWarning`. In + a future Python version they will be a :exc:`SyntaxWarning` and + eventually a :exc:`SyntaxError`. Even in a raw literal, quotes can be escaped with a backslash, but the backslash remains in the result; for example, ``r"\""`` is a valid string From f98903542c55823bf0f149840d81729003ffc9d0 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 12 Oct 2023 10:09:49 +0200 Subject: [PATCH 466/632] [3.11] gh-110673: test_pty raises on short write (GH-110677) (#110743) gh-110673: test_pty raises on short write (GH-110677) Add write_all() helper function to test_pty to raise an exception on short write: if os.writes() does not write all bytes. It should not happen for a PTY. (cherry picked from commit b4e8049766a46a9e6548b18d7e9a0c9f573cd122) Co-authored-by: Victor Stinner --- Lib/test/test_pty.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_pty.py b/Lib/test/test_pty.py index a971f6b0250efb..f31a68c5d84e03 100644 --- a/Lib/test/test_pty.py +++ b/Lib/test/test_pty.py @@ -76,6 +76,15 @@ def expectedFailureIfStdinIsTTY(fun): pass return fun + +def write_all(fd, data): + written = os.write(fd, data) + if written != len(data): + # gh-73256, gh-110673: It should never happen, but check just in case + raise Exception(f"short write: os.write({fd}, {len(data)} bytes) " + f"wrote {written} bytes") + + # Marginal testing of pty suite. Cannot do extensive 'do or fail' testing # because pty code is not too portable. class PtyTest(unittest.TestCase): @@ -170,14 +179,14 @@ def test_openpty(self): os.set_blocking(master_fd, blocking) debug("Writing to slave_fd") - os.write(slave_fd, TEST_STRING_1) + write_all(slave_fd, TEST_STRING_1) s1 = _readline(master_fd) self.assertEqual(b'I wish to buy a fish license.\n', normalize_output(s1)) debug("Writing chunked output") - os.write(slave_fd, TEST_STRING_2[:5]) - os.write(slave_fd, TEST_STRING_2[5:]) + write_all(slave_fd, TEST_STRING_2[:5]) + write_all(slave_fd, TEST_STRING_2[5:]) s2 = _readline(master_fd) self.assertEqual(b'For my pet fish, Eric.\n', normalize_output(s2)) @@ -360,8 +369,8 @@ def test__copy_to_each(self): masters = [s.fileno() for s in socketpair] # Feed data. Smaller than PIPEBUF. These writes will not block. - os.write(masters[1], b'from master') - os.write(write_to_stdin_fd, b'from stdin') + write_all(masters[1], b'from master') + write_all(write_to_stdin_fd, b'from stdin') # Expect three select calls, the last one will cause IndexError pty.select = self._mock_select From 5178fb0b89586be2b572bae7fc04a7e63168eee8 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 12 Oct 2023 11:52:59 +0200 Subject: [PATCH 467/632] [3.11] GH-107518: Remove the Argument Clinic How-To (#109900) (#110761) (cherry picked from commit d1f7fae424d51b0374c8204599583c4a26c1a992) * Remove the content of the Argument Clinic HOWTO * Update cross-references to the Argument Clinic * Add a note directing readers to the devguide Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/howto/clinic.rst | 1926 +--------------------------------------- Doc/howto/index.rst | 1 - Doc/whatsnew/3.8.rst | 2 +- Misc/NEWS.d/3.11.5.rst | 2 +- 4 files changed, 7 insertions(+), 1924 deletions(-) diff --git a/Doc/howto/clinic.rst b/Doc/howto/clinic.rst index 5f4d3977bc1605..060977246149cf 100644 --- a/Doc/howto/clinic.rst +++ b/Doc/howto/clinic.rst @@ -1,1930 +1,14 @@ -.. highlight:: c +:orphan: -.. _howto-clinic: +.. This page is retained solely for existing links to /howto/clinic.html. + Direct readers to the devguide. ********************** Argument Clinic How-To ********************** -:author: Larry Hastings - -**Source code:** :source:`Tools/clinic/clinic.py`. - -.. topic:: Abstract - - Argument Clinic is a preprocessor for CPython C files. - It was introduced in Python 3.4 with :pep:`436`, - in order to provide introspection signatures, - and to generate performant and tailor-made boilerplate code - for argument parsing in CPython builtins, - module level functions, and class methods. - This document is divided in four major sections: - - * :ref:`clinic-background` talks about the basic concepts and goals of - Argument Clinic. - * :ref:`clinic-reference` describes the command-line interface and Argument - Clinic terminology. - * :ref:`clinic-tutorial` guides you through all the steps required to - adapt an existing C function to Argument Clinic. - * :ref:`clinic-howtos` details how to handle specific tasks. - - -.. note:: - - Argument Clinic is considered internal-only - for CPython. Its use is not supported for files outside - CPython, and no guarantees are made regarding backwards - compatibility for future versions. In other words: if you - maintain an external C extension for CPython, you're welcome - to experiment with Argument Clinic in your own code. But the - version of Argument Clinic that ships with the next version - of CPython *could* be totally incompatible and break all your code. - - -.. _clinic-background: - -Background -========== - -Basic concepts --------------- - -When Argument Clinic is run on a file, either via the :ref:`clinic-cli` -or via ``make clinic``, it will scan over the input files looking for -:term:`start lines `: - -.. code-block:: none - - /*[clinic input] - -When it finds one, it reads everything up to the :term:`end line`: - -.. code-block:: none - - [clinic start generated code]*/ - -Everything in between these two lines is Argument Clinic :term:`input`. -When Argument Clinic parses input, it generates :term:`output`. -The output is rewritten into the C file immediately after the input, -followed by a :term:`checksum line`. -All of these lines, including the :term:`start line` and :term:`checksum line`, -are collectively called an Argument Clinic :term:`block`: - -.. code-block:: none - - /*[clinic input] - ... clinic input goes here ... - [clinic start generated code]*/ - ... clinic output goes here ... - /*[clinic end generated code: ...]*/ - -If you run Argument Clinic on the same file a second time, Argument Clinic -will discard the old :term:`output` and write out the new output with a fresh -:term:`checksum line`. -If the :term:`input` hasn't changed, the output won't change either. .. note:: - You should never modify the output of an Argument Clinic block, - as any change will be lost in future Argument Clinic runs; - Argument Clinic will detect an output checksum mismatch and regenerate the - correct output. - If you are not happy with the generated output, - you should instead change the input until it produces the output you want. - - -.. _clinic-reference: - -Reference -========= - - -.. _clinic-terminology: - -Terminology ------------ - -.. glossary:: - - start line - The line ``/*[clinic input]``. - This line marks the beginning of Argument Clinic input. - Note that the *start line* opens a C block comment. - - end line - The line ``[clinic start generated code]*/``. - The *end line* marks the _end_ of Argument Clinic :term:`input`, - but at the same time marks the _start_ of Argument Clinic :term:`output`, - thus the text *"clinic start start generated code"* - Note that the *end line* closes the C block comment opened - by the *start line*. - - checksum - A hash to distinguish unique :term:`inputs ` - and :term:`outputs `. - - checksum line - A line that looks like ``/*[clinic end generated code: ...]*/``. - The three dots will be replaced by a :term:`checksum` generated from the - :term:`input`, and a :term:`checksum` generated from the :term:`output`. - The checksum line marks the end of Argument Clinic generated code, - and is used by Argument Clinic to determine if it needs to regenerate - output. - - input - The text between the :term:`start line` and the :term:`end line`. - Note that the start and end lines open and close a C block comment; - the *input* is thus a part of that same C block comment. - - output - The text between the :term:`end line` and the :term:`checksum line`. - - block - All text from the :term:`start line` to the :term:`checksum line` inclusively. - - -.. _clinic-cli: - -Command-line interface ----------------------- - -The Argument Clinic :abbr:`CLI (Command-Line Interface)` is typically used to -process a single source file, like this: - -.. code-block:: shell-session - - $ python3 ./Tools/clinic/clinic.py foo.c - -The CLI supports the following options: - -.. program:: ./Tools/clinic/clinic.py [-h] [-f] [-o OUTPUT] [-v] \ - [--converters] [--make] [--srcdir SRCDIR] [FILE ...] - -.. option:: -h, --help - - Print CLI usage. - -.. option:: -f, --force - - Force output regeneration. - -.. option:: -o, --output OUTPUT - - Redirect file output to OUTPUT - -.. option:: -v, --verbose - - Enable verbose mode. - -.. option:: --converters - - Print a list of all supported converters and return converters. - -.. option:: --make - - Walk :option:`--srcdir` to run over all relevant files. - -.. option:: --srcdir SRCDIR - - The directory tree to walk in :option:`--make` mode. - -.. option:: FILE ... - - The list of files to process. - - -.. _clinic-classes: - -Classes for extending Argument Clinic -------------------------------------- - -.. module:: clinic - -.. class:: CConverter - - The base class for all converters. - See :ref:`clinic-howto-custom-converter` for how to subclass this class. - - .. attribute:: type - - The C type to use for this variable. - :attr:`!type` should be a Python string specifying the type, - e.g. ``'int'``. - If this is a pointer type, the type string should end with ``' *'``. - - .. attribute:: default - - The Python default value for this parameter, as a Python value. - Or the magic value ``unspecified`` if there is no default. - - .. attribute:: py_default - - :attr:`!default` as it should appear in Python code, - as a string. - Or ``None`` if there is no default. - - .. attribute:: c_default - - :attr:`!default` as it should appear in C code, - as a string. - Or ``None`` if there is no default. - - .. attribute:: c_ignored_default - - The default value used to initialize the C variable when - there is no default, but not specifying a default may - result in an "uninitialized variable" warning. This can - easily happen when using option groups—although - properly written code will never actually use this value, - the variable does get passed in to the impl, and the - C compiler will complain about the "use" of the - uninitialized value. This value should always be a - non-empty string. - - .. attribute:: converter - - The name of the C converter function, as a string. - - .. attribute:: impl_by_reference - - A boolean value. If true, - Argument Clinic will add a ``&`` in front of the name of - the variable when passing it into the impl function. - - .. attribute:: parse_by_reference - - A boolean value. If true, - Argument Clinic will add a ``&`` in front of the name of - the variable when passing it into :c:func:`PyArg_ParseTuple`. - - -.. _clinic-tutorial: - -Tutorial -======== - -The best way to get a sense of how Argument Clinic works is to -convert a function to work with it. Here, then, are the bare -minimum steps you'd need to follow to convert a function to -work with Argument Clinic. Note that for code you plan to -check in to CPython, you really should take the conversion farther, -using some of the :ref:`advanced concepts ` -you'll see later on in the document, -like :ref:`clinic-howto-return-converters` -and :ref:`clinic-howto-self-converter`. -But we'll keep it simple for this walkthrough so you can learn. - -First, make sure you're working with a freshly updated checkout -of the CPython trunk. - -Next, find a Python builtin that calls either :c:func:`PyArg_ParseTuple` -or :c:func:`PyArg_ParseTupleAndKeywords`, and hasn't been converted -to work with Argument Clinic yet. -For this tutorial, we'll be using -:py:meth:`_pickle.Pickler.dump `. - -If the call to the :c:func:`!PyArg_Parse*` function uses any of the -following format units...: - - .. code-block:: none - - O& - O! - es - es# - et - et# - -... or if it has multiple calls to :c:func:`PyArg_ParseTuple`, -you should choose a different function. -(See :ref:`clinic-howto-advanced-converters` for those scenarios.) - -Also, if the function has multiple calls to :c:func:`!PyArg_ParseTuple` -or :c:func:`PyArg_ParseTupleAndKeywords` where it supports different -types for the same argument, or if the function uses something besides -:c:func:`!PyArg_Parse*` functions to parse its arguments, it probably -isn't suitable for conversion to Argument Clinic. Argument Clinic -doesn't support generic functions or polymorphic parameters. - -Next, add the following boilerplate above the function, -creating our input block:: - - /*[clinic input] - [clinic start generated code]*/ - -Cut the docstring and paste it in between the ``[clinic]`` lines, -removing all the junk that makes it a properly quoted C string. -When you're done you should have just the text, based at the left -margin, with no line wider than 80 characters. -Argument Clinic will preserve indents inside the docstring. - -If the old docstring had a first line that looked like a function -signature, throw that line away; The docstring doesn't need it anymore --- -when you use :py:func:`help` on your builtin in the future, -the first line will be built automatically based on the function's signature. - -Example docstring summary line:: - - /*[clinic input] - Write a pickled representation of obj to the open file. - [clinic start generated code]*/ - -If your docstring doesn't have a "summary" line, Argument Clinic will -complain, so let's make sure it has one. The "summary" line should -be a paragraph consisting of a single 80-column line -at the beginning of the docstring. -(See :pep:`257` regarding docstring conventions.) - -Our example docstring consists solely of a summary line, so the sample -code doesn't have to change for this step. - -Now, above the docstring, enter the name of the function, followed -by a blank line. This should be the Python name of the function, -and should be the full dotted path to the function --- -it should start with the name of the module, -include any sub-modules, and if the function is a method on -a class it should include the class name too. - -In our example, :mod:`!_pickle` is the module, :py:class:`!Pickler` is the class, -and :py:meth:`!dump` is the method, so the name becomes -:py:meth:`!_pickle.Pickler.dump`:: - - /*[clinic input] - _pickle.Pickler.dump - - Write a pickled representation of obj to the open file. - [clinic start generated code]*/ - -If this is the first time that module or class has been used with Argument -Clinic in this C file, -you must declare the module and/or class. Proper Argument Clinic hygiene -prefers declaring these in a separate block somewhere near the -top of the C file, in the same way that include files and statics go at -the top. -In our sample code we'll just show the two blocks next to each other. - -The name of the class and module should be the same as the one -seen by Python. Check the name defined in the :c:type:`PyModuleDef` -or :c:type:`PyTypeObject` as appropriate. - -When you declare a class, you must also specify two aspects of its type -in C: the type declaration you'd use for a pointer to an instance of -this class, and a pointer to the :c:type:`!PyTypeObject` for this class:: - - /*[clinic input] - module _pickle - class _pickle.Pickler "PicklerObject *" "&Pickler_Type" - [clinic start generated code]*/ - - /*[clinic input] - _pickle.Pickler.dump - - Write a pickled representation of obj to the open file. - [clinic start generated code]*/ - -Declare each of the parameters to the function. Each parameter -should get its own line. All the parameter lines should be -indented from the function name and the docstring. -The general form of these parameter lines is as follows: - -.. code-block:: none - - name_of_parameter: converter - -If the parameter has a default value, add that after the -converter: - -.. code-block:: none - - name_of_parameter: converter = default_value - -Argument Clinic's support for "default values" is quite sophisticated; -see :ref:`clinic-howto-default-values` for more information. - -Next, add a blank line below the parameters. - -What's a "converter"? -It establishes both the type of the variable used in C, -and the method to convert the Python value into a C value at runtime. -For now you're going to use what's called a "legacy converter" --- -a convenience syntax intended to make porting old code into Argument -Clinic easier. - -For each parameter, copy the "format unit" for that -parameter from the :c:func:`PyArg_Parse` format argument and -specify *that* as its converter, as a quoted string. -The "format unit" is the formal name for the one-to-three -character substring of the *format* parameter that tells -the argument parsing function what the type of the variable -is and how to convert it. -For more on format units please see :ref:`arg-parsing`. - -For multicharacter format units like ``z#``, -use the entire two-or-three character string. - -Sample:: - - /*[clinic input] - module _pickle - class _pickle.Pickler "PicklerObject *" "&Pickler_Type" - [clinic start generated code]*/ - - /*[clinic input] - _pickle.Pickler.dump - - obj: 'O' - - Write a pickled representation of obj to the open file. - [clinic start generated code]*/ - -If your function has ``|`` in the format string, -meaning some parameters have default values, you can ignore it. -Argument Clinic infers which parameters are optional -based on whether or not they have default values. - -If your function has ``$`` in the format string, -meaning it takes keyword-only arguments, -specify ``*`` on a line by itself before the first keyword-only argument, -indented the same as the parameter lines. - -:py:meth:`!_pickle.Pickler.dump` has neither, so our sample is unchanged. - -Next, if the existing C function calls :c:func:`PyArg_ParseTuple` -(as opposed to :c:func:`PyArg_ParseTupleAndKeywords`), then all its -arguments are positional-only. - -To mark parameters as positional-only in Argument Clinic, -add a ``/`` on a line by itself after the last positional-only parameter, -indented the same as the parameter lines. - -Sample:: - - /*[clinic input] - module _pickle - class _pickle.Pickler "PicklerObject *" "&Pickler_Type" - [clinic start generated code]*/ - - /*[clinic input] - _pickle.Pickler.dump - - obj: 'O' - / - - Write a pickled representation of obj to the open file. - [clinic start generated code]*/ - -It can be helpful to write a per-parameter docstring for each parameter. -Since per-parameter docstrings are optional, -you can skip this step if you prefer. - -Nevertheless, here's how to add a per-parameter docstring. -The first line of the per-parameter docstring -must be indented further than the parameter definition. -The left margin of this first line establishes -the left margin for the whole per-parameter docstring; -all the text you write will be outdented by this amount. -You can write as much text as you like, across multiple lines if you wish. - -Sample:: - - /*[clinic input] - module _pickle - class _pickle.Pickler "PicklerObject *" "&Pickler_Type" - [clinic start generated code]*/ - - /*[clinic input] - _pickle.Pickler.dump - - obj: 'O' - The object to be pickled. - / - - Write a pickled representation of obj to the open file. - [clinic start generated code]*/ - -Save and close the file, then run ``Tools/clinic/clinic.py`` on it. -With luck everything worked---your block now has output, -and a :file:`.c.h` file has been generated! -Reload the file in your text editor to see the generated code:: - - /*[clinic input] - _pickle.Pickler.dump - - obj: 'O' - The object to be pickled. - / - - Write a pickled representation of obj to the open file. - [clinic start generated code]*/ - - static PyObject * - _pickle_Pickler_dump(PicklerObject *self, PyObject *obj) - /*[clinic end generated code: output=87ecad1261e02ac7 input=552eb1c0f52260d9]*/ - -Obviously, if Argument Clinic didn't produce any output, -it's because it found an error in your input. -Keep fixing your errors and retrying until Argument Clinic processes your file -without complaint. - -For readability, most of the glue code has been generated to a :file:`.c.h` -file. You'll need to include that in your original :file:`.c` file, -typically right after the clinic module block:: - - #include "clinic/_pickle.c.h" - -Double-check that the argument-parsing code Argument Clinic generated -looks basically the same as the existing code. - -First, ensure both places use the same argument-parsing function. -The existing code must call either -:c:func:`PyArg_ParseTuple` or :c:func:`PyArg_ParseTupleAndKeywords`; -ensure that the code generated by Argument Clinic calls the -*exact* same function. - -Second, the format string passed in to :c:func:`!PyArg_ParseTuple` or -:c:func:`!PyArg_ParseTupleAndKeywords` should be *exactly* the same -as the hand-written one in the existing function, -up to the colon or semi-colon. - -Argument Clinic always generates its format strings -with a ``:`` followed by the name of the function. -If the existing code's format string ends with ``;``, -to provide usage help, this change is harmless --- don't worry about it. - -Third, for parameters whose format units require two arguments, -like a length variable, an encoding string, or a pointer -to a conversion function, ensure that the second argument is -*exactly* the same between the two invocations. - -Fourth, inside the output portion of the block, -you'll find a preprocessor macro defining the appropriate static -:c:type:`PyMethodDef` structure for this builtin:: - - #define __PICKLE_PICKLER_DUMP_METHODDEF \ - {"dump", (PyCFunction)__pickle_Pickler_dump, METH_O, __pickle_Pickler_dump__doc__}, - -This static structure should be *exactly* the same as the existing static -:c:type:`!PyMethodDef` structure for this builtin. - -If any of these items differ in *any way*, -adjust your Argument Clinic function specification and rerun -``Tools/clinic/clinic.py`` until they *are* the same. - -Notice that the last line of its output is the declaration -of your "impl" function. This is where the builtin's implementation goes. -Delete the existing prototype of the function you're modifying, but leave -the opening curly brace. Now delete its argument parsing code and the -declarations of all the variables it dumps the arguments into. -Notice how the Python arguments are now arguments to this impl function; -if the implementation used different names for these variables, fix it. - -Let's reiterate, just because it's kind of weird. -Your code should now look like this:: - - static return_type - your_function_impl(...) - /*[clinic end generated code: input=..., output=...]*/ - { - ... - -Argument Clinic generated the checksum line and the function prototype just -above it. You should write the opening and closing curly braces for the -function, and the implementation inside. - -Sample:: - - /*[clinic input] - module _pickle - class _pickle.Pickler "PicklerObject *" "&Pickler_Type" - [clinic start generated code]*/ - /*[clinic end generated code: checksum=da39a3ee5e6b4b0d3255bfef95601890afd80709]*/ - - /*[clinic input] - _pickle.Pickler.dump - - obj: 'O' - The object to be pickled. - / - - Write a pickled representation of obj to the open file. - [clinic start generated code]*/ - - PyDoc_STRVAR(__pickle_Pickler_dump__doc__, - "Write a pickled representation of obj to the open file.\n" - "\n" - ... - static PyObject * - _pickle_Pickler_dump_impl(PicklerObject *self, PyObject *obj) - /*[clinic end generated code: checksum=3bd30745bf206a48f8b576a1da3d90f55a0a4187]*/ - { - /* Check whether the Pickler was initialized correctly (issue3664). - Developers often forget to call __init__() in their subclasses, which - would trigger a segfault without this check. */ - if (self->write == NULL) { - PyErr_Format(PicklingError, - "Pickler.__init__() was not called by %s.__init__()", - Py_TYPE(self)->tp_name); - return NULL; - } - - if (_Pickler_ClearBuffer(self) < 0) { - return NULL; - } - - ... - -Remember the macro with the :c:type:`PyMethodDef` structure for this function? -Find the existing :c:type:`!PyMethodDef` structure for this -function and replace it with a reference to the macro. If the builtin -is at module scope, this will probably be very near the end of the file; -if the builtin is a class method, this will probably be below but relatively -near to the implementation. - -Note that the body of the macro contains a trailing comma; when you -replace the existing static :c:type:`!PyMethodDef` structure with the macro, -*don't* add a comma to the end. - -Sample:: - - static struct PyMethodDef Pickler_methods[] = { - __PICKLE_PICKLER_DUMP_METHODDEF - __PICKLE_PICKLER_CLEAR_MEMO_METHODDEF - {NULL, NULL} /* sentinel */ - }; - -Finally, compile, then run the relevant portions of the regression-test suite. -This change should not introduce any new compile-time warnings or errors, -and there should be no externally visible change to Python's behavior, -except for one difference: :py:func:`inspect.signature` run on your function -should now provide a valid signature! - -Congratulations, you've ported your first function to work with Argument Clinic! - - -.. _clinic-howtos: - -How-to guides -============= - - -How to rename C functions and variables generated by Argument Clinic --------------------------------------------------------------------- - -Argument Clinic automatically names the functions it generates for you. -Occasionally this may cause a problem, if the generated name collides with -the name of an existing C function. There's an easy solution: override the names -used for the C functions. Just add the keyword ``"as"`` -to your function declaration line, followed by the function name you wish to use. -Argument Clinic will use that function name for the base (generated) function, -then add ``"_impl"`` to the end and use that for the name of the impl function. - -For example, if we wanted to rename the C function names generated for -:py:meth:`pickle.Pickler.dump`, it'd look like this:: - - /*[clinic input] - pickle.Pickler.dump as pickler_dumper - - ... - -The base function would now be named :c:func:`!pickler_dumper`, -and the impl function would now be named :c:func:`!pickler_dumper_impl`. - - -Similarly, you may have a problem where you want to give a parameter -a specific Python name, but that name may be inconvenient in C. Argument -Clinic allows you to give a parameter different names in Python and in C, -using the same ``"as"`` syntax:: - - /*[clinic input] - pickle.Pickler.dump - - obj: object - file as file_obj: object - protocol: object = NULL - * - fix_imports: bool = True - -Here, the name used in Python (in the signature and the ``keywords`` -array) would be *file*, but the C variable would be named ``file_obj``. - -You can use this to rename the *self* parameter too! - - -How to convert functions using ``PyArg_UnpackTuple`` ----------------------------------------------------- - -To convert a function parsing its arguments with :c:func:`PyArg_UnpackTuple`, -simply write out all the arguments, specifying each as an ``object``. You -may specify the *type* argument to cast the type as appropriate. All -arguments should be marked positional-only (add a ``/`` on a line by itself -after the last argument). - -Currently the generated code will use :c:func:`PyArg_ParseTuple`, but this -will change soon. - - -How to use optional groups --------------------------- - -Some legacy functions have a tricky approach to parsing their arguments: -they count the number of positional arguments, then use a ``switch`` statement -to call one of several different :c:func:`PyArg_ParseTuple` calls depending on -how many positional arguments there are. (These functions cannot accept -keyword-only arguments.) This approach was used to simulate optional -arguments back before :c:func:`PyArg_ParseTupleAndKeywords` was created. - -While functions using this approach can often be converted to -use :c:func:`!PyArg_ParseTupleAndKeywords`, optional arguments, and default values, -it's not always possible. Some of these legacy functions have -behaviors :c:func:`!PyArg_ParseTupleAndKeywords` doesn't directly support. -The most obvious example is the builtin function :py:func:`range`, which has -an optional argument on the *left* side of its required argument! -Another example is :py:meth:`curses.window.addch`, which has a group of two -arguments that must always be specified together. (The arguments are -called *x* and *y*; if you call the function passing in *x*, -you must also pass in *y* — and if you don't pass in *x* you may not -pass in *y* either.) - -In any case, the goal of Argument Clinic is to support argument parsing -for all existing CPython builtins without changing their semantics. -Therefore Argument Clinic supports -this alternate approach to parsing, using what are called *optional groups*. -Optional groups are groups of arguments that must all be passed in together. -They can be to the left or the right of the required arguments. They -can *only* be used with positional-only parameters. - -.. note:: Optional groups are *only* intended for use when converting - functions that make multiple calls to :c:func:`PyArg_ParseTuple`! - Functions that use *any* other approach for parsing arguments - should *almost never* be converted to Argument Clinic using - optional groups. Functions using optional groups currently - cannot have accurate signatures in Python, because Python just - doesn't understand the concept. Please avoid using optional - groups wherever possible. - -To specify an optional group, add a ``[`` on a line by itself before -the parameters you wish to group together, and a ``]`` on a line by itself -after these parameters. As an example, here's how :py:meth:`curses.window.addch` -uses optional groups to make the first two parameters and the last -parameter optional:: - - /*[clinic input] - - curses.window.addch - - [ - x: int - X-coordinate. - y: int - Y-coordinate. - ] - - ch: object - Character to add. - - [ - attr: long - Attributes for the character. - ] - / - - ... - - -Notes: - -* For every optional group, one additional parameter will be passed into the - impl function representing the group. The parameter will be an int named - ``group_{direction}_{number}``, - where ``{direction}`` is either ``right`` or ``left`` depending on whether the group - is before or after the required parameters, and ``{number}`` is a monotonically - increasing number (starting at 1) indicating how far away the group is from - the required parameters. When the impl is called, this parameter will be set - to zero if this group was unused, and set to non-zero if this group was used. - (By used or unused, I mean whether or not the parameters received arguments - in this invocation.) - -* If there are no required arguments, the optional groups will behave - as if they're to the right of the required arguments. - -* In the case of ambiguity, the argument parsing code - favors parameters on the left (before the required parameters). - -* Optional groups can only contain positional-only parameters. - -* Optional groups are *only* intended for legacy code. Please do not - use optional groups for new code. - - -How to use real Argument Clinic converters, instead of "legacy converters" --------------------------------------------------------------------------- - -To save time, and to minimize how much you need to learn -to achieve your first port to Argument Clinic, the walkthrough above tells -you to use "legacy converters". "Legacy converters" are a convenience, -designed explicitly to make porting existing code to Argument Clinic -easier. And to be clear, their use is acceptable when porting code for -Python 3.4. - -However, in the long term we probably want all our blocks to -use Argument Clinic's real syntax for converters. Why? A couple -reasons: - -* The proper converters are far easier to read and clearer in their intent. -* There are some format units that are unsupported as "legacy converters", - because they require arguments, and the legacy converter syntax doesn't - support specifying arguments. -* In the future we may have a new argument parsing library that isn't - restricted to what :c:func:`PyArg_ParseTuple` supports; this flexibility - won't be available to parameters using legacy converters. - -Therefore, if you don't mind a little extra effort, please use the normal -converters instead of legacy converters. - -In a nutshell, the syntax for Argument Clinic (non-legacy) converters -looks like a Python function call. However, if there are no explicit -arguments to the function (all functions take their default values), -you may omit the parentheses. Thus ``bool`` and ``bool()`` are exactly -the same converters. - -All arguments to Argument Clinic converters are keyword-only. -All Argument Clinic converters accept the following arguments: - - *c_default* - The default value for this parameter when defined in C. - Specifically, this will be the initializer for the variable declared - in the "parse function". See :ref:`the section on default values ` - for how to use this. - Specified as a string. - - *annotation* - The annotation value for this parameter. Not currently supported, - because :pep:`8` mandates that the Python library may not use - annotations. - -In addition, some converters accept additional arguments. Here is a list -of these arguments, along with their meanings: - - *accept* - A set of Python types (and possibly pseudo-types); - this restricts the allowable Python argument to values of these types. - (This is not a general-purpose facility; as a rule it only supports - specific lists of types as shown in the legacy converter table.) - - To accept ``None``, add ``NoneType`` to this set. - - *bitwise* - Only supported for unsigned integers. The native integer value of this - Python argument will be written to the parameter without any range checking, - even for negative values. - - *converter* - Only supported by the ``object`` converter. Specifies the name of a - :ref:`C "converter function" ` - to use to convert this object to a native type. - - *encoding* - Only supported for strings. Specifies the encoding to use when converting - this string from a Python str (Unicode) value into a C ``char *`` value. - - - *subclass_of* - Only supported for the ``object`` converter. Requires that the Python - value be a subclass of a Python type, as expressed in C. - - *type* - Only supported for the ``object`` and ``self`` converters. Specifies - the C type that will be used to declare the variable. Default value is - ``"PyObject *"``. - - *zeroes* - Only supported for strings. If true, embedded NUL bytes (``'\\0'``) are - permitted inside the value. The length of the string will be passed in - to the impl function, just after the string parameter, as a parameter named - ``_length``. - -Please note, not every possible combination of arguments will work. -Usually these arguments are implemented by specific :c:func:`PyArg_ParseTuple` -*format units*, with specific behavior. For example, currently you cannot -call ``unsigned_short`` without also specifying ``bitwise=True``. -Although it's perfectly reasonable to think this would work, these semantics don't -map to any existing format unit. So Argument Clinic doesn't support it. (Or, at -least, not yet.) - -Below is a table showing the mapping of legacy converters into real -Argument Clinic converters. On the left is the legacy converter, -on the right is the text you'd replace it with. - -========= ================================================================================= -``'B'`` ``unsigned_char(bitwise=True)`` -``'b'`` ``unsigned_char`` -``'c'`` ``char`` -``'C'`` ``int(accept={str})`` -``'d'`` ``double`` -``'D'`` ``Py_complex`` -``'es'`` ``str(encoding='name_of_encoding')`` -``'es#'`` ``str(encoding='name_of_encoding', zeroes=True)`` -``'et'`` ``str(encoding='name_of_encoding', accept={bytes, bytearray, str})`` -``'et#'`` ``str(encoding='name_of_encoding', accept={bytes, bytearray, str}, zeroes=True)`` -``'f'`` ``float`` -``'h'`` ``short`` -``'H'`` ``unsigned_short(bitwise=True)`` -``'i'`` ``int`` -``'I'`` ``unsigned_int(bitwise=True)`` -``'k'`` ``unsigned_long(bitwise=True)`` -``'K'`` ``unsigned_long_long(bitwise=True)`` -``'l'`` ``long`` -``'L'`` ``long long`` -``'n'`` ``Py_ssize_t`` -``'O'`` ``object`` -``'O!'`` ``object(subclass_of='&PySomething_Type')`` -``'O&'`` ``object(converter='name_of_c_function')`` -``'p'`` ``bool`` -``'S'`` ``PyBytesObject`` -``'s'`` ``str`` -``'s#'`` ``str(zeroes=True)`` -``'s*'`` ``Py_buffer(accept={buffer, str})`` -``'U'`` ``unicode`` -``'u'`` ``Py_UNICODE`` -``'u#'`` ``Py_UNICODE(zeroes=True)`` -``'w*'`` ``Py_buffer(accept={rwbuffer})`` -``'Y'`` ``PyByteArrayObject`` -``'y'`` ``str(accept={bytes})`` -``'y#'`` ``str(accept={robuffer}, zeroes=True)`` -``'y*'`` ``Py_buffer`` -``'Z'`` ``Py_UNICODE(accept={str, NoneType})`` -``'Z#'`` ``Py_UNICODE(accept={str, NoneType}, zeroes=True)`` -``'z'`` ``str(accept={str, NoneType})`` -``'z#'`` ``str(accept={str, NoneType}, zeroes=True)`` -``'z*'`` ``Py_buffer(accept={buffer, str, NoneType})`` -========= ================================================================================= - -As an example, here's our sample ``pickle.Pickler.dump`` using the proper -converter:: - - /*[clinic input] - pickle.Pickler.dump - - obj: object - The object to be pickled. - / - - Write a pickled representation of obj to the open file. - [clinic start generated code]*/ - -One advantage of real converters is that they're more flexible than legacy -converters. For example, the ``unsigned_int`` converter (and all the -``unsigned_`` converters) can be specified without ``bitwise=True``. Their -default behavior performs range checking on the value, and they won't accept -negative numbers. You just can't do that with a legacy converter! - -Argument Clinic will show you all the converters it has -available. For each converter it'll show you all the parameters -it accepts, along with the default value for each parameter. -Just run ``Tools/clinic/clinic.py --converters`` to see the full list. - - -How to use the ``Py_buffer`` converter --------------------------------------- - -When using the ``Py_buffer`` converter -(or the ``'s*'``, ``'w*'``, ``'*y'``, or ``'z*'`` legacy converters), -you *must* not call :c:func:`PyBuffer_Release` on the provided buffer. -Argument Clinic generates code that does it for you (in the parsing function). - - -.. _clinic-howto-advanced-converters: - -How to use advanced converters ------------------------------- - -Remember those format units you skipped for your first -time because they were advanced? Here's how to handle those too. - -The trick is, all those format units take arguments—either -conversion functions, or types, or strings specifying an encoding. -(But "legacy converters" don't support arguments. That's why we -skipped them for your first function.) The argument you specified -to the format unit is now an argument to the converter; this -argument is either *converter* (for ``O&``), *subclass_of* (for ``O!``), -or *encoding* (for all the format units that start with ``e``). - -When using *subclass_of*, you may also want to use the other -custom argument for ``object()``: *type*, which lets you set the type -actually used for the parameter. For example, if you want to ensure -that the object is a subclass of :c:var:`PyUnicode_Type`, you probably want -to use the converter ``object(type='PyUnicodeObject *', subclass_of='&PyUnicode_Type')``. - -One possible problem with using Argument Clinic: it takes away some possible -flexibility for the format units starting with ``e``. When writing a -:c:func:`!PyArg_Parse*` call by hand, you could theoretically decide at runtime what -encoding string to pass to that call. But now this string must -be hard-coded at Argument-Clinic-preprocessing-time. This limitation is deliberate; -it made supporting this format unit much easier, and may allow for future optimizations. -This restriction doesn't seem unreasonable; CPython itself always passes in static -hard-coded encoding strings for parameters whose format units start with ``e``. - - -.. _clinic-howto-default-values: -.. _default_values: - -How to assign default values to parameter ------------------------------------------ - -Default values for parameters can be any of a number of values. -At their simplest, they can be string, int, or float literals: - -.. code-block:: none - - foo: str = "abc" - bar: int = 123 - bat: float = 45.6 - -They can also use any of Python's built-in constants: - -.. code-block:: none - - yep: bool = True - nope: bool = False - nada: object = None - -There's also special support for a default value of ``NULL``, and -for simple expressions, documented in the following sections. - - -The ``NULL`` default value -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -For string and object parameters, you can set them to ``None`` to indicate -that there's no default. However, that means the C variable will be -initialized to ``Py_None``. For convenience's sakes, there's a special -value called ``NULL`` for just this reason: from Python's perspective it -behaves like a default value of ``None``, but the C variable is initialized -with ``NULL``. - - -Symbolic default values -^^^^^^^^^^^^^^^^^^^^^^^ - -The default value you provide for a parameter can't be any arbitrary -expression. Currently the following are explicitly supported: - -* Numeric constants (integer and float) -* String constants -* ``True``, ``False``, and ``None`` -* Simple symbolic constants like :py:data:`sys.maxsize`, which must - start with the name of the module - -(In the future, this may need to get even more elaborate, -to allow full expressions like ``CONSTANT - 1``.) - - -Expressions as default values -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The default value for a parameter can be more than just a literal value. -It can be an entire expression, using math operators and looking up attributes -on objects. However, this support isn't exactly simple, because of some -non-obvious semantics. - -Consider the following example: - -.. code-block:: none - - foo: Py_ssize_t = sys.maxsize - 1 - -:py:data:`sys.maxsize` can have different values on different platforms. Therefore -Argument Clinic can't simply evaluate that expression locally and hard-code it -in C. So it stores the default in such a way that it will get evaluated at -runtime, when the user asks for the function's signature. - -What namespace is available when the expression is evaluated? It's evaluated -in the context of the module the builtin came from. So, if your module has an -attribute called :py:attr:`!max_widgets`, you may simply use it: - -.. code-block:: none - - foo: Py_ssize_t = max_widgets - -If the symbol isn't found in the current module, it fails over to looking in -:py:data:`sys.modules`. That's how it can find :py:data:`sys.maxsize` for example. -(Since you don't know in advance what modules the user will load into their interpreter, -it's best to restrict yourself to modules that are preloaded by Python itself.) - -Evaluating default values only at runtime means Argument Clinic can't compute -the correct equivalent C default value. So you need to tell it explicitly. -When you use an expression, you must also specify the equivalent expression -in C, using the *c_default* parameter to the converter: - -.. code-block:: none - - foo: Py_ssize_t(c_default="PY_SSIZE_T_MAX - 1") = sys.maxsize - 1 - -Another complication: Argument Clinic can't know in advance whether or not the -expression you supply is valid. It parses it to make sure it looks legal, but -it can't *actually* know. You must be very careful when using expressions to -specify values that are guaranteed to be valid at runtime! - -Finally, because expressions must be representable as static C values, there -are many restrictions on legal expressions. Here's a list of Python features -you're not permitted to use: - -* Function calls. -* Inline if statements (``3 if foo else 5``). -* Automatic sequence unpacking (``*[1, 2, 3]``). -* List/set/dict comprehensions and generator expressions. -* Tuple/list/set/dict literals. - - -.. _clinic-howto-return-converters: - -How to use return converters ----------------------------- - -By default, the impl function Argument Clinic generates for you returns -:c:type:`PyObject * `. -But your C function often computes some C type, -then converts it into the :c:type:`!PyObject *` -at the last moment. Argument Clinic handles converting your inputs from Python types -into native C types—why not have it convert your return value from a native C type -into a Python type too? - -That's what a "return converter" does. It changes your impl function to return -some C type, then adds code to the generated (non-impl) function to handle converting -that value into the appropriate :c:type:`!PyObject *`. - -The syntax for return converters is similar to that of parameter converters. -You specify the return converter like it was a return annotation on the -function itself, using ``->`` notation. - -For example: - -.. code-block:: c - - /*[clinic input] - add -> int - - a: int - b: int - / - - [clinic start generated code]*/ - -Return converters behave much the same as parameter converters; -they take arguments, the arguments are all keyword-only, and if you're not changing -any of the default arguments you can omit the parentheses. - -(If you use both ``"as"`` *and* a return converter for your function, -the ``"as"`` should come before the return converter.) - -There's one additional complication when using return converters: how do you -indicate an error has occurred? Normally, a function returns a valid (non-``NULL``) -pointer for success, and ``NULL`` for failure. But if you use an integer return converter, -all integers are valid. How can Argument Clinic detect an error? Its solution: each return -converter implicitly looks for a special value that indicates an error. If you return -that value, and an error has been set (c:func:`PyErr_Occurred` returns a true -value), then the generated code will propagate the error. Otherwise it will -encode the value you return like normal. - -Currently Argument Clinic supports only a few return converters: - -.. code-block:: none - - bool - double - float - int - long - Py_ssize_t - size_t - unsigned int - unsigned long - -None of these take parameters. -For all of these, return ``-1`` to indicate error. - -(There's also an experimental ``NoneType`` converter, which lets you -return ``Py_None`` on success or ``NULL`` on failure, without having -to increment the reference count on ``Py_None``. I'm not sure it adds -enough clarity to be worth using.) - -To see all the return converters Argument Clinic supports, along with -their parameters (if any), -just run ``Tools/clinic/clinic.py --converters`` for the full list. - - -How to clone existing functions -------------------------------- - -If you have a number of functions that look similar, you may be able to -use Clinic's "clone" feature. When you clone an existing function, -you reuse: - -* its parameters, including - - * their names, - - * their converters, with all parameters, - - * their default values, - - * their per-parameter docstrings, - - * their *kind* (whether they're positional only, - positional or keyword, or keyword only), and - -* its return converter. - -The only thing not copied from the original function is its docstring; -the syntax allows you to specify a new docstring. - -Here's the syntax for cloning a function:: - - /*[clinic input] - module.class.new_function [as c_basename] = module.class.existing_function - - Docstring for new_function goes here. - [clinic start generated code]*/ - -(The functions can be in different modules or classes. I wrote -``module.class`` in the sample just to illustrate that you must -use the full path to *both* functions.) - -Sorry, there's no syntax for partially cloning a function, or cloning a function -then modifying it. Cloning is an all-or nothing proposition. - -Also, the function you are cloning from must have been previously defined -in the current file. - - -How to call Python code ------------------------ - -The rest of the advanced topics require you to write Python code -which lives inside your C file and modifies Argument Clinic's -runtime state. This is simple: you simply define a Python block. - -A Python block uses different delimiter lines than an Argument -Clinic function block. It looks like this:: - - /*[python input] - # python code goes here - [python start generated code]*/ - -All the code inside the Python block is executed at the -time it's parsed. All text written to stdout inside the block -is redirected into the "output" after the block. - -As an example, here's a Python block that adds a static integer -variable to the C code:: - - /*[python input] - print('static int __ignored_unused_variable__ = 0;') - [python start generated code]*/ - static int __ignored_unused_variable__ = 0; - /*[python checksum:...]*/ - - -.. _clinic-howto-self-converter: - -How to use the "self converter" -------------------------------- - -Argument Clinic automatically adds a "self" parameter for you -using a default converter. It automatically sets the ``type`` -of this parameter to the "pointer to an instance" you specified -when you declared the type. However, you can override -Argument Clinic's converter and specify one yourself. -Just add your own *self* parameter as the first parameter in a -block, and ensure that its converter is an instance of -:class:`!self_converter` or a subclass thereof. - -What's the point? This lets you override the type of ``self``, -or give it a different default name. - -How do you specify the custom type you want to cast ``self`` to? -If you only have one or two functions with the same type for ``self``, -you can directly use Argument Clinic's existing ``self`` converter, -passing in the type you want to use as the *type* parameter:: - - /*[clinic input] - - _pickle.Pickler.dump - - self: self(type="PicklerObject *") - obj: object - / - - Write a pickled representation of the given object to the open file. - [clinic start generated code]*/ - -On the other hand, if you have a lot of functions that will use the same -type for ``self``, it's best to create your own converter, subclassing -:class:`!self_converter` but overwriting the :py:attr:`!type` member:: - - /*[python input] - class PicklerObject_converter(self_converter): - type = "PicklerObject *" - [python start generated code]*/ - - /*[clinic input] - - _pickle.Pickler.dump - - self: PicklerObject - obj: object - / - - Write a pickled representation of the given object to the open file. - [clinic start generated code]*/ - - -How to use the "defining class" converter ------------------------------------------ - -Argument Clinic facilitates gaining access to the defining class of a method. -This is useful for :ref:`heap type ` methods that need to fetch -module level state. Use :c:func:`PyType_FromModuleAndSpec` to associate a new -heap type with a module. You can now use :c:func:`PyType_GetModuleState` on -the defining class to fetch the module state, for example from a module method. - -Example from :source:`Modules/zlibmodule.c`. -First, ``defining_class`` is added to the clinic input:: - - /*[clinic input] - zlib.Compress.compress - - cls: defining_class - data: Py_buffer - Binary data to be compressed. - / - - -After running the Argument Clinic tool, the following function signature is -generated:: - - /*[clinic start generated code]*/ - static PyObject * - zlib_Compress_compress_impl(compobject *self, PyTypeObject *cls, - Py_buffer *data) - /*[clinic end generated code: output=6731b3f0ff357ca6 input=04d00f65ab01d260]*/ - - -The following code can now use ``PyType_GetModuleState(cls)`` to fetch the -module state:: - - zlibstate *state = PyType_GetModuleState(cls); - - -Each method may only have one argument using this converter, and it must appear -after ``self``, or, if ``self`` is not used, as the first argument. The argument -will be of type ``PyTypeObject *``. The argument will not appear in the -:py:attr:`!__text_signature__`. - -The ``defining_class`` converter is not compatible with :py:meth:`!__init__` -and :py:meth:`!__new__` methods, which cannot use the :c:macro:`METH_METHOD` -convention. - -It is not possible to use ``defining_class`` with slot methods. In order to -fetch the module state from such methods, use :c:func:`PyType_GetModuleByDef` -to look up the module and then :c:func:`PyModule_GetState` to fetch the module -state. Example from the ``setattro`` slot method in -:source:`Modules/_threadmodule.c`:: - - static int - local_setattro(localobject *self, PyObject *name, PyObject *v) - { - PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &thread_module); - thread_module_state *state = get_thread_state(module); - ... - } - - -See also :pep:`573`. - - -.. _clinic-howto-custom-converter: - -How to write a custom converter -------------------------------- - -A converter is a Python class that inherits from :py:class:`CConverter`. -The main purpose of a custom converter, is for parameters parsed with -the ``O&`` format unit --- parsing such a parameter means calling -a :c:func:`PyArg_ParseTuple` "converter function". - -Your converter class should be named :samp:`{ConverterName}_converter`. -By following this convention, your converter class will be automatically -registered with Argument Clinic, with its *converter name* being the name of -your converter class with the ``_converter`` suffix stripped off. - -Instead of subclassing :py:meth:`!CConverter.__init__`, -write a :py:meth:`!converter_init` method. -:py:meth:`!converter_init` always accepts a *self* parameter. -After *self*, all additional parameters **must** be keyword-only. -Any arguments passed to the converter in Argument Clinic -will be passed along to your :py:meth:`!converter_init` method. -See :py:class:`CConverter` for a list of members you may wish to specify in -your subclass. - -Here's the simplest example of a custom converter, from :source:`Modules/zlibmodule.c`:: - - /*[python input] - - class ssize_t_converter(CConverter): - type = 'Py_ssize_t' - converter = 'ssize_t_converter' - - [python start generated code]*/ - /*[python end generated code: output=da39a3ee5e6b4b0d input=35521e4e733823c7]*/ - -This block adds a converter named ``ssize_t`` to Argument Clinic. -Parameters declared as ``ssize_t`` will be declared with type :c:type:`Py_ssize_t`, -and will be parsed by the ``'O&'`` format unit, -which will call the :c:func:`!ssize_t_converter` converter C function. -``ssize_t`` variables automatically support default values. - -More sophisticated custom converters can insert custom C code to -handle initialization and cleanup. -You can see more examples of custom converters in the CPython -source tree; grep the C files for the string ``CConverter``. - - -How to write a custom return converter --------------------------------------- - -Writing a custom return converter is much like writing -a custom converter. Except it's somewhat simpler, because return -converters are themselves much simpler. - -Return converters must subclass :py:class:`!CReturnConverter`. -There are no examples yet of custom return converters, -because they are not widely used yet. If you wish to -write your own return converter, please read :source:`Tools/clinic/clinic.py`, -specifically the implementation of :py:class:`!CReturnConverter` and -all its subclasses. - - -How to convert ``METH_O`` and ``METH_NOARGS`` functions -------------------------------------------------------- - -To convert a function using :c:macro:`METH_O`, make sure the function's -single argument is using the ``object`` converter, and mark the -arguments as positional-only:: - - /*[clinic input] - meth_o_sample - - argument: object - / - [clinic start generated code]*/ - - -To convert a function using :c:macro:`METH_NOARGS`, just don't specify -any arguments. - -You can still use a self converter, a return converter, and specify -a *type* argument to the object converter for :c:macro:`METH_O`. - - -How to convert ``tp_new`` and ``tp_init`` functions ---------------------------------------------------- - -You can convert :c:member:`~PyTypeObject.tp_new` and -:c:member:`~PyTypeObject.tp_init` functions. -Just name them ``__new__`` or ``__init__`` as appropriate. Notes: - -* The function name generated for ``__new__`` doesn't end in ``__new__`` - like it would by default. It's just the name of the class, converted - into a valid C identifier. - -* No :c:type:`PyMethodDef` ``#define`` is generated for these functions. - -* ``__init__`` functions return ``int``, not ``PyObject *``. - -* Use the docstring as the class docstring. - -* Although ``__new__`` and ``__init__`` functions must always - accept both the ``args`` and ``kwargs`` objects, when converting - you may specify any signature for these functions that you like. - (If your function doesn't support keywords, the parsing function - generated will throw an exception if it receives any.) - - -How to change and redirect Clinic's output ------------------------------------------- - -It can be inconvenient to have Clinic's output interspersed with -your conventional hand-edited C code. Luckily, Clinic is configurable: -you can buffer up its output for printing later (or earlier!), or write -its output to a separate file. You can also add a prefix or suffix to -every line of Clinic's generated output. - -While changing Clinic's output in this manner can be a boon to readability, -it may result in Clinic code using types before they are defined, or -your code attempting to use Clinic-generated code before it is defined. -These problems can be easily solved by rearranging the declarations in your file, -or moving where Clinic's generated code goes. (This is why the default behavior -of Clinic is to output everything into the current block; while many people -consider this hampers readability, it will never require rearranging your -code to fix definition-before-use problems.) - -Let's start with defining some terminology: - -*field* - A field, in this context, is a subsection of Clinic's output. - For example, the ``#define`` for the :c:type:`PyMethodDef` structure - is a field, called ``methoddef_define``. Clinic has seven - different fields it can output per function definition: - - .. code-block:: none - - docstring_prototype - docstring_definition - methoddef_define - impl_prototype - parser_prototype - parser_definition - impl_definition - - All the names are of the form ``"_"``, - where ``""`` is the semantic object represented (the parsing function, - the impl function, the docstring, or the methoddef structure) and ``""`` - represents what kind of statement the field is. Field names that end in - ``"_prototype"`` - represent forward declarations of that thing, without the actual body/data - of the thing; field names that end in ``"_definition"`` represent the actual - definition of the thing, with the body/data of the thing. (``"methoddef"`` - is special, it's the only one that ends with ``"_define"``, representing that - it's a preprocessor #define.) - -*destination* - A destination is a place Clinic can write output to. There are - five built-in destinations: - - ``block`` - The default destination: printed in the output section of - the current Clinic block. - - ``buffer`` - A text buffer where you can save text for later. Text sent - here is appended to the end of any existing text. It's an - error to have any text left in the buffer when Clinic finishes - processing a file. - - ``file`` - A separate "clinic file" that will be created automatically by Clinic. - The filename chosen for the file is ``{basename}.clinic{extension}``, - where ``basename`` and ``extension`` were assigned the output - from ``os.path.splitext()`` run on the current file. (Example: - the ``file`` destination for :file:`_pickle.c` would be written to - :file:`_pickle.clinic.c`.) - - **Important: When using a** ``file`` **destination, you** - *must check in* **the generated file!** - - ``two-pass`` - A buffer like ``buffer``. However, a two-pass buffer can only - be dumped once, and it prints out all text sent to it during - all processing, even from Clinic blocks *after* the dumping point. - - ``suppress`` - The text is suppressed—thrown away. - - -Clinic defines five new directives that let you reconfigure its output. - -The first new directive is ``dump``: - -.. code-block:: none - - dump - -This dumps the current contents of the named destination into the output of -the current block, and empties it. This only works with ``buffer`` and -``two-pass`` destinations. - -The second new directive is ``output``. The most basic form of ``output`` -is like this: - -.. code-block:: none - - output - -This tells Clinic to output *field* to *destination*. ``output`` also -supports a special meta-destination, called ``everything``, which tells -Clinic to output *all* fields to that *destination*. - -``output`` has a number of other functions: - -.. code-block:: none - - output push - output pop - output preset - - -``output push`` and ``output pop`` allow you to push and pop -configurations on an internal configuration stack, so that you -can temporarily modify the output configuration, then easily restore -the previous configuration. Simply push before your change to save -the current configuration, then pop when you wish to restore the -previous configuration. - -``output preset`` sets Clinic's output to one of several built-in -preset configurations, as follows: - - ``block`` - Clinic's original starting configuration. Writes everything - immediately after the input block. - - Suppress the ``parser_prototype`` - and ``docstring_prototype``, write everything else to ``block``. - - ``file`` - Designed to write everything to the "clinic file" that it can. - You then ``#include`` this file near the top of your file. - You may need to rearrange your file to make this work, though - usually this just means creating forward declarations for various - ``typedef`` and ``PyTypeObject`` definitions. - - Suppress the ``parser_prototype`` - and ``docstring_prototype``, write the ``impl_definition`` to - ``block``, and write everything else to ``file``. - - The default filename is ``"{dirname}/clinic/{basename}.h"``. - - ``buffer`` - Save up most of the output from Clinic, to be written into - your file near the end. For Python files implementing modules - or builtin types, it's recommended that you dump the buffer - just above the static structures for your module or - builtin type; these are normally very near the end. Using - ``buffer`` may require even more editing than ``file``, if - your file has static ``PyMethodDef`` arrays defined in the - middle of the file. - - Suppress the ``parser_prototype``, ``impl_prototype``, - and ``docstring_prototype``, write the ``impl_definition`` to - ``block``, and write everything else to ``file``. - - ``two-pass`` - Similar to the ``buffer`` preset, but writes forward declarations to - the ``two-pass`` buffer, and definitions to the ``buffer``. - This is similar to the ``buffer`` preset, but may require - less editing than ``buffer``. Dump the ``two-pass`` buffer - near the top of your file, and dump the ``buffer`` near - the end just like you would when using the ``buffer`` preset. - - Suppresses the ``impl_prototype``, write the ``impl_definition`` - to ``block``, write ``docstring_prototype``, ``methoddef_define``, - and ``parser_prototype`` to ``two-pass``, write everything else - to ``buffer``. - - ``partial-buffer`` - Similar to the ``buffer`` preset, but writes more things to ``block``, - only writing the really big chunks of generated code to ``buffer``. - This avoids the definition-before-use problem of ``buffer`` completely, - at the small cost of having slightly more stuff in the block's output. - Dump the ``buffer`` near the end, just like you would when using - the ``buffer`` preset. - - Suppresses the ``impl_prototype``, write the ``docstring_definition`` - and ``parser_definition`` to ``buffer``, write everything else to ``block``. - -The third new directive is ``destination``: - -.. code-block:: none - - destination [...] - -This performs an operation on the destination named ``name``. - -There are two defined subcommands: ``new`` and ``clear``. - -The ``new`` subcommand works like this: - -.. code-block:: none - - destination new - -This creates a new destination with name ```` and type ````. - -There are five destination types: - - ``suppress`` - Throws the text away. - - ``block`` - Writes the text to the current block. This is what Clinic - originally did. - - ``buffer`` - A simple text buffer, like the "buffer" builtin destination above. - - ``file`` - A text file. The file destination takes an extra argument, - a template to use for building the filename, like so: - - destination new - - The template can use three strings internally that will be replaced - by bits of the filename: - - {path} - The full path to the file, including directory and full filename. - {dirname} - The name of the directory the file is in. - {basename} - Just the name of the file, not including the directory. - {basename_root} - Basename with the extension clipped off - (everything up to but not including the last '.'). - {basename_extension} - The last '.' and everything after it. If the basename - does not contain a period, this will be the empty string. - - If there are no periods in the filename, {basename} and {filename} - are the same, and {extension} is empty. "{basename}{extension}" - is always exactly the same as "{filename}"." - - ``two-pass`` - A two-pass buffer, like the "two-pass" builtin destination above. - - -The ``clear`` subcommand works like this: - -.. code-block:: none - - destination clear - -It removes all the accumulated text up to this point in the destination. -(I don't know what you'd need this for, but I thought maybe it'd be -useful while someone's experimenting.) - -The fourth new directive is ``set``: - -.. code-block:: none - - set line_prefix "string" - set line_suffix "string" - -``set`` lets you set two internal variables in Clinic. -``line_prefix`` is a string that will be prepended to every line of Clinic's output; -``line_suffix`` is a string that will be appended to every line of Clinic's output. - -Both of these support two format strings: - - ``{block comment start}`` - Turns into the string ``/*``, the start-comment text sequence for C files. - - ``{block comment end}`` - Turns into the string ``*/``, the end-comment text sequence for C files. - -The final new directive is one you shouldn't need to use directly, -called ``preserve``: - -.. code-block:: none - - preserve - -This tells Clinic that the current contents of the output should be kept, unmodified. -This is used internally by Clinic when dumping output into ``file`` files; wrapping -it in a Clinic block lets Clinic use its existing checksum functionality to ensure -the file was not modified by hand before it gets overwritten. - - -How to use the ``#ifdef`` trick -------------------------------- - -If you're converting a function that isn't available on all platforms, -there's a trick you can use to make life a little easier. The existing -code probably looks like this:: - - #ifdef HAVE_FUNCTIONNAME - static module_functionname(...) - { - ... - } - #endif /* HAVE_FUNCTIONNAME */ - -And then in the ``PyMethodDef`` structure at the bottom the existing code -will have: - -.. code-block:: none - - #ifdef HAVE_FUNCTIONNAME - {'functionname', ... }, - #endif /* HAVE_FUNCTIONNAME */ - -In this scenario, you should enclose the body of your impl function inside the ``#ifdef``, -like so:: - - #ifdef HAVE_FUNCTIONNAME - /*[clinic input] - module.functionname - ... - [clinic start generated code]*/ - static module_functionname(...) - { - ... - } - #endif /* HAVE_FUNCTIONNAME */ - -Then, remove those three lines from the :c:type:`PyMethodDef` structure, -replacing them with the macro Argument Clinic generated: - -.. code-block:: none - - MODULE_FUNCTIONNAME_METHODDEF - -(You can find the real name for this macro inside the generated code. -Or you can calculate it yourself: it's the name of your function as defined -on the first line of your block, but with periods changed to underscores, -uppercased, and ``"_METHODDEF"`` added to the end.) - -Perhaps you're wondering: what if ``HAVE_FUNCTIONNAME`` isn't defined? -The ``MODULE_FUNCTIONNAME_METHODDEF`` macro won't be defined either! - -Here's where Argument Clinic gets very clever. It actually detects that the -Argument Clinic block might be deactivated by the ``#ifdef``. When that -happens, it generates a little extra code that looks like this:: - - #ifndef MODULE_FUNCTIONNAME_METHODDEF - #define MODULE_FUNCTIONNAME_METHODDEF - #endif /* !defined(MODULE_FUNCTIONNAME_METHODDEF) */ - -That means the macro always works. If the function is defined, this turns -into the correct structure, including the trailing comma. If the function is -undefined, this turns into nothing. - -However, this causes one ticklish problem: where should Argument Clinic put this -extra code when using the "block" output preset? It can't go in the output block, -because that could be deactivated by the ``#ifdef``. (That's the whole point!) - -In this situation, Argument Clinic writes the extra code to the "buffer" destination. -This may mean that you get a complaint from Argument Clinic: - -.. code-block:: none - - Warning in file "Modules/posixmodule.c" on line 12357: - Destination buffer 'buffer' not empty at end of file, emptying. - -When this happens, just open your file, find the ``dump buffer`` block that -Argument Clinic added to your file (it'll be at the very bottom), then -move it above the :c:type:`PyMethodDef` structure where that macro is used. - - -How to use Argument Clinic in Python files ------------------------------------------- - -It's actually possible to use Argument Clinic to preprocess Python files. -There's no point to using Argument Clinic blocks, of course, as the output -wouldn't make any sense to the Python interpreter. But using Argument Clinic -to run Python blocks lets you use Python as a Python preprocessor! - -Since Python comments are different from C comments, Argument Clinic -blocks embedded in Python files look slightly different. They look like this: - -.. code-block:: python3 - - #/*[python input] - #print("def foo(): pass") - #[python start generated code]*/ - def foo(): pass - #/*[python checksum:...]*/ - - -.. _clinic-howto-override-signature: - -How to override the generated signature ---------------------------------------- - -You can use the ``@text_signature`` directive to override the default generated -signature in the docstring. -This can be useful for complex signatures that Argument Clinic cannot handle. -The ``@text_signature`` directive takes one argument: -the custom signature as a string. -The provided signature is copied verbatim to the generated docstring. - -Example from :source:`Objects/codeobject.c`:: - - /*[clinic input] - @text_signature "($self, /, **changes)" - code.replace - * - co_argcount: int(c_default="self->co_argcount") = unchanged - co_posonlyargcount: int(c_default="self->co_posonlyargcount") = unchanged - # etc ... - - Return a copy of the code object with new values for the specified fields. - [clinic start generated output]*/ - -The generated docstring ends up looking like this: - -.. code-block:: none - - replace($self, /, **changes) - -- - - Return a copy of the code object with new values for the specified fields. + The Argument Clinic How-TO has been moved to the `Python Developer's Guide + `__. diff --git a/Doc/howto/index.rst b/Doc/howto/index.rst index 8a378e6659efc4..c53c613bc5b1c6 100644 --- a/Doc/howto/index.rst +++ b/Doc/howto/index.rst @@ -28,7 +28,6 @@ Currently, the HOWTOs are: urllib2.rst argparse.rst ipaddress.rst - clinic.rst instrumentation.rst annotations.rst isolating-extensions.rst diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index b73bf14bcbf192..3789164cc1f72e 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -123,7 +123,7 @@ There is a new function parameter syntax ``/`` to indicate that some function parameters must be specified positionally and cannot be used as keyword arguments. This is the same notation shown by ``help()`` for C functions annotated with Larry Hastings' -:ref:`Argument Clinic ` tool. +`Argument Clinic `__ tool. In the following example, parameters *a* and *b* are positional-only, while *c* or *d* can be positional or keyword, and *e* or *f* are diff --git a/Misc/NEWS.d/3.11.5.rst b/Misc/NEWS.d/3.11.5.rst index 502e0ccfd68c46..a26e3937418b95 100644 --- a/Misc/NEWS.d/3.11.5.rst +++ b/Misc/NEWS.d/3.11.5.rst @@ -793,7 +793,7 @@ and 3.1.2. Argument Clinic now supports overriding automatically generated signature by using directive ``@text_signature``. See -:ref:`clinic-howto-override-signature`. +`the Argument Clinic docs `__. .. From c9214b90f469aa85e307a6dc3b6052c961003807 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 12 Oct 2023 11:57:36 +0200 Subject: [PATCH 468/632] [3.11] gh-107450: Raise OverflowError when parser column offset overflows (GH-110754) (#110763) (cherry picked from commit fb7843ee895ac7f6eeb58f356b1a320eea081cfc) Co-authored-by: Lysandros Nikolaou --- Lib/test/test_exceptions.py | 4 ++++ Parser/pegen_errors.c | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 6f34e29e42f7c7..7f6eaa34e11406 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -318,6 +318,10 @@ def baz(): check('(yield i) = 2', 1, 2) check('def f(*):\n pass', 1, 7) + def testMemoryErrorBigSource(self): + with self.assertRaisesRegex(OverflowError, "column offset overflow"): + exec(f"if True:\n {' ' * 2**31}print('hello world')") + @cpython_only def testSettingException(self): # test that setting an exception at the C level works even if the diff --git a/Parser/pegen_errors.c b/Parser/pegen_errors.c index 3d8cccb0a9749c..ea5c4e227ff763 100644 --- a/Parser/pegen_errors.c +++ b/Parser/pegen_errors.c @@ -224,6 +224,12 @@ _PyPegen_raise_error(Parser *p, PyObject *errtype, const char *errmsg, ...) col_offset = 0; } else { const char* start = p->tok->buf ? p->tok->line_start : p->tok->buf; + if (p->tok->cur - start > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "Parser column offset overflow - source line is too big"); + p->error_indicator = 1; + return NULL; + } col_offset = Py_SAFE_DOWNCAST(p->tok->cur - start, intptr_t, int); } } else { From e16922f07010a620298d7fb8abfae5c578d5d337 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Thu, 12 Oct 2023 15:52:03 +0300 Subject: [PATCH 469/632] [3.11] gh-109216: Fix possible memory leak in `BUILD_MAP` (#109323) * [3.11] gh-109216: Fix possible memory leak in `BUILD_MAP` * Add NEWS * Update Python/ceval.c Co-authored-by: Kumar Aditya --------- Co-authored-by: Kumar Aditya --- .../2023-09-11-12-41-42.gh-issue-109216.60QOSb.rst | 2 ++ Python/ceval.c | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-09-11-12-41-42.gh-issue-109216.60QOSb.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-09-11-12-41-42.gh-issue-109216.60QOSb.rst b/Misc/NEWS.d/next/Core and Builtins/2023-09-11-12-41-42.gh-issue-109216.60QOSb.rst new file mode 100644 index 00000000000000..f36310fc5f8064 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-09-11-12-41-42.gh-issue-109216.60QOSb.rst @@ -0,0 +1,2 @@ +Fix possible memory leak in :opcode:`BUILD_MAP`. + diff --git a/Python/ceval.c b/Python/ceval.c index 172bc4743bda90..bb6bb35030d212 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3318,13 +3318,14 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int &PEEK(2*oparg), 2, &PEEK(2*oparg - 1), 2, oparg); - if (map == NULL) - goto error; while (oparg--) { Py_DECREF(POP()); Py_DECREF(POP()); } + if (map == NULL) { + goto error; + } PUSH(map); DISPATCH(); } From 26748ed4f61520c59af15547792d1e73144a4314 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 12 Oct 2023 23:45:36 +0200 Subject: [PATCH 470/632] gh-110756: Sync regrtest with main branch (#110758) (#110781) Copy files from main to this branch: * Lib/test/libregrtest/*.py * Lib/test/__init__.py * Lib/test/__main__.py * Lib/test/autotest.py * Lib/test/pythoninfo.py * Lib/test/regrtest.py * Lib/test/test_regrtest.py Copy also changes from: * Lib/test/support/__init__.py * Lib/test/support/os_helper.py * Lib/test/support/testresult.py * Lib/test/support/threading_helper.py * Lib/test/test_support.py Do not modify scripts running tests such as Makefile.pre.in, .github/workflows/build.yml or Tools/scripts/run_tests.py: do not use --fast-ci and --slow-ci in this change. Changes: * SPLITTESTDIRS: don't include test_inspect. * Add utils.process_cpu_count() using len(os.sched_getaffinity(0)). * test_regrtest doesn't use @support.without_optimizer which doesn't exist in Python 3.11. * Add support.set_sanitizer_env_var(). * Update test_faulthandler to use support.set_sanitizer_env_var(). * @support.without_optimizer doesn't exist in 3.11. * Add support.Py_DEBUG. * regrtest.refleak: 3.11 doesn't have sys.getunicodeinternedsize. --- Lib/test/__main__.py | 4 +- Lib/test/autotest.py | 2 +- Lib/test/libregrtest/__init__.py | 2 - Lib/test/libregrtest/cmdline.py | 125 ++- Lib/test/libregrtest/findtests.py | 105 +++ Lib/test/libregrtest/logger.py | 86 ++ Lib/test/libregrtest/main.py | 1199 +++++++++++--------------- Lib/test/libregrtest/pgo.py | 8 +- Lib/test/libregrtest/refleak.py | 35 +- Lib/test/libregrtest/result.py | 190 ++++ Lib/test/libregrtest/results.py | 261 ++++++ Lib/test/libregrtest/run_workers.py | 607 +++++++++++++ Lib/test/libregrtest/runtest.py | 479 ---------- Lib/test/libregrtest/runtest_mp.py | 564 ------------ Lib/test/libregrtest/runtests.py | 162 ++++ Lib/test/libregrtest/save_env.py | 14 +- Lib/test/libregrtest/setup.py | 139 ++- Lib/test/libregrtest/single.py | 278 ++++++ Lib/test/libregrtest/utils.py | 409 ++++++++- Lib/test/libregrtest/worker.py | 116 +++ Lib/test/pythoninfo.py | 122 ++- Lib/test/regrtest.py | 2 +- Lib/test/support/__init__.py | 73 +- Lib/test/support/os_helper.py | 2 +- Lib/test/support/testresult.py | 3 + Lib/test/support/threading_helper.py | 18 +- Lib/test/test_faulthandler.py | 17 +- Lib/test/test_regrtest.py | 823 +++++++++++++----- Lib/test/test_support.py | 43 +- 29 files changed, 3712 insertions(+), 2176 deletions(-) create mode 100644 Lib/test/libregrtest/findtests.py create mode 100644 Lib/test/libregrtest/logger.py create mode 100644 Lib/test/libregrtest/result.py create mode 100644 Lib/test/libregrtest/results.py create mode 100644 Lib/test/libregrtest/run_workers.py delete mode 100644 Lib/test/libregrtest/runtest.py delete mode 100644 Lib/test/libregrtest/runtest_mp.py create mode 100644 Lib/test/libregrtest/runtests.py create mode 100644 Lib/test/libregrtest/single.py create mode 100644 Lib/test/libregrtest/worker.py diff --git a/Lib/test/__main__.py b/Lib/test/__main__.py index 19a6b2b8904526..82b50ad2c6e777 100644 --- a/Lib/test/__main__.py +++ b/Lib/test/__main__.py @@ -1,2 +1,2 @@ -from test.libregrtest import main -main() +from test.libregrtest.main import main +main(_add_python_opts=True) diff --git a/Lib/test/autotest.py b/Lib/test/autotest.py index fa85cc153a133a..b5a1fab404c72d 100644 --- a/Lib/test/autotest.py +++ b/Lib/test/autotest.py @@ -1,5 +1,5 @@ # This should be equivalent to running regrtest.py from the cmdline. # It can be especially handy if you're in an interactive shell, e.g., # from test import autotest. -from test.libregrtest import main +from test.libregrtest.main import main main() diff --git a/Lib/test/libregrtest/__init__.py b/Lib/test/libregrtest/__init__.py index 5e8dba5dbde71a..e69de29bb2d1d6 100644 --- a/Lib/test/libregrtest/__init__.py +++ b/Lib/test/libregrtest/__init__.py @@ -1,2 +0,0 @@ -from test.libregrtest.cmdline import _parse_args, RESOURCE_NAMES, ALL_RESOURCES -from test.libregrtest.main import main diff --git a/Lib/test/libregrtest/cmdline.py b/Lib/test/libregrtest/cmdline.py index 40ae8fc46e9b01..dd4cd335bef7e3 100644 --- a/Lib/test/libregrtest/cmdline.py +++ b/Lib/test/libregrtest/cmdline.py @@ -1,8 +1,9 @@ import argparse -import os +import os.path import shlex import sys from test.support import os_helper +from .utils import ALL_RESOURCES, RESOURCE_NAMES USAGE = """\ @@ -27,8 +28,10 @@ Additional option details: -r randomizes test execution order. You can use --randseed=int to provide an -int seed value for the randomizer; this is useful for reproducing troublesome -test orders. +int seed value for the randomizer. The randseed value will be used +to set seeds for all random usages in tests +(including randomizing the tests order if -r is set). +By default we always set random seed, but do not randomize test order. -s On the first invocation of regrtest using -s, the first test file found or the first test file given on the command line is run, and the name of @@ -130,25 +133,17 @@ """ -ALL_RESOURCES = ('audio', 'curses', 'largefile', 'network', - 'decimal', 'cpu', 'subprocess', 'urlfetch', 'gui', 'walltime') - -# Other resources excluded from --use=all: -# -# - extralagefile (ex: test_zipfile64): really too slow to be enabled -# "by default" -# - tzdata: while needed to validate fully test_datetime, it makes -# test_datetime too slow (15-20 min on some buildbots) and so is disabled by -# default (see bpo-30822). -RESOURCE_NAMES = ALL_RESOURCES + ('extralargefile', 'tzdata') - - class Namespace(argparse.Namespace): def __init__(self, **kwargs) -> None: + self.ci = False self.testdir = None self.verbose = 0 self.quiet = False self.exclude = False + self.cleanup = False + self.wait = False + self.list_cases = False + self.list_tests = False self.single = False self.randomize = False self.fromfile = None @@ -157,8 +152,8 @@ def __init__(self, **kwargs) -> None: self.trace = False self.coverdir = 'coverage' self.runleaks = False - self.huntrleaks = False - self.verbose2 = False + self.huntrleaks: tuple[int, int, str] | None = None + self.rerun = False self.verbose3 = False self.print_slow = False self.random_seed = None @@ -170,6 +165,14 @@ def __init__(self, **kwargs) -> None: self.ignore_tests = None self.pgo = False self.pgo_extended = False + self.worker_json = None + self.start = None + self.timeout = None + self.memlimit = None + self.threshold = None + self.fail_rerun = False + self.tempdir = None + self._add_python_opts = True super().__init__(**kwargs) @@ -198,25 +201,35 @@ def _create_parser(): # We add help explicitly to control what argument group it renders under. group.add_argument('-h', '--help', action='help', help='show this help message and exit') - group.add_argument('--timeout', metavar='TIMEOUT', type=float, + group.add_argument('--fast-ci', action='store_true', + help='Fast Continuous Integration (CI) mode used by ' + 'GitHub Actions') + group.add_argument('--slow-ci', action='store_true', + help='Slow Continuous Integration (CI) mode used by ' + 'buildbot workers') + group.add_argument('--timeout', metavar='TIMEOUT', help='dump the traceback and exit if a test takes ' 'more than TIMEOUT seconds; disabled if TIMEOUT ' 'is negative or equals to zero') group.add_argument('--wait', action='store_true', help='wait for user input, e.g., allow a debugger ' 'to be attached') - group.add_argument('--worker-args', metavar='ARGS') group.add_argument('-S', '--start', metavar='START', help='the name of the test at which to start.' + more_details) group.add_argument('-p', '--python', metavar='PYTHON', help='Command to run Python test subprocesses with.') + group.add_argument('--randseed', metavar='SEED', + dest='random_seed', type=int, + help='pass a global random seed') group = parser.add_argument_group('Verbosity') group.add_argument('-v', '--verbose', action='count', help='run tests in verbose mode with output to stdout') - group.add_argument('-w', '--verbose2', action='store_true', + group.add_argument('-w', '--rerun', action='store_true', help='re-run failed tests in verbose mode') + group.add_argument('--verbose2', action='store_true', dest='rerun', + help='deprecated alias to --rerun') group.add_argument('-W', '--verbose3', action='store_true', help='display test output on failure') group.add_argument('-q', '--quiet', action='store_true', @@ -229,10 +242,6 @@ def _create_parser(): group = parser.add_argument_group('Selecting tests') group.add_argument('-r', '--randomize', action='store_true', help='randomize test execution order.' + more_details) - group.add_argument('--randseed', metavar='SEED', - dest='random_seed', type=int, - help='pass a random seed to reproduce a previous ' - 'random run') group.add_argument('-f', '--fromfile', metavar='FILE', help='read names of tests to run from a file.' + more_details) @@ -311,6 +320,9 @@ def _create_parser(): group.add_argument('--fail-env-changed', action='store_true', help='if a test file alters the environment, mark ' 'the test as failed') + group.add_argument('--fail-rerun', action='store_true', + help='if a test failed and then passed when re-run, ' + 'mark the tests as failed') group.add_argument('--junit-xml', dest='xmlpath', metavar='FILENAME', help='writes JUnit-style XML results to the specified ' @@ -319,6 +331,9 @@ def _create_parser(): help='override the working directory for the test run') group.add_argument('--cleanup', action='store_true', help='remove old test_python_* directories') + group.add_argument('--dont-add-python-opts', dest='_add_python_opts', + action='store_false', + help="internal option, don't use it") return parser @@ -369,7 +384,50 @@ def _parse_args(args, **kwargs): for arg in ns.args: if arg.startswith('-'): parser.error("unrecognized arguments: %s" % arg) - sys.exit(1) + + if ns.timeout is not None: + # Support "--timeout=" (no value) so Makefile.pre.pre TESTTIMEOUT + # can be used by "make buildbottest" and "make test". + if ns.timeout != "": + try: + ns.timeout = float(ns.timeout) + except ValueError: + parser.error(f"invalid timeout value: {ns.timeout!r}") + else: + ns.timeout = None + + # Continuous Integration (CI): common options for fast/slow CI modes + if ns.slow_ci or ns.fast_ci: + # Similar to options: + # + # -j0 --randomize --fail-env-changed --fail-rerun --rerun + # --slowest --verbose3 + if ns.use_mp is None: + ns.use_mp = 0 + ns.randomize = True + ns.fail_env_changed = True + ns.fail_rerun = True + if ns.python is None: + ns.rerun = True + ns.print_slow = True + ns.verbose3 = True + else: + ns._add_python_opts = False + + # When both --slow-ci and --fast-ci options are present, + # --slow-ci has the priority + if ns.slow_ci: + # Similar to: -u "all" --timeout=1200 + if not ns.use: + ns.use = [['all']] + if ns.timeout is None: + ns.timeout = 1200 # 20 minutes + elif ns.fast_ci: + # Similar to: -u "all,-cpu" --timeout=600 + if not ns.use: + ns.use = [['all', '-cpu']] + if ns.timeout is None: + ns.timeout = 600 # 10 minutes if ns.single and ns.fromfile: parser.error("-s and -f don't go together!") @@ -382,7 +440,7 @@ def _parse_args(args, **kwargs): ns.python = shlex.split(ns.python) if ns.failfast and not (ns.verbose or ns.verbose3): parser.error("-G/--failfast needs either -v or -W") - if ns.pgo and (ns.verbose or ns.verbose2 or ns.verbose3): + if ns.pgo and (ns.verbose or ns.rerun or ns.verbose3): parser.error("--pgo/-v don't go together!") if ns.pgo_extended: ns.pgo = True # pgo_extended implies pgo @@ -396,10 +454,6 @@ def _parse_args(args, **kwargs): if ns.timeout is not None: if ns.timeout <= 0: ns.timeout = None - if ns.use_mp is not None: - if ns.use_mp <= 0: - # Use all cores + extras for tests that like to sleep - ns.use_mp = 2 + (os.cpu_count() or 1) if ns.use: for a in ns.use: for r in a: @@ -443,4 +497,13 @@ def _parse_args(args, **kwargs): # --forever implies --failfast ns.failfast = True + if ns.huntrleaks: + warmup, repetitions, _ = ns.huntrleaks + if warmup < 1 or repetitions < 1: + msg = ("Invalid values for the --huntrleaks/-R parameters. The " + "number of warmups and repetitions must be at least 1 " + "each (1:1).") + print(msg, file=sys.stderr, flush=True) + sys.exit(2) + return ns diff --git a/Lib/test/libregrtest/findtests.py b/Lib/test/libregrtest/findtests.py new file mode 100644 index 00000000000000..96cc3e0d021184 --- /dev/null +++ b/Lib/test/libregrtest/findtests.py @@ -0,0 +1,105 @@ +import os +import sys +import unittest + +from test import support + +from .utils import ( + StrPath, TestName, TestTuple, TestList, FilterTuple, + abs_module_name, count, printlist) + + +# If these test directories are encountered recurse into them and treat each +# "test_*.py" file or each sub-directory as a separate test module. This can +# increase parallelism. +# +# Beware this can't generally be done for any directory with sub-tests as the +# __init__.py may do things which alter what tests are to be run. +SPLITTESTDIRS: set[TestName] = { + "test_asyncio", + "test_concurrent_futures", + "test_future_stmt", + "test_gdb", + "test_multiprocessing_fork", + "test_multiprocessing_forkserver", + "test_multiprocessing_spawn", +} + + +def findtestdir(path: StrPath | None = None) -> StrPath: + return path or os.path.dirname(os.path.dirname(__file__)) or os.curdir + + +def findtests(*, testdir: StrPath | None = None, exclude=(), + split_test_dirs: set[TestName] = SPLITTESTDIRS, + base_mod: str = "") -> TestList: + """Return a list of all applicable test modules.""" + testdir = findtestdir(testdir) + tests = [] + for name in os.listdir(testdir): + mod, ext = os.path.splitext(name) + if (not mod.startswith("test_")) or (mod in exclude): + continue + if base_mod: + fullname = f"{base_mod}.{mod}" + else: + fullname = mod + if fullname in split_test_dirs: + subdir = os.path.join(testdir, mod) + if not base_mod: + fullname = f"test.{mod}" + tests.extend(findtests(testdir=subdir, exclude=exclude, + split_test_dirs=split_test_dirs, + base_mod=fullname)) + elif ext in (".py", ""): + tests.append(fullname) + return sorted(tests) + + +def split_test_packages(tests, *, testdir: StrPath | None = None, exclude=(), + split_test_dirs=SPLITTESTDIRS): + testdir = findtestdir(testdir) + splitted = [] + for name in tests: + if name in split_test_dirs: + subdir = os.path.join(testdir, name) + splitted.extend(findtests(testdir=subdir, exclude=exclude, + split_test_dirs=split_test_dirs, + base_mod=name)) + else: + splitted.append(name) + return splitted + + +def _list_cases(suite): + for test in suite: + if isinstance(test, unittest.loader._FailedTest): + continue + if isinstance(test, unittest.TestSuite): + _list_cases(test) + elif isinstance(test, unittest.TestCase): + if support.match_test(test): + print(test.id()) + +def list_cases(tests: TestTuple, *, + match_tests: FilterTuple | None = None, + ignore_tests: FilterTuple | None = None, + test_dir: StrPath | None = None): + support.verbose = False + support.set_match_tests(match_tests, ignore_tests) + + skipped = [] + for test_name in tests: + module_name = abs_module_name(test_name, test_dir) + try: + suite = unittest.defaultTestLoader.loadTestsFromName(module_name) + _list_cases(suite) + except unittest.SkipTest: + skipped.append(test_name) + + if skipped: + sys.stdout.flush() + stderr = sys.stderr + print(file=stderr) + print(count(len(skipped), "test"), "skipped:", file=stderr) + printlist(skipped, file=stderr) diff --git a/Lib/test/libregrtest/logger.py b/Lib/test/libregrtest/logger.py new file mode 100644 index 00000000000000..a125706927393c --- /dev/null +++ b/Lib/test/libregrtest/logger.py @@ -0,0 +1,86 @@ +import os +import time + +from test.support import MS_WINDOWS +from .results import TestResults +from .runtests import RunTests +from .utils import print_warning + +if MS_WINDOWS: + from .win_utils import WindowsLoadTracker + + +class Logger: + def __init__(self, results: TestResults, quiet: bool, pgo: bool): + self.start_time = time.perf_counter() + self.test_count_text = '' + self.test_count_width = 3 + self.win_load_tracker: WindowsLoadTracker | None = None + self._results: TestResults = results + self._quiet: bool = quiet + self._pgo: bool = pgo + + def log(self, line: str = '') -> None: + empty = not line + + # add the system load prefix: "load avg: 1.80 " + load_avg = self.get_load_avg() + if load_avg is not None: + line = f"load avg: {load_avg:.2f} {line}" + + # add the timestamp prefix: "0:01:05 " + log_time = time.perf_counter() - self.start_time + + mins, secs = divmod(int(log_time), 60) + hours, mins = divmod(mins, 60) + formatted_log_time = "%d:%02d:%02d" % (hours, mins, secs) + + line = f"{formatted_log_time} {line}" + if empty: + line = line[:-1] + + print(line, flush=True) + + def get_load_avg(self) -> float | None: + if hasattr(os, 'getloadavg'): + return os.getloadavg()[0] + if self.win_load_tracker is not None: + return self.win_load_tracker.getloadavg() + return None + + def display_progress(self, test_index: int, text: str) -> None: + if self._quiet: + return + results = self._results + + # "[ 51/405/1] test_tcl passed" + line = f"{test_index:{self.test_count_width}}{self.test_count_text}" + fails = len(results.bad) + len(results.env_changed) + if fails and not self._pgo: + line = f"{line}/{fails}" + self.log(f"[{line}] {text}") + + def set_tests(self, runtests: RunTests) -> None: + if runtests.forever: + self.test_count_text = '' + self.test_count_width = 3 + else: + self.test_count_text = '/{}'.format(len(runtests.tests)) + self.test_count_width = len(self.test_count_text) - 1 + + def start_load_tracker(self) -> None: + if not MS_WINDOWS: + return + + try: + self.win_load_tracker = WindowsLoadTracker() + except PermissionError as error: + # Standard accounts may not have access to the performance + # counters. + print_warning(f'Failed to create WindowsLoadTracker: {error}') + + def stop_load_tracker(self) -> None: + if self.win_load_tracker is None: + return + self.win_load_tracker.close() + self.win_load_tracker = None diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index 7192244c69c55e..fe35df05c80e89 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -1,45 +1,30 @@ -import faulthandler -import locale import os -import platform import random import re +import shlex import sys import sysconfig -import tempfile import time -import unittest -from test.libregrtest.cmdline import _parse_args -from test.libregrtest.runtest import ( - findtests, split_test_packages, runtest, get_abs_module, - PROGRESS_MIN_TIME, State) -from test.libregrtest.setup import setup_tests -from test.libregrtest.pgo import setup_pgo_tests -from test.libregrtest.utils import (removepy, count, format_duration, - printlist, get_build_info) -from test import support -from test.support import TestStats -from test.support import os_helper -from test.support import threading_helper - - -# bpo-38203: Maximum delay in seconds to exit Python (call Py_Finalize()). -# Used to protect against threading._shutdown() hang. -# Must be smaller than buildbot "1200 seconds without output" limit. -EXIT_TIMEOUT = 120.0 -# gh-90681: When rerunning tests, we might need to rerun the whole -# class or module suite if some its life-cycle hooks fail. -# Test level hooks are not affected. -_TEST_LIFECYCLE_HOOKS = frozenset(( - 'setUpClass', 'tearDownClass', - 'setUpModule', 'tearDownModule', -)) - -EXITCODE_BAD_TEST = 2 -EXITCODE_INTERRUPTED = 130 -EXITCODE_ENV_CHANGED = 3 -EXITCODE_NO_TESTS_RAN = 4 +from test import support +from test.support import os_helper, MS_WINDOWS + +from .cmdline import _parse_args, Namespace +from .findtests import findtests, split_test_packages, list_cases +from .logger import Logger +from .pgo import setup_pgo_tests +from .result import State +from .results import TestResults, EXITCODE_INTERRUPTED +from .runtests import RunTests, HuntRefleak +from .setup import setup_process, setup_test_dir +from .single import run_single_test, PROGRESS_MIN_TIME +from .utils import ( + StrPath, StrJSON, TestName, TestList, TestTuple, FilterTuple, + strip_py_suffix, count, format_duration, + printlist, get_temp_dir, get_work_dir, exit_timeout, + display_header, cleanup_temp_dir, print_warning, + is_cross_compiled, get_host_runner, process_cpu_count, + EXIT_TIMEOUT) class Regrtest: @@ -65,266 +50,212 @@ class Regrtest: directly to set the values that would normally be set by flags on the command line. """ - def __init__(self): - # Namespace of command line options - self.ns = None + def __init__(self, ns: Namespace, _add_python_opts: bool = False): + # Log verbosity + self.verbose: int = int(ns.verbose) + self.quiet: bool = ns.quiet + self.pgo: bool = ns.pgo + self.pgo_extended: bool = ns.pgo_extended + + # Test results + self.results: TestResults = TestResults() + self.first_state: str | None = None + + # Logger + self.logger = Logger(self.results, self.quiet, self.pgo) + + # Actions + self.want_header: bool = ns.header + self.want_list_tests: bool = ns.list_tests + self.want_list_cases: bool = ns.list_cases + self.want_wait: bool = ns.wait + self.want_cleanup: bool = ns.cleanup + self.want_rerun: bool = ns.rerun + self.want_run_leaks: bool = ns.runleaks + + self.ci_mode: bool = (ns.fast_ci or ns.slow_ci) + self.want_add_python_opts: bool = (_add_python_opts + and ns._add_python_opts) + + # Select tests + if ns.match_tests: + self.match_tests: FilterTuple | None = tuple(ns.match_tests) + else: + self.match_tests = None + if ns.ignore_tests: + self.ignore_tests: FilterTuple | None = tuple(ns.ignore_tests) + else: + self.ignore_tests = None + self.exclude: bool = ns.exclude + self.fromfile: StrPath | None = ns.fromfile + self.starting_test: TestName | None = ns.start + self.cmdline_args: TestList = ns.args + + # Workers + if ns.use_mp is None: + num_workers = 0 # run sequentially + elif ns.use_mp <= 0: + num_workers = -1 # use the number of CPUs + else: + num_workers = ns.use_mp + self.num_workers: int = num_workers + self.worker_json: StrJSON | None = ns.worker_json + + # Options to run tests + self.fail_fast: bool = ns.failfast + self.fail_env_changed: bool = ns.fail_env_changed + self.fail_rerun: bool = ns.fail_rerun + self.forever: bool = ns.forever + self.output_on_failure: bool = ns.verbose3 + self.timeout: float | None = ns.timeout + if ns.huntrleaks: + warmups, runs, filename = ns.huntrleaks + filename = os.path.abspath(filename) + self.hunt_refleak: HuntRefleak | None = HuntRefleak(warmups, runs, filename) + else: + self.hunt_refleak = None + self.test_dir: StrPath | None = ns.testdir + self.junit_filename: StrPath | None = ns.xmlpath + self.memory_limit: str | None = ns.memlimit + self.gc_threshold: int | None = ns.threshold + self.use_resources: tuple[str, ...] = tuple(ns.use_resources) + if ns.python: + self.python_cmd: tuple[str, ...] | None = tuple(ns.python) + else: + self.python_cmd = None + self.coverage: bool = ns.trace + self.coverage_dir: StrPath | None = ns.coverdir + self.tmp_dir: StrPath | None = ns.tempdir + + # Randomize + self.randomize: bool = ns.randomize + self.random_seed: int | None = ( + ns.random_seed + if ns.random_seed is not None + else random.getrandbits(32) + ) + if 'SOURCE_DATE_EPOCH' in os.environ: + self.randomize = False + self.random_seed = None # tests - self.tests = [] - self.selected = [] - - # test results - self.good = [] - self.bad = [] - self.skipped = [] - self.resource_denied = [] - self.environment_changed = [] - self.run_no_tests = [] - self.need_rerun = [] - self.rerun = [] - self.first_result = None - self.interrupted = False - self.stats_dict: dict[str, TestStats] = {} - - # used by --slow - self.test_times = [] - - # used by --coverage, trace.Trace instance - self.tracer = None + self.first_runtests: RunTests | None = None + + # used by --slowest + self.print_slowest: bool = ns.print_slow # used to display the progress bar "[ 3/100]" self.start_time = time.perf_counter() - self.test_count = '' - self.test_count_width = 1 # used by --single - self.next_single_test = None - self.next_single_filename = None - - # used by --junit-xml - self.testsuite_xml = None - - # misc - self.win_load_tracker = None - self.tmp_dir = None - self.worker_test_name = None - - def get_executed(self): - return (set(self.good) | set(self.bad) | set(self.skipped) - | set(self.resource_denied) | set(self.environment_changed) - | set(self.run_no_tests)) - - def accumulate_result(self, result, rerun=False): - test_name = result.test_name - - if result.has_meaningful_duration() and not rerun: - self.test_times.append((result.duration, test_name)) - - match result.state: - case State.PASSED: - self.good.append(test_name) - case State.ENV_CHANGED: - self.environment_changed.append(test_name) - case State.SKIPPED: - self.skipped.append(test_name) - case State.RESOURCE_DENIED: - self.skipped.append(test_name) - self.resource_denied.append(test_name) - case State.INTERRUPTED: - self.interrupted = True - case State.DID_NOT_RUN: - self.run_no_tests.append(test_name) - case _: - if result.is_failed(self.ns.fail_env_changed): - if not rerun: - self.bad.append(test_name) - self.need_rerun.append(result) - else: - raise ValueError(f"invalid test state: {state!r}") - - if result.stats is not None: - self.stats_dict[result.test_name] = result.stats - - if rerun and not(result.is_failed(False) or result.state == State.INTERRUPTED): - self.bad.remove(test_name) - - xml_data = result.xml_data - if xml_data: - import xml.etree.ElementTree as ET - for e in xml_data: - try: - self.testsuite_xml.append(ET.fromstring(e)) - except ET.ParseError: - print(xml_data, file=sys.__stderr__) - raise + self.single_test_run: bool = ns.single + self.next_single_test: TestName | None = None + self.next_single_filename: StrPath | None = None def log(self, line=''): - empty = not line - - # add the system load prefix: "load avg: 1.80 " - load_avg = self.getloadavg() - if load_avg is not None: - line = f"load avg: {load_avg:.2f} {line}" - - # add the timestamp prefix: "0:01:05 " - test_time = time.perf_counter() - self.start_time - - mins, secs = divmod(int(test_time), 60) - hours, mins = divmod(mins, 60) - test_time = "%d:%02d:%02d" % (hours, mins, secs) - - line = f"{test_time} {line}" - if empty: - line = line[:-1] - - print(line, flush=True) - - def display_progress(self, test_index, text): - if self.ns.quiet: - return - - # "[ 51/405/1] test_tcl passed" - line = f"{test_index:{self.test_count_width}}{self.test_count}" - fails = len(self.bad) + len(self.environment_changed) - if fails and not self.ns.pgo: - line = f"{line}/{fails}" - self.log(f"[{line}] {text}") - - def parse_args(self, kwargs): - ns = _parse_args(sys.argv[1:], **kwargs) - - if ns.xmlpath: - support.junit_xml_list = self.testsuite_xml = [] - - worker_args = ns.worker_args - if worker_args is not None: - from test.libregrtest.runtest_mp import parse_worker_args - ns, test_name = parse_worker_args(ns.worker_args) - ns.worker_args = worker_args - self.worker_test_name = test_name - - # Strip .py extensions. - removepy(ns.args) + self.logger.log(line) - if ns.huntrleaks: - warmup, repetitions, _ = ns.huntrleaks - if warmup < 1 or repetitions < 1: - msg = ("Invalid values for the --huntrleaks/-R parameters. The " - "number of warmups and repetitions must be at least 1 " - "each (1:1).") - print(msg, file=sys.stderr, flush=True) - sys.exit(2) - - if ns.tempdir: - ns.tempdir = os.path.expanduser(ns.tempdir) - - self.ns = ns - - def find_tests(self, tests): - self.tests = tests - - if self.ns.single: + def find_tests(self, tests: TestList | None = None) -> tuple[TestTuple, TestList | None]: + if self.single_test_run: self.next_single_filename = os.path.join(self.tmp_dir, 'pynexttest') try: with open(self.next_single_filename, 'r') as fp: next_test = fp.read().strip() - self.tests = [next_test] + tests = [next_test] except OSError: pass - if self.ns.fromfile: - self.tests = [] + if self.fromfile: + tests = [] # regex to match 'test_builtin' in line: # '0:00:00 [ 4/400] test_builtin -- test_dict took 1 sec' regex = re.compile(r'\btest_[a-zA-Z0-9_]+\b') - with open(os.path.join(os_helper.SAVEDCWD, self.ns.fromfile)) as fp: + with open(os.path.join(os_helper.SAVEDCWD, self.fromfile)) as fp: for line in fp: line = line.split('#', 1)[0] line = line.strip() match = regex.search(line) if match is not None: - self.tests.append(match.group()) + tests.append(match.group()) - removepy(self.tests) + strip_py_suffix(tests) - if self.ns.pgo: + if self.pgo: # add default PGO tests if no tests are specified - setup_pgo_tests(self.ns) + setup_pgo_tests(self.cmdline_args, self.pgo_extended) - exclude = set() - if self.ns.exclude: - for arg in self.ns.args: - exclude.add(arg) - self.ns.args = [] + exclude_tests = set() + if self.exclude: + for arg in self.cmdline_args: + exclude_tests.add(arg) + self.cmdline_args = [] - alltests = findtests(testdir=self.ns.testdir, exclude=exclude) + alltests = findtests(testdir=self.test_dir, + exclude=exclude_tests) - if not self.ns.fromfile: - self.selected = self.tests or self.ns.args - if self.selected: - self.selected = split_test_packages(self.selected) + if not self.fromfile: + selected = tests or self.cmdline_args + if selected: + selected = split_test_packages(selected) else: - self.selected = alltests + selected = alltests else: - self.selected = self.tests + selected = tests - if self.ns.single: - self.selected = self.selected[:1] + if self.single_test_run: + selected = selected[:1] try: - pos = alltests.index(self.selected[0]) + pos = alltests.index(selected[0]) self.next_single_test = alltests[pos + 1] except IndexError: pass # Remove all the selected tests that precede start if it's set. - if self.ns.start: + if self.starting_test: try: - del self.selected[:self.selected.index(self.ns.start)] + del selected[:selected.index(self.starting_test)] except ValueError: - print("Couldn't find starting test (%s), using all tests" - % self.ns.start, file=sys.stderr) - - if self.ns.randomize: - if self.ns.random_seed is None: - self.ns.random_seed = random.randrange(10000000) - random.seed(self.ns.random_seed) - random.shuffle(self.selected) + print(f"Cannot find starting test: {self.starting_test}") + sys.exit(1) - def list_tests(self): - for name in self.selected: - print(name) - - def _list_cases(self, suite): - for test in suite: - if isinstance(test, unittest.loader._FailedTest): - continue - if isinstance(test, unittest.TestSuite): - self._list_cases(test) - elif isinstance(test, unittest.TestCase): - if support.match_test(test): - print(test.id()) - - def list_cases(self): - support.verbose = False - support.set_match_tests(self.ns.match_tests, self.ns.ignore_tests) - - for test_name in self.selected: - abstest = get_abs_module(self.ns, test_name) - try: - suite = unittest.defaultTestLoader.loadTestsFromName(abstest) - self._list_cases(suite) - except unittest.SkipTest: - self.skipped.append(test_name) + random.seed(self.random_seed) + if self.randomize: + random.shuffle(selected) - if self.skipped: - print(file=sys.stderr) - print(count(len(self.skipped), "test"), "skipped:", file=sys.stderr) - printlist(self.skipped, file=sys.stderr) + return (tuple(selected), tests) - def rerun_failed_tests(self): - self.log() + @staticmethod + def list_tests(tests: TestTuple): + for name in tests: + print(name) - if self.ns.python: + def _rerun_failed_tests(self, runtests: RunTests): + # Configure the runner to re-run tests + if self.num_workers == 0: + # Always run tests in fresh processes to have more deterministic + # initial state. Don't re-run tests in parallel but limit to a + # single worker process to have side effects (on the system load + # and timings) between tests. + self.num_workers = 1 + + tests, match_tests_dict = self.results.prepare_rerun() + + # Re-run failed tests + self.log(f"Re-running {len(tests)} failed tests in verbose mode in subprocesses") + runtests = runtests.copy( + tests=tests, + rerun=True, + verbose=True, + forever=False, + fail_fast=False, + match_tests_dict=match_tests_dict, + output_on_failure=False) + self.logger.set_tests(runtests) + self._run_tests_mp(runtests, self.num_workers) + return runtests + + def rerun_failed_tests(self, runtests: RunTests): + if self.python_cmd: # Temp patch for https://github.com/python/cpython/issues/94052 self.log( "Re-running failed tests is not supported with --python " @@ -332,160 +263,81 @@ def rerun_failed_tests(self): ) return - self.ns.verbose = True - self.ns.failfast = False - self.ns.verbose3 = False - - self.first_result = self.get_tests_result() - - self.log("Re-running failed tests in verbose mode") - rerun_list = list(self.need_rerun) - self.need_rerun.clear() - for result in rerun_list: - test_name = result.test_name - self.rerun.append(test_name) - - errors = result.errors or [] - failures = result.failures or [] - error_names = [ - self.normalize_test_name(test_full_name, is_error=True) - for (test_full_name, *_) in errors] - failure_names = [ - self.normalize_test_name(test_full_name) - for (test_full_name, *_) in failures] - self.ns.verbose = True - orig_match_tests = self.ns.match_tests - if errors or failures: - if self.ns.match_tests is None: - self.ns.match_tests = [] - self.ns.match_tests.extend(error_names) - self.ns.match_tests.extend(failure_names) - matching = "matching: " + ", ".join(self.ns.match_tests) - self.log(f"Re-running {test_name} in verbose mode ({matching})") - else: - self.log(f"Re-running {test_name} in verbose mode") - result = runtest(self.ns, test_name) - self.ns.match_tests = orig_match_tests + self.first_state = self.get_state() - self.accumulate_result(result, rerun=True) + print() + rerun_runtests = self._rerun_failed_tests(runtests) - if result.state == State.INTERRUPTED: - break + if self.results.bad: + print(count(len(self.results.bad), 'test'), "failed again:") + printlist(self.results.bad) + + self.display_result(rerun_runtests) - if self.bad: - print(count(len(self.bad), 'test'), "failed again:") - printlist(self.bad) - - self.display_result() - - def normalize_test_name(self, test_full_name, *, is_error=False): - short_name = test_full_name.split(" ")[0] - if is_error and short_name in _TEST_LIFECYCLE_HOOKS: - # This means that we have a failure in a life-cycle hook, - # we need to rerun the whole module or class suite. - # Basically the error looks like this: - # ERROR: setUpClass (test.test_reg_ex.RegTest) - # or - # ERROR: setUpModule (test.test_reg_ex) - # So, we need to parse the class / module name. - lpar = test_full_name.index('(') - rpar = test_full_name.index(')') - return test_full_name[lpar + 1: rpar].split('.')[-1] - return short_name - - def display_result(self): + def display_result(self, runtests): # If running the test suite for PGO then no one cares about results. - if self.ns.pgo: + if runtests.pgo: return + state = self.get_state() print() - print("== Tests result: %s ==" % self.get_tests_result()) - - if self.interrupted: - print("Test suite interrupted by signal SIGINT.") - - omitted = set(self.selected) - self.get_executed() - if omitted: - print() - print(count(len(omitted), "test"), "omitted:") - printlist(omitted) - - if self.good and not self.ns.quiet: - print() - if (not self.bad - and not self.skipped - and not self.interrupted - and len(self.good) > 1): - print("All", end=' ') - print(count(len(self.good), "test"), "OK.") - - if self.ns.print_slow: - self.test_times.sort(reverse=True) - print() - print("10 slowest tests:") - for test_time, test in self.test_times[:10]: - print("- %s: %s" % (test, format_duration(test_time))) - - if self.bad: - print() - print(count(len(self.bad), "test"), "failed:") - printlist(self.bad) - - if self.environment_changed: - print() - print("{} altered the execution environment:".format( - count(len(self.environment_changed), "test"))) - printlist(self.environment_changed) - - if self.skipped and not self.ns.quiet: - print() - print(count(len(self.skipped), "test"), "skipped:") - printlist(self.skipped) - - if self.rerun: - print() - print("%s:" % count(len(self.rerun), "re-run test")) - printlist(self.rerun) - - if self.run_no_tests: - print() - print(count(len(self.run_no_tests), "test"), "run no tests:") - printlist(self.run_no_tests) - - def run_tests_sequential(self): - if self.ns.trace: + print(f"== Tests result: {state} ==") + + self.results.display_result(runtests.tests, + self.quiet, self.print_slowest) + + def run_test(self, test_name: TestName, runtests: RunTests, tracer): + if tracer is not None: + # If we're tracing code coverage, then we don't exit with status + # if on a false return value from main. + cmd = ('result = run_single_test(test_name, runtests)') + namespace = dict(locals()) + tracer.runctx(cmd, globals=globals(), locals=namespace) + result = namespace['result'] + else: + result = run_single_test(test_name, runtests) + + self.results.accumulate_result(result, runtests) + + return result + + def run_tests_sequentially(self, runtests): + if self.coverage: import trace - self.tracer = trace.Trace(trace=False, count=True) + tracer = trace.Trace(trace=False, count=True) + else: + tracer = None save_modules = sys.modules.keys() - msg = "Run tests sequentially" - if self.ns.timeout: - msg += " (timeout: %s)" % format_duration(self.ns.timeout) + jobs = runtests.get_jobs() + if jobs is not None: + tests = count(jobs, 'test') + else: + tests = 'tests' + msg = f"Run {tests} sequentially" + if runtests.timeout: + msg += " (timeout: %s)" % format_duration(runtests.timeout) self.log(msg) previous_test = None - for test_index, test_name in enumerate(self.tests, 1): + tests_iter = runtests.iter_tests() + for test_index, test_name in enumerate(tests_iter, 1): start_time = time.perf_counter() text = test_name if previous_test: text = '%s -- %s' % (text, previous_test) - self.display_progress(test_index, text) - - if self.tracer: - # If we're tracing code coverage, then we don't exit with status - # if on a false return value from main. - cmd = ('result = runtest(self.ns, test_name); ' - 'self.accumulate_result(result)') - ns = dict(locals()) - self.tracer.runctx(cmd, globals=globals(), locals=ns) - result = ns['result'] - else: - result = runtest(self.ns, test_name) - self.accumulate_result(result) + self.logger.display_progress(test_index, text) + + result = self.run_test(test_name, runtests, tracer) - if result.state == State.INTERRUPTED: + # 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) + + if result.must_stop(self.fail_fast, self.fail_env_changed): break previous_test = str(result) @@ -496,140 +348,22 @@ def run_tests_sequential(self): # be quiet: say nothing if the test passed shortly previous_test = None - # 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) - - if self.ns.failfast and result.is_failed(self.ns.fail_env_changed): - break - if previous_test: print(previous_test) - def _test_forever(self, tests): - while True: - for test_name in tests: - yield test_name - if self.bad: - return - if self.ns.fail_env_changed and self.environment_changed: - return - - def display_header(self): - # Print basic platform information - print("==", platform.python_implementation(), *sys.version.split()) - print("==", platform.platform(aliased=True), - "%s-endian" % sys.byteorder) - print("== Python build:", ' '.join(get_build_info())) - print("== cwd:", os.getcwd()) - cpu_count = os.cpu_count() - if cpu_count: - print("== CPU count:", cpu_count) - print("== encodings: locale=%s, FS=%s" - % (locale.getencoding(), sys.getfilesystemencoding())) - self.display_sanitizers() - - def display_sanitizers(self): - # This makes it easier to remember what to set in your local - # environment when trying to reproduce a sanitizer failure. - asan = support.check_sanitizer(address=True) - msan = support.check_sanitizer(memory=True) - ubsan = support.check_sanitizer(ub=True) - sanitizers = [] - if asan: - sanitizers.append("address") - if msan: - sanitizers.append("memory") - if ubsan: - sanitizers.append("undefined behavior") - if not sanitizers: - return - - print(f"== sanitizers: {', '.join(sanitizers)}") - for sanitizer, env_var in ( - (asan, "ASAN_OPTIONS"), - (msan, "MSAN_OPTIONS"), - (ubsan, "UBSAN_OPTIONS"), - ): - options= os.environ.get(env_var) - if sanitizer and options is not None: - print(f"== {env_var}={options!r}") - - def no_tests_run(self): - return not any((self.good, self.bad, self.skipped, self.interrupted, - self.environment_changed)) - - def get_tests_result(self): - result = [] - if self.bad: - result.append("FAILURE") - elif self.ns.fail_env_changed and self.environment_changed: - result.append("ENV CHANGED") - elif self.no_tests_run(): - result.append("NO TESTS RAN") - - if self.interrupted: - result.append("INTERRUPTED") - - if not result: - result.append("SUCCESS") - - result = ', '.join(result) - if self.first_result: - result = '%s then %s' % (self.first_result, result) - return result + return tracer - def run_tests(self): - # For a partial run, we do not need to clutter the output. - if (self.ns.header - or not(self.ns.pgo or self.ns.quiet or self.ns.single - or self.tests or self.ns.args)): - self.display_header() - - if self.ns.huntrleaks: - warmup, repetitions, _ = self.ns.huntrleaks - if warmup < 3: - msg = ("WARNING: Running tests with --huntrleaks/-R and less than " - "3 warmup repetitions can give false positives!") - print(msg, file=sys.stdout, flush=True) - - if self.ns.randomize: - print("Using random seed", self.ns.random_seed) - - if self.ns.forever: - self.tests = self._test_forever(list(self.selected)) - self.test_count = '' - self.test_count_width = 3 - else: - self.tests = iter(self.selected) - self.test_count = '/{}'.format(len(self.selected)) - self.test_count_width = len(self.test_count) - 1 - - if self.ns.use_mp: - from test.libregrtest.runtest_mp import run_tests_multiprocess - # If we're on windows and this is the parent runner (not a worker), - # track the load average. - if sys.platform == 'win32' and self.worker_test_name is None: - from test.libregrtest.win_utils import WindowsLoadTracker - - try: - self.win_load_tracker = WindowsLoadTracker() - except PermissionError as error: - # Standard accounts may not have access to the performance - # counters. - print(f'Failed to create WindowsLoadTracker: {error}') + def get_state(self): + state = self.results.get_state(self.fail_env_changed) + if self.first_state: + state = f'{self.first_state} then {state}' + return state - try: - run_tests_multiprocess(self) - finally: - if self.win_load_tracker is not None: - self.win_load_tracker.close() - self.win_load_tracker = None - else: - self.run_tests_sequential() + def _run_tests_mp(self, runtests: RunTests, num_workers: int) -> None: + from .run_workers import RunWorkers + RunWorkers(num_workers, runtests, self.logger, self.results).run() - def finalize(self): + def finalize_tests(self, tracer): if self.next_single_filename: if self.next_single_test: with open(self.next_single_filename, 'w') as fp: @@ -637,232 +371,299 @@ def finalize(self): else: os.unlink(self.next_single_filename) - if self.tracer: - r = self.tracer.results() - r.write_results(show_missing=True, summary=True, - coverdir=self.ns.coverdir) - - print() - self.display_summary() + if tracer is not None: + results = tracer.results() + results.write_results(show_missing=True, summary=True, + coverdir=self.coverage_dir) - if self.ns.runleaks: + if self.want_run_leaks: os.system("leaks %d" % os.getpid()) + if self.junit_filename: + self.results.write_junit(self.junit_filename) + def display_summary(self): - duration = time.perf_counter() - self.start_time + duration = time.perf_counter() - self.logger.start_time + filtered = bool(self.match_tests) or bool(self.ignore_tests) # Total duration + print() print("Total duration: %s" % format_duration(duration)) - # Total tests - total = TestStats() - for stats in self.stats_dict.values(): - total.accumulate(stats) - stats = [f'run={total.tests_run:,}'] - if total.failures: - stats.append(f'failures={total.failures:,}') - if total.skipped: - stats.append(f'skipped={total.skipped:,}') - print(f"Total tests: {' '.join(stats)}") - - # Total test files - report = [f'success={len(self.good)}'] - if self.bad: - report.append(f'failed={len(self.bad)}') - if self.environment_changed: - report.append(f'env_changed={len(self.environment_changed)}') - if self.skipped: - report.append(f'skipped={len(self.skipped)}') - if self.resource_denied: - report.append(f'resource_denied={len(self.resource_denied)}') - if self.rerun: - report.append(f'rerun={len(self.rerun)}') - if self.run_no_tests: - report.append(f'run_no_tests={len(self.run_no_tests)}') - print(f"Total test files: {' '.join(report)}") + self.results.display_summary(self.first_runtests, filtered) # Result - result = self.get_tests_result() - print(f"Result: {result}") + state = self.get_state() + print(f"Result: {state}") + + def create_run_tests(self, tests: TestTuple): + return RunTests( + tests, + fail_fast=self.fail_fast, + fail_env_changed=self.fail_env_changed, + match_tests=self.match_tests, + ignore_tests=self.ignore_tests, + match_tests_dict=None, + rerun=False, + forever=self.forever, + pgo=self.pgo, + pgo_extended=self.pgo_extended, + output_on_failure=self.output_on_failure, + timeout=self.timeout, + verbose=self.verbose, + quiet=self.quiet, + hunt_refleak=self.hunt_refleak, + test_dir=self.test_dir, + use_junit=(self.junit_filename is not None), + memory_limit=self.memory_limit, + gc_threshold=self.gc_threshold, + use_resources=self.use_resources, + 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: + if self.hunt_refleak and self.hunt_refleak.warmups < 3: + msg = ("WARNING: Running tests with --huntrleaks/-R and " + "less than 3 warmup repetitions can give false positives!") + print(msg, file=sys.stdout, flush=True) + + if self.num_workers < 0: + # Use all CPUs + 2 extra worker processes for tests + # that like to sleep + self.num_workers = (process_cpu_count() or 1) + 2 - def save_xml_result(self): - if not self.ns.xmlpath and not self.testsuite_xml: - return + # For a partial run, we do not need to clutter the output. + if (self.want_header + or not(self.pgo or self.quiet or self.single_test_run + or tests or self.cmdline_args)): + display_header(self.use_resources, self.python_cmd) - import xml.etree.ElementTree as ET - root = ET.Element("testsuites") - - # Manually count the totals for the overall summary - totals = {'tests': 0, 'errors': 0, 'failures': 0} - for suite in self.testsuite_xml: - root.append(suite) - for k in totals: - try: - totals[k] += int(suite.get(k, 0)) - except ValueError: - pass - - for k, v in totals.items(): - root.set(k, str(v)) - - xmlpath = os.path.join(os_helper.SAVEDCWD, self.ns.xmlpath) - with open(xmlpath, 'wb') as f: - for s in ET.tostringlist(root): - f.write(s) - - def fix_umask(self): - if support.is_emscripten: - # Emscripten has default umask 0o777, which breaks some tests. - # see https://github.com/emscripten-core/emscripten/issues/17269 - old_mask = os.umask(0) - if old_mask == 0o777: - os.umask(0o027) - else: - os.umask(old_mask) - - def set_temp_dir(self): - if self.ns.tempdir: - self.tmp_dir = self.ns.tempdir - - if not self.tmp_dir: - # When tests are run from the Python build directory, it is best practice - # to keep the test files in a subfolder. This eases the cleanup of leftover - # files using the "make distclean" command. - if sysconfig.is_python_build(): - self.tmp_dir = sysconfig.get_config_var('abs_builddir') - if self.tmp_dir is None: - self.tmp_dir = sysconfig.get_config_var('abs_srcdir') - if not self.tmp_dir: - # gh-74470: On Windows, only srcdir is available. Using - # abs_builddir mostly matters on UNIX when building - # Python out of the source tree, especially when the - # source tree is read only. - self.tmp_dir = sysconfig.get_config_var('srcdir') - self.tmp_dir = os.path.join(self.tmp_dir, 'build') - else: - self.tmp_dir = tempfile.gettempdir() + print("Using random seed", self.random_seed) - self.tmp_dir = os.path.abspath(self.tmp_dir) + runtests = self.create_run_tests(selected) + self.first_runtests = runtests + self.logger.set_tests(runtests) - def create_temp_dir(self): - os.makedirs(self.tmp_dir, exist_ok=True) + setup_process() - # Define a writable temp dir that will be used as cwd while running - # the tests. The name of the dir includes the pid to allow parallel - # testing (see the -j option). - # Emscripten and WASI have stubbed getpid(), Emscripten has only - # milisecond clock resolution. Use randint() instead. - if sys.platform in {"emscripten", "wasi"}: - nounce = random.randint(0, 1_000_000) - else: - nounce = os.getpid() - if self.worker_test_name is not None: - test_cwd = 'test_python_worker_{}'.format(nounce) + if self.hunt_refleak and not self.num_workers: + # gh-109739: WindowsLoadTracker thread interfers with refleak check + use_load_tracker = False else: - test_cwd = 'test_python_{}'.format(nounce) - test_cwd += os_helper.FS_NONASCII - test_cwd = os.path.join(self.tmp_dir, test_cwd) - return test_cwd - - def cleanup(self): - import glob - - path = os.path.join(glob.escape(self.tmp_dir), 'test_python_*') - print("Cleanup %s directory" % self.tmp_dir) - for name in glob.glob(path): - if os.path.isdir(name): - print("Remove directory: %s" % name) - os_helper.rmtree(name) + # WindowsLoadTracker is only needed on Windows + use_load_tracker = MS_WINDOWS + + if use_load_tracker: + self.logger.start_load_tracker() + try: + if self.num_workers: + self._run_tests_mp(runtests, self.num_workers) + tracer = None else: - print("Remove file: %s" % name) - os_helper.unlink(name) + tracer = self.run_tests_sequentially(runtests) - def main(self, tests=None, **kwargs): - self.parse_args(kwargs) + self.display_result(runtests) - self.set_temp_dir() + if self.want_rerun and self.results.need_rerun(): + self.rerun_failed_tests(runtests) + finally: + if use_load_tracker: + self.logger.stop_load_tracker() - self.fix_umask() + self.display_summary() + self.finalize_tests(tracer) - if self.ns.cleanup: - self.cleanup() - sys.exit(0) + return self.results.get_exitcode(self.fail_env_changed, + self.fail_rerun) - test_cwd = self.create_temp_dir() + def run_tests(self, selected: TestTuple, tests: TestList | None) -> int: + os.makedirs(self.tmp_dir, exist_ok=True) + work_dir = get_work_dir(self.tmp_dir) - try: - # Run the tests in a context manager that temporarily changes the CWD - # to a temporary and writable directory. If it's not possible to - # create or change the CWD, the original CWD will be used. + # Put a timeout on Python exit + with exit_timeout(): + # Run the tests in a context manager that temporarily changes the + # CWD to a temporary and writable directory. If it's not possible + # to create or change the CWD, the original CWD will be used. # The original CWD is available from os_helper.SAVEDCWD. - with os_helper.temp_cwd(test_cwd, quiet=True): - # When using multiprocessing, worker processes will use test_cwd - # as their parent temporary directory. So when the main process - # exit, it removes also subdirectories of worker processes. - self.ns.tempdir = test_cwd - - self._main(tests, kwargs) - except SystemExit as exc: - # bpo-38203: Python can hang at exit in Py_Finalize(), especially - # on threading._shutdown() call: put a timeout - if threading_helper.can_start_thread: - faulthandler.dump_traceback_later(EXIT_TIMEOUT, exit=True) + with os_helper.temp_cwd(work_dir, quiet=True): + # When using multiprocessing, worker processes will use + # work_dir as their parent temporary directory. So when the + # main process exit, it removes also subdirectories of worker + # processes. + return self._run_tests(selected, tests) + + def _add_cross_compile_opts(self, regrtest_opts): + # WASM/WASI buildbot builders pass multiple PYTHON environment + # variables such as PYTHONPATH and _PYTHON_HOSTRUNNER. + keep_environ = bool(self.python_cmd) + environ = None + + # Are we using cross-compilation? + cross_compile = is_cross_compiled() + + # Get HOSTRUNNER + hostrunner = get_host_runner() + + if cross_compile: + # emulate -E, but keep PYTHONPATH + cross compile env vars, + # so test executable can load correct sysconfigdata file. + keep = { + '_PYTHON_PROJECT_BASE', + '_PYTHON_HOST_PLATFORM', + '_PYTHON_SYSCONFIGDATA_NAME', + 'PYTHONPATH' + } + old_environ = os.environ + new_environ = { + name: value for name, value in os.environ.items() + if not name.startswith(('PYTHON', '_PYTHON')) or name in keep + } + # Only set environ if at least one variable was removed + if new_environ != old_environ: + environ = new_environ + keep_environ = True + + if cross_compile and hostrunner: + if self.num_workers == 0: + # For now use only two cores for cross-compiled builds; + # hostrunner can be expensive. + regrtest_opts.extend(['-j', '2']) + + # If HOSTRUNNER is set and -p/--python option is not given, then + # use hostrunner to execute python binary for tests. + if not self.python_cmd: + buildpython = sysconfig.get_config_var("BUILDPYTHON") + python_cmd = f"{hostrunner} {buildpython}" + regrtest_opts.extend(["--python", python_cmd]) + keep_environ = True + + return (environ, keep_environ) + + def _add_ci_python_opts(self, python_opts, keep_environ): + # --fast-ci and --slow-ci add options to Python: + # "-u -W default -bb -E" + + # Unbuffered stdout and stderr + if not sys.stdout.write_through: + python_opts.append('-u') + + # Add warnings filter 'default' + if 'default' not in sys.warnoptions: + python_opts.extend(('-W', 'default')) + + # Error on bytes/str comparison + if sys.flags.bytes_warning < 2: + python_opts.append('-bb') + + if not keep_environ: + # Ignore PYTHON* environment variables + if not sys.flags.ignore_environment: + python_opts.append('-E') + + def _execute_python(self, cmd, environ): + # Make sure that messages before execv() are logged + sys.stdout.flush() + sys.stderr.flush() + + cmd_text = shlex.join(cmd) + try: + print(f"+ {cmd_text}", flush=True) - sys.exit(exc.code) + if hasattr(os, 'execv') and not MS_WINDOWS: + os.execv(cmd[0], cmd) + # On success, execv() do no return. + # On error, it raises an OSError. + else: + import subprocess + with subprocess.Popen(cmd, env=environ) as proc: + try: + proc.wait() + except KeyboardInterrupt: + # There is no need to call proc.terminate(): on CTRL+C, + # SIGTERM is also sent to the child process. + try: + proc.wait(timeout=EXIT_TIMEOUT) + except subprocess.TimeoutExpired: + proc.kill() + proc.wait() + sys.exit(EXITCODE_INTERRUPTED) + + sys.exit(proc.returncode) + except Exception as exc: + print_warning(f"Failed to change Python options: {exc!r}\n" + f"Command: {cmd_text}") + # continue executing main() + + def _add_python_opts(self): + python_opts = [] + regrtest_opts = [] + + environ, keep_environ = self._add_cross_compile_opts(regrtest_opts) + if self.ci_mode: + self._add_ci_python_opts(python_opts, keep_environ) + + if (not python_opts) and (not regrtest_opts) and (environ is None): + # Nothing changed: nothing to do + return - def getloadavg(self): - if self.win_load_tracker is not None: - return self.win_load_tracker.getloadavg() + # Create new command line + cmd = list(sys.orig_argv) + if python_opts: + cmd[1:1] = python_opts + if regrtest_opts: + cmd.extend(regrtest_opts) + cmd.append("--dont-add-python-opts") - if hasattr(os, 'getloadavg'): - return os.getloadavg()[0] + self._execute_python(cmd, environ) - return None + def _init(self): + # Set sys.stdout encoder error handler to backslashreplace, + # similar to sys.stderr error handler, to avoid UnicodeEncodeError + # when printing a traceback or any other non-encodable character. + sys.stdout.reconfigure(errors="backslashreplace") - def _main(self, tests, kwargs): - if self.worker_test_name is not None: - from test.libregrtest.runtest_mp import run_tests_worker - run_tests_worker(self.ns, self.worker_test_name) + if self.junit_filename and not os.path.isabs(self.junit_filename): + self.junit_filename = os.path.abspath(self.junit_filename) - if self.ns.wait: - input("Press any key to continue...") + strip_py_suffix(self.cmdline_args) - support.PGO = self.ns.pgo - support.PGO_EXTENDED = self.ns.pgo_extended + self.tmp_dir = get_temp_dir(self.tmp_dir) - setup_tests(self.ns) + def main(self, tests: TestList | None = None): + if self.want_add_python_opts: + self._add_python_opts() - self.find_tests(tests) + self._init() - if self.ns.list_tests: - self.list_tests() + if self.want_cleanup: + cleanup_temp_dir(self.tmp_dir) sys.exit(0) - if self.ns.list_cases: - self.list_cases() - sys.exit(0) - - self.run_tests() - self.display_result() - - if self.ns.verbose2 and self.bad: - self.rerun_failed_tests() - - self.finalize() + if self.want_wait: + input("Press any key to continue...") - self.save_xml_result() + setup_test_dir(self.test_dir) + selected, tests = self.find_tests(tests) + + exitcode = 0 + if self.want_list_tests: + self.list_tests(selected) + elif self.want_list_cases: + list_cases(selected, + match_tests=self.match_tests, + ignore_tests=self.ignore_tests, + test_dir=self.test_dir) + else: + exitcode = self.run_tests(selected, tests) - if self.bad: - sys.exit(EXITCODE_BAD_TEST) - if self.interrupted: - sys.exit(EXITCODE_INTERRUPTED) - if self.ns.fail_env_changed and self.environment_changed: - sys.exit(EXITCODE_ENV_CHANGED) - if self.no_tests_run(): - sys.exit(EXITCODE_NO_TESTS_RAN) - sys.exit(0) + sys.exit(exitcode) -def main(tests=None, **kwargs): +def main(tests=None, _add_python_opts=False, **kwargs): """Run the Python suite.""" - Regrtest().main(tests=tests, **kwargs) + ns = _parse_args(sys.argv[1:], **kwargs) + Regrtest(ns, _add_python_opts=_add_python_opts).main(tests=tests) diff --git a/Lib/test/libregrtest/pgo.py b/Lib/test/libregrtest/pgo.py index 42ce5fba7a97c3..e3a6927be5db1d 100644 --- a/Lib/test/libregrtest/pgo.py +++ b/Lib/test/libregrtest/pgo.py @@ -42,15 +42,15 @@ 'test_set', 'test_sqlite3', 'test_statistics', + 'test_str', 'test_struct', 'test_tabnanny', 'test_time', - 'test_unicode', 'test_xml_etree', 'test_xml_etree_c', ] -def setup_pgo_tests(ns): - if not ns.args and not ns.pgo_extended: +def setup_pgo_tests(cmdline_args, pgo_extended: bool): + if not cmdline_args and not pgo_extended: # run default set of tests for PGO training - ns.args = PGO_TESTS[:] + cmdline_args[:] = PGO_TESTS[:] diff --git a/Lib/test/libregrtest/refleak.py b/Lib/test/libregrtest/refleak.py index 58a1419e0501df..59f48bd1e3cf78 100644 --- a/Lib/test/libregrtest/refleak.py +++ b/Lib/test/libregrtest/refleak.py @@ -1,10 +1,13 @@ -import os import sys import warnings from inspect import isabstract +from typing import Any + from test import support from test.support import os_helper -from test.libregrtest.utils import clear_caches + +from .runtests import HuntRefleak +from .utils import clear_caches try: from _abc import _get_dump @@ -19,7 +22,9 @@ def _get_dump(cls): cls._abc_negative_cache, cls._abc_negative_cache_version) -def dash_R(ns, test_name, test_func): +def runtest_refleak(test_name, test_func, + hunt_refleak: HuntRefleak, + quiet: bool): """Run a test multiple times, looking for reference leaks. Returns: @@ -41,6 +46,7 @@ def dash_R(ns, test_name, test_func): fs = warnings.filters[:] ps = copyreg.dispatch_table.copy() pic = sys.path_importer_cache.copy() + zdc: dict[str, Any] | None try: import zipimport except ImportError: @@ -62,9 +68,10 @@ def dash_R(ns, test_name, test_func): def get_pooled_int(value): return int_pool.setdefault(value, value) - nwarmup, ntracked, fname = ns.huntrleaks - fname = os.path.join(os_helper.SAVEDCWD, fname) - repcount = nwarmup + ntracked + warmups = hunt_refleak.warmups + runs = hunt_refleak.runs + filename = hunt_refleak.filename + repcount = warmups + runs # Pre-allocate to ensure that the loop doesn't allocate anything new rep_range = list(range(repcount)) @@ -73,12 +80,11 @@ def get_pooled_int(value): fd_deltas = [0] * repcount getallocatedblocks = sys.getallocatedblocks gettotalrefcount = sys.gettotalrefcount - _getquickenedcount = sys._getquickenedcount fd_count = os_helper.fd_count # initialize variables to make pyflakes quiet rc_before = alloc_before = fd_before = 0 - if not ns.quiet: + if not quiet: print("beginning", repcount, "repetitions", file=sys.stderr) print(("1234567890"*(repcount//10 + 1))[:repcount], file=sys.stderr, flush=True) @@ -93,12 +99,12 @@ def get_pooled_int(value): dash_R_cleanup(fs, ps, pic, zdc, abcs) support.gc_collect() - # Read memory statistics immediately after the garbage collection - alloc_after = getallocatedblocks() - _getquickenedcount() + # Read memory statistics immediately after the garbage collection. + alloc_after = getallocatedblocks() rc_after = gettotalrefcount() fd_after = fd_count() - if not ns.quiet: + if not quiet: print('.', end='', file=sys.stderr, flush=True) rc_deltas[i] = get_pooled_int(rc_after - rc_before) @@ -109,7 +115,7 @@ def get_pooled_int(value): rc_before = rc_after fd_before = fd_after - if not ns.quiet: + if not quiet: print(file=sys.stderr) # These checkers return False on success, True on failure @@ -138,12 +144,12 @@ def check_fd_deltas(deltas): (fd_deltas, 'file descriptors', check_fd_deltas) ]: # ignore warmup runs - deltas = deltas[nwarmup:] + deltas = deltas[warmups:] if checker(deltas): msg = '%s leaked %s %s, sum=%s' % ( test_name, deltas, item_name, sum(deltas)) print(msg, file=sys.stderr, flush=True) - with open(fname, "a", encoding="utf-8") as refrep: + with open(filename, "a", encoding="utf-8") as refrep: print(msg, file=refrep) refrep.flush() failed = True @@ -169,6 +175,7 @@ def dash_R_cleanup(fs, ps, pic, zdc, abcs): zipimport._zip_directory_cache.update(zdc) # Clear ABC registries, restoring previously saved ABC registries. + # ignore deprecation warning for collections.abc.ByteString abs_classes = [getattr(collections.abc, a) for a in collections.abc.__all__] abs_classes = filter(isabstract, abs_classes) for abc in abs_classes: diff --git a/Lib/test/libregrtest/result.py b/Lib/test/libregrtest/result.py new file mode 100644 index 00000000000000..d6b0d5ad383a5b --- /dev/null +++ b/Lib/test/libregrtest/result.py @@ -0,0 +1,190 @@ +import dataclasses +import json +from typing import Any + +from test.support import TestStats + +from .utils import ( + StrJSON, TestName, FilterTuple, + format_duration, normalize_test_name, print_warning) + + +# Avoid enum.Enum to reduce the number of imports when tests are run +class State: + PASSED = "PASSED" + FAILED = "FAILED" + SKIPPED = "SKIPPED" + UNCAUGHT_EXC = "UNCAUGHT_EXC" + REFLEAK = "REFLEAK" + ENV_CHANGED = "ENV_CHANGED" + RESOURCE_DENIED = "RESOURCE_DENIED" + INTERRUPTED = "INTERRUPTED" + WORKER_FAILED = "WORKER_FAILED" # non-zero worker process exit code + WORKER_BUG = "WORKER_BUG" # exception when running a worker + DID_NOT_RUN = "DID_NOT_RUN" + TIMEOUT = "TIMEOUT" + + @staticmethod + def is_failed(state): + return state in { + State.FAILED, + State.UNCAUGHT_EXC, + State.REFLEAK, + State.WORKER_FAILED, + State.WORKER_BUG, + State.TIMEOUT} + + @staticmethod + def has_meaningful_duration(state): + # Consider that the duration is meaningless for these cases. + # For example, if a whole test file is skipped, its duration + # is unlikely to be the duration of executing its tests, + # but just the duration to execute code which skips the test. + return state not in { + State.SKIPPED, + State.RESOURCE_DENIED, + State.INTERRUPTED, + State.WORKER_FAILED, + State.WORKER_BUG, + State.DID_NOT_RUN} + + @staticmethod + def must_stop(state): + return state in { + State.INTERRUPTED, + State.WORKER_BUG, + } + + +@dataclasses.dataclass(slots=True) +class TestResult: + test_name: TestName + state: str | None = None + # Test duration in seconds + duration: float | None = None + xml_data: list[str] | None = None + stats: TestStats | None = None + + # errors and failures copied from support.TestFailedWithDetails + errors: list[tuple[str, str]] | None = None + failures: list[tuple[str, str]] | None = None + + def is_failed(self, fail_env_changed: bool) -> bool: + if self.state == State.ENV_CHANGED: + return fail_env_changed + return State.is_failed(self.state) + + def _format_failed(self): + if self.errors and self.failures: + le = len(self.errors) + lf = len(self.failures) + error_s = "error" + ("s" if le > 1 else "") + failure_s = "failure" + ("s" if lf > 1 else "") + return f"{self.test_name} failed ({le} {error_s}, {lf} {failure_s})" + + if self.errors: + le = len(self.errors) + error_s = "error" + ("s" if le > 1 else "") + return f"{self.test_name} failed ({le} {error_s})" + + if self.failures: + lf = len(self.failures) + failure_s = "failure" + ("s" if lf > 1 else "") + return f"{self.test_name} failed ({lf} {failure_s})" + + return f"{self.test_name} failed" + + def __str__(self) -> str: + match self.state: + case State.PASSED: + return f"{self.test_name} passed" + case State.FAILED: + return self._format_failed() + case State.SKIPPED: + return f"{self.test_name} skipped" + case State.UNCAUGHT_EXC: + return f"{self.test_name} failed (uncaught exception)" + case State.REFLEAK: + return f"{self.test_name} failed (reference leak)" + case State.ENV_CHANGED: + return f"{self.test_name} failed (env changed)" + case State.RESOURCE_DENIED: + return f"{self.test_name} skipped (resource denied)" + case State.INTERRUPTED: + return f"{self.test_name} interrupted" + case State.WORKER_FAILED: + return f"{self.test_name} worker non-zero exit code" + case State.WORKER_BUG: + return f"{self.test_name} worker bug" + case State.DID_NOT_RUN: + return f"{self.test_name} ran no tests" + case State.TIMEOUT: + return f"{self.test_name} timed out ({format_duration(self.duration)})" + case _: + raise ValueError("unknown result state: {state!r}") + + def has_meaningful_duration(self): + return State.has_meaningful_duration(self.state) + + def set_env_changed(self): + if self.state is None or self.state == State.PASSED: + self.state = State.ENV_CHANGED + + def must_stop(self, fail_fast: bool, fail_env_changed: bool) -> bool: + if State.must_stop(self.state): + return True + if fail_fast and self.is_failed(fail_env_changed): + return True + return False + + def get_rerun_match_tests(self) -> FilterTuple | None: + match_tests = [] + + errors = self.errors or [] + failures = self.failures or [] + for error_list, is_error in ( + (errors, True), + (failures, False), + ): + for full_name, *_ in error_list: + match_name = normalize_test_name(full_name, is_error=is_error) + if match_name is None: + # 'setUpModule (test.test_sys)': don't filter tests + return None + if not match_name: + error_type = "ERROR" if is_error else "FAIL" + print_warning(f"rerun failed to parse {error_type} test name: " + f"{full_name!r}: don't filter tests") + return None + match_tests.append(match_name) + + if not match_tests: + return None + return tuple(match_tests) + + def write_json_into(self, file) -> None: + json.dump(self, file, cls=_EncodeTestResult) + + @staticmethod + def from_json(worker_json: StrJSON) -> 'TestResult': + return json.loads(worker_json, object_hook=_decode_test_result) + + +class _EncodeTestResult(json.JSONEncoder): + def default(self, o: Any) -> dict[str, Any]: + if isinstance(o, TestResult): + result = dataclasses.asdict(o) + result["__test_result__"] = o.__class__.__name__ + return result + else: + return super().default(o) + + +def _decode_test_result(data: dict[str, Any]) -> TestResult | dict[str, Any]: + if "__test_result__" in data: + data.pop('__test_result__') + if data['stats'] is not None: + data['stats'] = TestStats(**data['stats']) + return TestResult(**data) + else: + return data diff --git a/Lib/test/libregrtest/results.py b/Lib/test/libregrtest/results.py new file mode 100644 index 00000000000000..3708078ff0bf3a --- /dev/null +++ b/Lib/test/libregrtest/results.py @@ -0,0 +1,261 @@ +import sys +from test.support import TestStats + +from .runtests import RunTests +from .result import State, TestResult +from .utils import ( + StrPath, TestName, TestTuple, TestList, FilterDict, + printlist, count, format_duration) + + +# Python uses exit code 1 when an exception is not catched +# argparse.ArgumentParser.error() uses exit code 2 +EXITCODE_BAD_TEST = 2 +EXITCODE_ENV_CHANGED = 3 +EXITCODE_NO_TESTS_RAN = 4 +EXITCODE_RERUN_FAIL = 5 +EXITCODE_INTERRUPTED = 130 # 128 + signal.SIGINT=2 + + +class TestResults: + def __init__(self): + self.bad: TestList = [] + self.good: TestList = [] + self.rerun_bad: TestList = [] + self.skipped: TestList = [] + self.resource_denied: TestList = [] + self.env_changed: TestList = [] + self.run_no_tests: TestList = [] + self.rerun: TestList = [] + self.rerun_results: list[TestResult] = [] + + self.interrupted: bool = False + self.worker_bug: bool = False + self.test_times: list[tuple[float, TestName]] = [] + self.stats = TestStats() + # used by --junit-xml + self.testsuite_xml: list[str] = [] + + def is_all_good(self): + return (not self.bad + and not self.skipped + and not self.interrupted + and not self.worker_bug) + + def get_executed(self): + return (set(self.good) | set(self.bad) | set(self.skipped) + | set(self.resource_denied) | set(self.env_changed) + | set(self.run_no_tests)) + + def no_tests_run(self): + return not any((self.good, self.bad, self.skipped, self.interrupted, + self.env_changed)) + + def get_state(self, fail_env_changed): + state = [] + if self.bad: + state.append("FAILURE") + elif fail_env_changed and self.env_changed: + state.append("ENV CHANGED") + elif self.no_tests_run(): + state.append("NO TESTS RAN") + + if self.interrupted: + state.append("INTERRUPTED") + if self.worker_bug: + state.append("WORKER BUG") + if not state: + state.append("SUCCESS") + + return ', '.join(state) + + def get_exitcode(self, fail_env_changed, fail_rerun): + exitcode = 0 + if self.bad: + exitcode = EXITCODE_BAD_TEST + elif self.interrupted: + exitcode = EXITCODE_INTERRUPTED + elif fail_env_changed and self.env_changed: + exitcode = EXITCODE_ENV_CHANGED + elif self.no_tests_run(): + exitcode = EXITCODE_NO_TESTS_RAN + elif fail_rerun and self.rerun: + exitcode = EXITCODE_RERUN_FAIL + elif self.worker_bug: + exitcode = EXITCODE_BAD_TEST + return exitcode + + def accumulate_result(self, result: TestResult, runtests: RunTests): + test_name = result.test_name + rerun = runtests.rerun + fail_env_changed = runtests.fail_env_changed + + match result.state: + case State.PASSED: + self.good.append(test_name) + case State.ENV_CHANGED: + self.env_changed.append(test_name) + self.rerun_results.append(result) + case State.SKIPPED: + self.skipped.append(test_name) + case State.RESOURCE_DENIED: + self.resource_denied.append(test_name) + case State.INTERRUPTED: + self.interrupted = True + case State.DID_NOT_RUN: + self.run_no_tests.append(test_name) + case _: + if result.is_failed(fail_env_changed): + self.bad.append(test_name) + self.rerun_results.append(result) + else: + raise ValueError(f"invalid test state: {result.state!r}") + + if result.state == State.WORKER_BUG: + self.worker_bug = True + + if result.has_meaningful_duration() and not rerun: + self.test_times.append((result.duration, test_name)) + if result.stats is not None: + self.stats.accumulate(result.stats) + if rerun: + self.rerun.append(test_name) + + xml_data = result.xml_data + if xml_data: + self.add_junit(xml_data) + + def need_rerun(self): + return bool(self.rerun_results) + + def prepare_rerun(self) -> tuple[TestTuple, FilterDict]: + tests: TestList = [] + match_tests_dict = {} + for result in self.rerun_results: + tests.append(result.test_name) + + match_tests = result.get_rerun_match_tests() + # ignore empty match list + if match_tests: + match_tests_dict[result.test_name] = match_tests + + # Clear previously failed tests + self.rerun_bad.extend(self.bad) + self.bad.clear() + self.env_changed.clear() + self.rerun_results.clear() + + return (tuple(tests), match_tests_dict) + + def add_junit(self, xml_data: list[str]): + import xml.etree.ElementTree as ET + for e in xml_data: + try: + self.testsuite_xml.append(ET.fromstring(e)) + except ET.ParseError: + print(xml_data, file=sys.__stderr__) + raise + + def write_junit(self, filename: StrPath): + if not self.testsuite_xml: + # Don't create empty XML file + return + + import xml.etree.ElementTree as ET + root = ET.Element("testsuites") + + # Manually count the totals for the overall summary + totals = {'tests': 0, 'errors': 0, 'failures': 0} + for suite in self.testsuite_xml: + root.append(suite) + for k in totals: + try: + totals[k] += int(suite.get(k, 0)) + except ValueError: + pass + + for k, v in totals.items(): + root.set(k, str(v)) + + with open(filename, 'wb') as f: + for s in ET.tostringlist(root): + f.write(s) + + def display_result(self, tests: TestTuple, quiet: bool, print_slowest: bool): + if print_slowest: + self.test_times.sort(reverse=True) + print() + print("10 slowest tests:") + for test_time, test in self.test_times[:10]: + print("- %s: %s" % (test, format_duration(test_time))) + + all_tests = [] + omitted = set(tests) - self.get_executed() + + # less important + all_tests.append((omitted, "test", "{} omitted:")) + if not quiet: + all_tests.append((self.skipped, "test", "{} skipped:")) + all_tests.append((self.resource_denied, "test", "{} skipped (resource denied):")) + all_tests.append((self.run_no_tests, "test", "{} run no tests:")) + + # more important + all_tests.append((self.env_changed, "test", "{} altered the execution environment (env changed):")) + all_tests.append((self.rerun, "re-run test", "{}:")) + all_tests.append((self.bad, "test", "{} failed:")) + + for tests_list, count_text, title_format in all_tests: + if tests_list: + print() + count_text = count(len(tests_list), count_text) + print(title_format.format(count_text)) + printlist(tests_list) + + if self.good and not quiet: + print() + text = count(len(self.good), "test") + text = f"{text} OK." + if (self.is_all_good() and len(self.good) > 1): + text = f"All {text}" + print(text) + + if self.interrupted: + print() + print("Test suite interrupted by signal SIGINT.") + + def display_summary(self, first_runtests: RunTests, filtered: bool): + # Total tests + stats = self.stats + text = f'run={stats.tests_run:,}' + if filtered: + text = f"{text} (filtered)" + report = [text] + if stats.failures: + report.append(f'failures={stats.failures:,}') + if stats.skipped: + report.append(f'skipped={stats.skipped:,}') + print(f"Total tests: {' '.join(report)}") + + # Total test files + all_tests = [self.good, self.bad, self.rerun, + self.skipped, + self.env_changed, self.run_no_tests] + run = sum(map(len, all_tests)) + text = f'run={run}' + if not first_runtests.forever: + ntest = len(first_runtests.tests) + text = f"{text}/{ntest}" + if filtered: + text = f"{text} (filtered)" + report = [text] + for name, tests in ( + ('failed', self.bad), + ('env_changed', self.env_changed), + ('skipped', self.skipped), + ('resource_denied', self.resource_denied), + ('rerun', self.rerun), + ('run_no_tests', self.run_no_tests), + ): + if tests: + report.append(f'{name}={len(tests)}') + print(f"Total test files: {' '.join(report)}") diff --git a/Lib/test/libregrtest/run_workers.py b/Lib/test/libregrtest/run_workers.py new file mode 100644 index 00000000000000..16f8331abd32f9 --- /dev/null +++ b/Lib/test/libregrtest/run_workers.py @@ -0,0 +1,607 @@ +import contextlib +import dataclasses +import faulthandler +import os.path +import queue +import signal +import subprocess +import sys +import tempfile +import threading +import time +import traceback +from typing import Literal, TextIO + +from test import support +from test.support import os_helper, MS_WINDOWS + +from .logger import Logger +from .result import TestResult, State +from .results import TestResults +from .runtests import RunTests, JsonFile, JsonFileType +from .single import PROGRESS_MIN_TIME +from .utils import ( + StrPath, TestName, + format_duration, print_warning, count, plural, get_signal_name) +from .worker import create_worker_process, USE_PROCESS_GROUP + +if MS_WINDOWS: + import locale + import msvcrt + + + +# Display the running tests if nothing happened last N seconds +PROGRESS_UPDATE = 30.0 # seconds +assert PROGRESS_UPDATE >= PROGRESS_MIN_TIME + +# Kill the main process after 5 minutes. It is supposed to write an update +# every PROGRESS_UPDATE seconds. Tolerate 5 minutes for Python slowest +# buildbot workers. +MAIN_PROCESS_TIMEOUT = 5 * 60.0 +assert MAIN_PROCESS_TIMEOUT >= PROGRESS_UPDATE + +# Time to wait until a worker completes: should be immediate +WAIT_COMPLETED_TIMEOUT = 30.0 # seconds + +# Time to wait a killed process (in seconds) +WAIT_KILLED_TIMEOUT = 60.0 + + +# We do not use a generator so multiple threads can call next(). +class MultiprocessIterator: + + """A thread-safe iterator over tests for multiprocess mode.""" + + def __init__(self, tests_iter): + self.lock = threading.Lock() + self.tests_iter = tests_iter + + def __iter__(self): + return self + + def __next__(self): + with self.lock: + if self.tests_iter is None: + raise StopIteration + return next(self.tests_iter) + + def stop(self): + with self.lock: + self.tests_iter = None + + +@dataclasses.dataclass(slots=True, frozen=True) +class MultiprocessResult: + result: TestResult + # bpo-45410: stderr is written into stdout to keep messages order + worker_stdout: str | None = None + err_msg: str | None = None + + +ExcStr = str +QueueOutput = tuple[Literal[False], MultiprocessResult] | tuple[Literal[True], ExcStr] + + +class ExitThread(Exception): + pass + + +class WorkerError(Exception): + def __init__(self, + test_name: TestName, + err_msg: str | None, + stdout: str | None, + state: str): + result = TestResult(test_name, state=state) + self.mp_result = MultiprocessResult(result, stdout, err_msg) + super().__init__() + + +class WorkerThread(threading.Thread): + def __init__(self, worker_id: int, runner: "RunWorkers") -> None: + super().__init__() + self.worker_id = worker_id + self.runtests = runner.runtests + self.pending = runner.pending + self.output = runner.output + self.timeout = runner.worker_timeout + self.log = runner.log + self.test_name: TestName | None = None + self.start_time: float | None = None + self._popen: subprocess.Popen[str] | None = None + self._killed = False + self._stopped = False + + def __repr__(self) -> str: + info = [f'WorkerThread #{self.worker_id}'] + if self.is_alive(): + info.append("running") + else: + info.append('stopped') + test = self.test_name + if test: + info.append(f'test={test}') + popen = self._popen + if popen is not None: + dt = time.monotonic() - self.start_time + info.extend((f'pid={self._popen.pid}', + f'time={format_duration(dt)}')) + return '<%s>' % ' '.join(info) + + def _kill(self) -> None: + popen = self._popen + if popen is None: + return + + if self._killed: + return + self._killed = True + + if USE_PROCESS_GROUP: + what = f"{self} process group" + else: + what = f"{self} process" + + print(f"Kill {what}", file=sys.stderr, flush=True) + try: + if USE_PROCESS_GROUP: + os.killpg(popen.pid, signal.SIGKILL) + else: + popen.kill() + except ProcessLookupError: + # popen.kill(): the process completed, the WorkerThread thread + # read its exit status, but Popen.send_signal() read the returncode + # just before Popen.wait() set returncode. + pass + except OSError as exc: + print_warning(f"Failed to kill {what}: {exc!r}") + + def stop(self) -> None: + # Method called from a different thread to stop this thread + self._stopped = True + self._kill() + + def _run_process(self, runtests: RunTests, output_fd: int, + tmp_dir: StrPath | None = None) -> int | None: + popen = create_worker_process(runtests, output_fd, tmp_dir) + self._popen = popen + self._killed = False + + try: + if self._stopped: + # If kill() has been called before self._popen is set, + # self._popen is still running. Call again kill() + # to ensure that the process is killed. + self._kill() + raise ExitThread + + try: + # gh-94026: stdout+stderr are written to tempfile + retcode = popen.wait(timeout=self.timeout) + assert retcode is not None + return retcode + except subprocess.TimeoutExpired: + if self._stopped: + # kill() has been called: communicate() fails on reading + # closed stdout + raise ExitThread + + # On timeout, kill the process + self._kill() + + # None means TIMEOUT for the caller + retcode = None + # bpo-38207: Don't attempt to call communicate() again: on it + # can hang until all child processes using stdout + # pipes completes. + except OSError: + if self._stopped: + # kill() has been called: communicate() fails + # on reading closed stdout + raise ExitThread + raise + except: + self._kill() + raise + finally: + self._wait_completed() + self._popen = None + + def create_stdout(self, stack: contextlib.ExitStack) -> TextIO: + """Create stdout temporay file (file descriptor).""" + + if MS_WINDOWS: + # gh-95027: When stdout is not a TTY, Python uses the ANSI code + # page for the sys.stdout encoding. If the main process runs in a + # terminal, sys.stdout uses WindowsConsoleIO with UTF-8 encoding. + encoding = locale.getencoding() + else: + encoding = sys.stdout.encoding + + # gh-94026: Write stdout+stderr to a tempfile as workaround for + # non-blocking pipes on Emscripten with NodeJS. + # gh-109425: Use "backslashreplace" error handler: log corrupted + # stdout+stderr, instead of failing with a UnicodeDecodeError and not + # logging stdout+stderr at all. + stdout_file = tempfile.TemporaryFile('w+', + encoding=encoding, + errors='backslashreplace') + stack.enter_context(stdout_file) + return stdout_file + + def create_json_file(self, stack: contextlib.ExitStack) -> tuple[JsonFile, TextIO | None]: + """Create JSON file.""" + + json_file_use_stdout = self.runtests.json_file_use_stdout() + if json_file_use_stdout: + json_file = JsonFile(None, JsonFileType.STDOUT) + json_tmpfile = None + else: + json_tmpfile = tempfile.TemporaryFile('w+', encoding='utf8') + stack.enter_context(json_tmpfile) + + json_fd = json_tmpfile.fileno() + if MS_WINDOWS: + json_handle = msvcrt.get_osfhandle(json_fd) + json_file = JsonFile(json_handle, + JsonFileType.WINDOWS_HANDLE) + else: + 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.""" + + tests = (test_name,) + if self.runtests.rerun: + match_tests = self.runtests.get_match_tests(test_name) + else: + match_tests = None + + kwargs = {} + if match_tests: + kwargs['match_tests'] = match_tests + if self.runtests.output_on_failure: + kwargs['verbose'] = True + kwargs['output_on_failure'] = False + return self.runtests.copy( + tests=tests, + json_file=json_file, + **kwargs) + + def run_tmp_files(self, worker_runtests: RunTests, + 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 + # Python finalization: too late for libregrtest. + if not support.is_wasi: + # Don't check for leaked temporary files and directories if Python is + # run on WASI. WASI don't pass environment variables like TMPDIR to + # worker processes. + tmp_dir = tempfile.mkdtemp(prefix="test_python_") + tmp_dir = os.path.abspath(tmp_dir) + try: + retcode = self._run_process(worker_runtests, + stdout_fd, tmp_dir) + finally: + tmp_files = os.listdir(tmp_dir) + os_helper.rmtree(tmp_dir) + else: + retcode = self._run_process(worker_runtests, stdout_fd) + tmp_files = [] + + return (retcode, tmp_files) + + def read_stdout(self, stdout_file: TextIO) -> str: + stdout_file.seek(0) + try: + return stdout_file.read().strip() + except Exception as exc: + # gh-101634: Catch UnicodeDecodeError if stdout cannot be + # decoded from encoding + raise WorkerError(self.test_name, + f"Cannot read process stdout: {exc}", + stdout=None, + state=State.WORKER_BUG) + + def read_json(self, json_file: JsonFile, json_tmpfile: TextIO | None, + stdout: str) -> tuple[TestResult, str]: + try: + if json_tmpfile is not None: + json_tmpfile.seek(0) + worker_json = json_tmpfile.read() + elif json_file.file_type == JsonFileType.STDOUT: + stdout, _, worker_json = stdout.rpartition("\n") + stdout = stdout.rstrip() + else: + with json_file.open(encoding='utf8') as json_fp: + worker_json = json_fp.read() + except Exception as exc: + # gh-101634: Catch UnicodeDecodeError if stdout cannot be + # decoded from encoding + err_msg = f"Failed to read worker process JSON: {exc}" + raise WorkerError(self.test_name, err_msg, stdout, + state=State.WORKER_BUG) + + if not worker_json: + raise WorkerError(self.test_name, "empty JSON", stdout, + state=State.WORKER_BUG) + + try: + result = TestResult.from_json(worker_json) + except Exception as exc: + # gh-101634: Catch UnicodeDecodeError if stdout cannot be + # decoded from encoding + err_msg = f"Failed to parse worker process JSON: {exc}" + raise WorkerError(self.test_name, err_msg, stdout, + state=State.WORKER_BUG) + + return (result, stdout) + + def _runtest(self, test_name: TestName) -> MultiprocessResult: + with contextlib.ExitStack() as stack: + stdout_file = self.create_stdout(stack) + json_file, json_tmpfile = self.create_json_file(stack) + worker_runtests = self.create_worker_runtests(test_name, json_file) + + retcode, tmp_files = self.run_tmp_files(worker_runtests, + stdout_file.fileno()) + + stdout = self.read_stdout(stdout_file) + + if retcode is None: + raise WorkerError(self.test_name, stdout=stdout, + err_msg=None, + state=State.TIMEOUT) + if retcode != 0: + name = get_signal_name(retcode) + if name: + retcode = f"{retcode} ({name})" + raise WorkerError(self.test_name, f"Exit code {retcode}", stdout, + state=State.WORKER_FAILED) + + result, stdout = self.read_json(json_file, json_tmpfile, stdout) + + if tmp_files: + msg = (f'\n\n' + f'Warning -- {test_name} leaked temporary files ' + f'({len(tmp_files)}): {", ".join(sorted(tmp_files))}') + stdout += msg + result.set_env_changed() + + return MultiprocessResult(result, stdout) + + def run(self) -> None: + fail_fast = self.runtests.fail_fast + fail_env_changed = self.runtests.fail_env_changed + while not self._stopped: + try: + try: + test_name = next(self.pending) + except StopIteration: + break + + self.start_time = time.monotonic() + self.test_name = test_name + try: + mp_result = self._runtest(test_name) + except WorkerError as exc: + mp_result = exc.mp_result + finally: + self.test_name = None + mp_result.result.duration = time.monotonic() - self.start_time + self.output.put((False, mp_result)) + + if mp_result.result.must_stop(fail_fast, fail_env_changed): + break + except ExitThread: + break + except BaseException: + self.output.put((True, traceback.format_exc())) + break + + def _wait_completed(self) -> None: + popen = self._popen + + try: + popen.wait(WAIT_COMPLETED_TIMEOUT) + except (subprocess.TimeoutExpired, OSError) as exc: + print_warning(f"Failed to wait for {self} completion " + f"(timeout={format_duration(WAIT_COMPLETED_TIMEOUT)}): " + f"{exc!r}") + + def wait_stopped(self, start_time: float) -> None: + # bpo-38207: RunWorkers.stop_workers() called self.stop() + # which killed the process. Sometimes, killing the process from the + # main thread does not interrupt popen.communicate() in + # WorkerThread thread. This loop with a timeout is a workaround + # for that. + # + # Moreover, if this method fails to join the thread, it is likely + # that Python will hang at exit while calling threading._shutdown() + # which tries again to join the blocked thread. Regrtest.main() + # uses EXIT_TIMEOUT to workaround this second bug. + while True: + # Write a message every second + self.join(1.0) + if not self.is_alive(): + break + dt = time.monotonic() - start_time + self.log(f"Waiting for {self} thread for {format_duration(dt)}") + if dt > WAIT_KILLED_TIMEOUT: + print_warning(f"Failed to join {self} in {format_duration(dt)}") + break + + +def get_running(workers: list[WorkerThread]) -> str | None: + running: list[str] = [] + for worker in workers: + test_name = worker.test_name + if not test_name: + continue + dt = time.monotonic() - worker.start_time + if dt >= PROGRESS_MIN_TIME: + text = f'{test_name} ({format_duration(dt)})' + running.append(text) + if not running: + return None + return f"running ({len(running)}): {', '.join(running)}" + + +class RunWorkers: + def __init__(self, num_workers: int, runtests: RunTests, + logger: Logger, results: TestResults) -> None: + self.num_workers = num_workers + self.runtests = runtests + self.log = logger.log + self.display_progress = logger.display_progress + self.results: TestResults = results + + self.output: queue.Queue[QueueOutput] = queue.Queue() + tests_iter = runtests.iter_tests() + self.pending = MultiprocessIterator(tests_iter) + self.timeout = runtests.timeout + if self.timeout is not None: + # Rely on faulthandler to kill a worker process. This timouet is + # when faulthandler fails to kill a worker process. Give a maximum + # of 5 minutes to faulthandler to kill the worker. + self.worker_timeout: float | None = min(self.timeout * 1.5, self.timeout + 5 * 60) + else: + self.worker_timeout = None + self.workers: list[WorkerThread] | None = None + + jobs = self.runtests.get_jobs() + if jobs is not None: + # Don't spawn more threads than the number of jobs: + # these worker threads would never get anything to do. + self.num_workers = min(self.num_workers, jobs) + + def start_workers(self) -> None: + self.workers = [WorkerThread(index, self) + for index in range(1, self.num_workers + 1)] + jobs = self.runtests.get_jobs() + if jobs is not None: + tests = count(jobs, 'test') + else: + tests = 'tests' + nworkers = len(self.workers) + processes = plural(nworkers, "process", "processes") + msg = (f"Run {tests} in parallel using " + f"{nworkers} worker {processes}") + if self.timeout: + msg += (" (timeout: %s, worker timeout: %s)" + % (format_duration(self.timeout), + format_duration(self.worker_timeout))) + self.log(msg) + for worker in self.workers: + worker.start() + + def stop_workers(self) -> None: + start_time = time.monotonic() + for worker in self.workers: + worker.stop() + for worker in self.workers: + worker.wait_stopped(start_time) + + def _get_result(self) -> QueueOutput | None: + pgo = self.runtests.pgo + use_faulthandler = (self.timeout is not None) + + # bpo-46205: check the status of workers every iteration to avoid + # waiting forever on an empty queue. + while any(worker.is_alive() for worker in self.workers): + if use_faulthandler: + faulthandler.dump_traceback_later(MAIN_PROCESS_TIMEOUT, + exit=True) + + # wait for a thread + try: + return self.output.get(timeout=PROGRESS_UPDATE) + except queue.Empty: + pass + + if not pgo: + # display progress + running = get_running(self.workers) + if running: + self.log(running) + + # all worker threads are done: consume pending results + try: + return self.output.get(timeout=0) + except queue.Empty: + return None + + def display_result(self, mp_result: MultiprocessResult) -> None: + result = mp_result.result + pgo = self.runtests.pgo + + text = str(result) + if mp_result.err_msg: + # WORKER_BUG + text += ' (%s)' % mp_result.err_msg + elif (result.duration >= PROGRESS_MIN_TIME and not pgo): + text += ' (%s)' % format_duration(result.duration) + if not pgo: + running = get_running(self.workers) + if running: + text += f' -- {running}' + self.display_progress(self.test_index, text) + + def _process_result(self, item: QueueOutput) -> TestResult: + """Returns True if test runner must stop.""" + if item[0]: + # Thread got an exception + format_exc = item[1] + print_warning(f"regrtest worker thread failed: {format_exc}") + result = TestResult("", state=State.WORKER_BUG) + self.results.accumulate_result(result, self.runtests) + return result + + self.test_index += 1 + mp_result = item[1] + result = mp_result.result + self.results.accumulate_result(result, self.runtests) + self.display_result(mp_result) + + # Display worker stdout + if not self.runtests.output_on_failure: + show_stdout = True + else: + # --verbose3 ignores stdout on success + show_stdout = (result.state != State.PASSED) + if show_stdout: + stdout = mp_result.worker_stdout + if stdout: + print(stdout, flush=True) + + return result + + def run(self) -> None: + fail_fast = self.runtests.fail_fast + fail_env_changed = self.runtests.fail_env_changed + + self.start_workers() + + self.test_index = 0 + try: + while True: + item = self._get_result() + if item is None: + break + + result = self._process_result(item) + if result.must_stop(fail_fast, fail_env_changed): + break + except KeyboardInterrupt: + print() + self.results.interrupted = True + finally: + if self.timeout is not None: + faulthandler.cancel_dump_traceback_later() + + # Always ensure that all worker processes are no longer + # worker when we exit this function + self.pending.stop() + self.stop_workers() diff --git a/Lib/test/libregrtest/runtest.py b/Lib/test/libregrtest/runtest.py deleted file mode 100644 index f37093b81cf284..00000000000000 --- a/Lib/test/libregrtest/runtest.py +++ /dev/null @@ -1,479 +0,0 @@ -import dataclasses -import doctest -import faulthandler -import functools -import gc -import importlib -import io -import os -import sys -import time -import traceback -import unittest - -from test import support -from test.support import TestStats -from test.support import os_helper -from test.support import threading_helper -from test.libregrtest.cmdline import Namespace -from test.libregrtest.save_env import saved_test_environment -from test.libregrtest.utils import clear_caches, format_duration, print_warning - - -# Avoid enum.Enum to reduce the number of imports when tests are run -class State: - PASSED = "PASSED" - FAILED = "FAILED" - SKIPPED = "SKIPPED" - UNCAUGHT_EXC = "UNCAUGHT_EXC" - REFLEAK = "REFLEAK" - ENV_CHANGED = "ENV_CHANGED" - RESOURCE_DENIED = "RESOURCE_DENIED" - INTERRUPTED = "INTERRUPTED" - MULTIPROCESSING_ERROR = "MULTIPROCESSING_ERROR" - DID_NOT_RUN = "DID_NOT_RUN" - TIMEOUT = "TIMEOUT" - - @staticmethod - def is_failed(state): - return state in { - State.FAILED, - State.UNCAUGHT_EXC, - State.REFLEAK, - State.MULTIPROCESSING_ERROR, - State.TIMEOUT} - - @staticmethod - def has_meaningful_duration(state): - # Consider that the duration is meaningless for these cases. - # For example, if a whole test file is skipped, its duration - # is unlikely to be the duration of executing its tests, - # but just the duration to execute code which skips the test. - return state not in { - State.SKIPPED, - State.RESOURCE_DENIED, - State.INTERRUPTED, - State.MULTIPROCESSING_ERROR, - State.DID_NOT_RUN} - - -@dataclasses.dataclass(slots=True) -class TestResult: - test_name: str - state: str | None = None - # Test duration in seconds - duration: float | None = None - xml_data: list[str] | None = None - stats: TestStats | None = None - - # errors and failures copied from support.TestFailedWithDetails - errors: list[tuple[str, str]] | None = None - failures: list[tuple[str, str]] | None = None - - def is_failed(self, fail_env_changed: bool) -> bool: - if self.state == State.ENV_CHANGED: - return fail_env_changed - return State.is_failed(self.state) - - def _format_failed(self): - if self.errors and self.failures: - le = len(self.errors) - lf = len(self.failures) - error_s = "error" + ("s" if le > 1 else "") - failure_s = "failure" + ("s" if lf > 1 else "") - return f"{self.test_name} failed ({le} {error_s}, {lf} {failure_s})" - - if self.errors: - le = len(self.errors) - error_s = "error" + ("s" if le > 1 else "") - return f"{self.test_name} failed ({le} {error_s})" - - if self.failures: - lf = len(self.failures) - failure_s = "failure" + ("s" if lf > 1 else "") - return f"{self.test_name} failed ({lf} {failure_s})" - - return f"{self.test_name} failed" - - def __str__(self) -> str: - match self.state: - case State.PASSED: - return f"{self.test_name} passed" - case State.FAILED: - return self._format_failed() - case State.SKIPPED: - return f"{self.test_name} skipped" - case State.UNCAUGHT_EXC: - return f"{self.test_name} failed (uncaught exception)" - case State.REFLEAK: - return f"{self.test_name} failed (reference leak)" - case State.ENV_CHANGED: - return f"{self.test_name} failed (env changed)" - case State.RESOURCE_DENIED: - return f"{self.test_name} skipped (resource denied)" - case State.INTERRUPTED: - return f"{self.test_name} interrupted" - case State.MULTIPROCESSING_ERROR: - return f"{self.test_name} process crashed" - case State.DID_NOT_RUN: - return f"{self.test_name} ran no tests" - case State.TIMEOUT: - return f"{self.test_name} timed out ({format_duration(self.duration)})" - case _: - raise ValueError("unknown result state: {state!r}") - - def has_meaningful_duration(self): - return State.has_meaningful_duration(self.state) - - def set_env_changed(self): - if self.state is None or self.state == State.PASSED: - self.state = State.ENV_CHANGED - - -# Minimum duration of a test to display its duration or to mention that -# the test is running in background -PROGRESS_MIN_TIME = 30.0 # seconds - -#If these test directories are encountered recurse into them and treat each -# test_ .py or dir as a separate test module. This can increase parallelism. -# Beware this can't generally be done for any directory with sub-tests as the -# __init__.py may do things which alter what tests are to be run. - -SPLITTESTDIRS = { - "test_asyncio", - "test_concurrent_futures", - "test_future_stmt", - "test_gdb", - "test_multiprocessing_fork", - "test_multiprocessing_forkserver", - "test_multiprocessing_spawn", -} - -# Storage of uncollectable objects -FOUND_GARBAGE = [] - - -def findtestdir(path=None): - return path or os.path.dirname(os.path.dirname(__file__)) or os.curdir - - -def findtests(*, testdir=None, exclude=(), - split_test_dirs=SPLITTESTDIRS, base_mod=""): - """Return a list of all applicable test modules.""" - testdir = findtestdir(testdir) - tests = [] - for name in os.listdir(testdir): - mod, ext = os.path.splitext(name) - if (not mod.startswith("test_")) or (mod in exclude): - continue - if mod in split_test_dirs: - subdir = os.path.join(testdir, mod) - mod = f"{base_mod or 'test'}.{mod}" - tests.extend(findtests(testdir=subdir, exclude=exclude, - split_test_dirs=split_test_dirs, base_mod=mod)) - elif ext in (".py", ""): - tests.append(f"{base_mod}.{mod}" if base_mod else mod) - return sorted(tests) - - -def split_test_packages(tests, *, testdir=None, exclude=(), - split_test_dirs=SPLITTESTDIRS): - testdir = findtestdir(testdir) - splitted = [] - for name in tests: - if name in split_test_dirs: - subdir = os.path.join(testdir, name) - splitted.extend(findtests(testdir=subdir, exclude=exclude, - split_test_dirs=split_test_dirs, - base_mod=name)) - else: - splitted.append(name) - return splitted - - -def get_abs_module(ns: Namespace, test_name: str) -> str: - if test_name.startswith('test.') or ns.testdir: - return test_name - else: - # Import it from the test package - return 'test.' + test_name - - -def _runtest_capture_output_timeout_junit(result: TestResult, ns: Namespace) -> None: - # Capture stdout and stderr, set faulthandler timeout, - # and create JUnit XML report. - - output_on_failure = ns.verbose3 - - use_timeout = ( - ns.timeout is not None and threading_helper.can_start_thread - ) - if use_timeout: - faulthandler.dump_traceback_later(ns.timeout, exit=True) - - try: - support.set_match_tests(ns.match_tests, ns.ignore_tests) - support.junit_xml_list = xml_list = [] if ns.xmlpath else None - if ns.failfast: - support.failfast = True - - if output_on_failure: - support.verbose = True - - stream = io.StringIO() - orig_stdout = sys.stdout - orig_stderr = sys.stderr - print_warning = support.print_warning - orig_print_warnings_stderr = print_warning.orig_stderr - - output = None - try: - sys.stdout = stream - sys.stderr = stream - # print_warning() writes into the temporary stream to preserve - # messages order. If support.environment_altered becomes true, - # warnings will be written to sys.stderr below. - print_warning.orig_stderr = stream - - _runtest_env_changed_exc(result, ns, display_failure=False) - # Ignore output if the test passed successfully - if result.state != State.PASSED: - output = stream.getvalue() - finally: - sys.stdout = orig_stdout - sys.stderr = orig_stderr - print_warning.orig_stderr = orig_print_warnings_stderr - - if output is not None: - sys.stderr.write(output) - sys.stderr.flush() - else: - # Tell tests to be moderately quiet - support.verbose = ns.verbose - - _runtest_env_changed_exc(result, ns, - display_failure=not ns.verbose) - - if xml_list: - import xml.etree.ElementTree as ET - result.xml_data = [ET.tostring(x).decode('us-ascii') - for x in xml_list] - finally: - if use_timeout: - faulthandler.cancel_dump_traceback_later() - support.junit_xml_list = None - - -def runtest(ns: Namespace, test_name: str) -> TestResult: - """Run a single test. - - ns -- regrtest namespace of options - test_name -- the name of the test - - Returns a TestResult. - - If ns.xmlpath is not None, xml_data is a list containing each - generated testsuite element. - """ - start_time = time.perf_counter() - result = TestResult(test_name) - try: - _runtest_capture_output_timeout_junit(result, ns) - except: - if not ns.pgo: - msg = traceback.format_exc() - print(f"test {test_name} crashed -- {msg}", - file=sys.stderr, flush=True) - result.state = State.UNCAUGHT_EXC - result.duration = time.perf_counter() - start_time - return result - - -def _test_module(the_module): - loader = unittest.TestLoader() - tests = loader.loadTestsFromModule(the_module) - for error in loader.errors: - print(error, file=sys.stderr) - if loader.errors: - raise Exception("errors while loading tests") - return support.run_unittest(tests) - - -def save_env(ns: Namespace, test_name: str): - return saved_test_environment(test_name, ns.verbose, ns.quiet, pgo=ns.pgo) - - -def regrtest_runner(result, test_func, ns) -> None: - # Run test_func(), collect statistics, and detect reference and memory - # leaks. - - if ns.huntrleaks: - from test.libregrtest.refleak import dash_R - refleak, test_result = dash_R(ns, result.test_name, test_func) - else: - test_result = test_func() - refleak = False - - if refleak: - result.state = State.REFLEAK - - match test_result: - case TestStats(): - stats = test_result - case unittest.TestResult(): - stats = TestStats.from_unittest(test_result) - case doctest.TestResults(): - stats = TestStats.from_doctest(test_result) - case None: - print_warning(f"{result.test_name} test runner returned None: {test_func}") - stats = None - case _: - print_warning(f"Unknown test result type: {type(test_result)}") - stats = None - - result.stats = stats - - -def _load_run_test(result: TestResult, ns: Namespace) -> None: - # Load the test function, run the test function. - - abstest = get_abs_module(ns, result.test_name) - - # remove the module from sys.module to reload it if it was already imported - try: - del sys.modules[abstest] - except KeyError: - pass - - the_module = importlib.import_module(abstest) - - if hasattr(the_module, "test_main"): - # https://github.com/python/cpython/issues/89392 - raise Exception(f"Module {result.test_name} defines test_main() which is no longer supported by regrtest") - test_func = functools.partial(_test_module, the_module) - - try: - with save_env(ns, result.test_name): - regrtest_runner(result, test_func, ns) - finally: - # First kill any dangling references to open files etc. - # This can also issue some ResourceWarnings which would otherwise get - # triggered during the following test run, and possibly produce - # failures. - support.gc_collect() - - cleanup_test_droppings(result.test_name, ns.verbose) - - if gc.garbage: - support.environment_altered = True - print_warning(f"{result.test_name} created {len(gc.garbage)} " - f"uncollectable object(s).") - - # move the uncollectable objects somewhere, - # so we don't see them again - FOUND_GARBAGE.extend(gc.garbage) - gc.garbage.clear() - - support.reap_children() - - -def _runtest_env_changed_exc(result: TestResult, ns: Namespace, - display_failure: bool = True) -> None: - # Detect environment changes, handle exceptions. - - # Reset the environment_altered flag to detect if a test altered - # the environment - support.environment_altered = False - - if ns.pgo: - display_failure = False - - test_name = result.test_name - try: - clear_caches() - support.gc_collect() - - with save_env(ns, test_name): - _load_run_test(result, ns) - except support.ResourceDenied as msg: - if not ns.quiet and not ns.pgo: - print(f"{test_name} skipped -- {msg}", flush=True) - result.state = State.RESOURCE_DENIED - return - except unittest.SkipTest as msg: - if not ns.quiet and not ns.pgo: - print(f"{test_name} skipped -- {msg}", flush=True) - result.state = State.SKIPPED - return - except support.TestFailedWithDetails as exc: - msg = f"test {test_name} failed" - if display_failure: - msg = f"{msg} -- {exc}" - print(msg, file=sys.stderr, flush=True) - result.state = State.FAILED - result.errors = exc.errors - result.failures = exc.failures - result.stats = exc.stats - return - except support.TestFailed as exc: - msg = f"test {test_name} failed" - if display_failure: - msg = f"{msg} -- {exc}" - print(msg, file=sys.stderr, flush=True) - result.state = State.FAILED - result.stats = exc.stats - return - except support.TestDidNotRun: - result.state = State.DID_NOT_RUN - return - except KeyboardInterrupt: - print() - result.state = State.INTERRUPTED - return - except: - if not ns.pgo: - msg = traceback.format_exc() - print(f"test {test_name} crashed -- {msg}", - file=sys.stderr, flush=True) - result.state = State.UNCAUGHT_EXC - return - - if support.environment_altered: - result.set_env_changed() - # Don't override the state if it was already set (REFLEAK or ENV_CHANGED) - if result.state is None: - result.state = State.PASSED - - -def cleanup_test_droppings(test_name: str, verbose: int) -> None: - # Try to clean up junk commonly left behind. While tests shouldn't leave - # any files or directories behind, when a test fails that can be tedious - # for it to arrange. The consequences can be especially nasty on Windows, - # since if a test leaves a file open, it cannot be deleted by name (while - # there's nothing we can do about that here either, we can display the - # name of the offending test, which is a real help). - for name in (os_helper.TESTFN,): - if not os.path.exists(name): - continue - - if os.path.isdir(name): - import shutil - kind, nuker = "directory", shutil.rmtree - elif os.path.isfile(name): - kind, nuker = "file", os.unlink - else: - raise RuntimeError(f"os.path says {name!r} exists but is neither " - f"directory nor file") - - if verbose: - print_warning(f"{test_name} left behind {kind} {name!r}") - support.environment_altered = True - - try: - import stat - # fix possible permissions problems that might prevent cleanup - os.chmod(name, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) - nuker(name) - except Exception as exc: - print_warning(f"{test_name} left behind {kind} {name!r} " - f"and it couldn't be removed: {exc}") diff --git a/Lib/test/libregrtest/runtest_mp.py b/Lib/test/libregrtest/runtest_mp.py deleted file mode 100644 index fb1f80b0c054e3..00000000000000 --- a/Lib/test/libregrtest/runtest_mp.py +++ /dev/null @@ -1,564 +0,0 @@ -import dataclasses -import faulthandler -import json -import os.path -import queue -import signal -import subprocess -import sys -import tempfile -import threading -import time -import traceback -from typing import NamedTuple, NoReturn, Literal, Any, TextIO - -from test import support -from test.support import os_helper -from test.support import TestStats - -from test.libregrtest.cmdline import Namespace -from test.libregrtest.main import Regrtest -from test.libregrtest.runtest import ( - runtest, TestResult, State, - PROGRESS_MIN_TIME) -from test.libregrtest.setup import setup_tests -from test.libregrtest.utils import format_duration, print_warning - -if sys.platform == 'win32': - import locale - - -# Display the running tests if nothing happened last N seconds -PROGRESS_UPDATE = 30.0 # seconds -assert PROGRESS_UPDATE >= PROGRESS_MIN_TIME - -# Kill the main process after 5 minutes. It is supposed to write an update -# every PROGRESS_UPDATE seconds. Tolerate 5 minutes for Python slowest -# buildbot workers. -MAIN_PROCESS_TIMEOUT = 5 * 60.0 -assert MAIN_PROCESS_TIMEOUT >= PROGRESS_UPDATE - -# Time to wait until a worker completes: should be immediate -JOIN_TIMEOUT = 30.0 # seconds - -USE_PROCESS_GROUP = (hasattr(os, "setsid") and hasattr(os, "killpg")) - - -def must_stop(result: TestResult, ns: Namespace) -> bool: - if result.state == State.INTERRUPTED: - return True - if ns.failfast and result.is_failed(ns.fail_env_changed): - return True - return False - - -def parse_worker_args(worker_args) -> tuple[Namespace, str]: - ns_dict, test_name = json.loads(worker_args) - ns = Namespace(**ns_dict) - return (ns, test_name) - - -def run_test_in_subprocess(testname: str, ns: Namespace, tmp_dir: str, stdout_fh: TextIO) -> subprocess.Popen: - ns_dict = vars(ns) - worker_args = (ns_dict, testname) - worker_args = json.dumps(worker_args) - if ns.python is not None: - executable = ns.python - else: - executable = [sys.executable] - cmd = [*executable, *support.args_from_interpreter_flags(), - '-u', # Unbuffered stdout and stderr - '-m', 'test.regrtest', - '--worker-args', worker_args] - - env = dict(os.environ) - if tmp_dir is not None: - env['TMPDIR'] = tmp_dir - env['TEMP'] = tmp_dir - env['TMP'] = tmp_dir - - # Running the child from the same working directory as regrtest's original - # invocation ensures that TEMPDIR for the child is the same when - # sysconfig.is_python_build() is true. See issue 15300. - kw = dict( - env=env, - stdout=stdout_fh, - # bpo-45410: Write stderr into stdout to keep messages order - stderr=stdout_fh, - text=True, - close_fds=(os.name != 'nt'), - cwd=os_helper.SAVEDCWD, - ) - if USE_PROCESS_GROUP: - kw['start_new_session'] = True - return subprocess.Popen(cmd, **kw) - - -def run_tests_worker(ns: Namespace, test_name: str) -> NoReturn: - setup_tests(ns) - - result = runtest(ns, test_name) - - print() # Force a newline (just in case) - - # Serialize TestResult as dict in JSON - print(json.dumps(result, cls=EncodeTestResult), flush=True) - sys.exit(0) - - -# We do not use a generator so multiple threads can call next(). -class MultiprocessIterator: - - """A thread-safe iterator over tests for multiprocess mode.""" - - def __init__(self, tests_iter): - self.lock = threading.Lock() - self.tests_iter = tests_iter - - def __iter__(self): - return self - - def __next__(self): - with self.lock: - if self.tests_iter is None: - raise StopIteration - return next(self.tests_iter) - - def stop(self): - with self.lock: - self.tests_iter = None - - -class MultiprocessResult(NamedTuple): - result: TestResult - # bpo-45410: stderr is written into stdout to keep messages order - worker_stdout: str | None = None - err_msg: str | None = None - - -ExcStr = str -QueueOutput = tuple[Literal[False], MultiprocessResult] | tuple[Literal[True], ExcStr] - - -class ExitThread(Exception): - pass - - -class TestWorkerProcess(threading.Thread): - def __init__(self, worker_id: int, runner: "MultiprocessTestRunner") -> None: - super().__init__() - self.worker_id = worker_id - self.pending = runner.pending - self.output = runner.output - self.ns = runner.ns - self.timeout = runner.worker_timeout - self.regrtest = runner.regrtest - self.current_test_name = None - self.start_time = None - self._popen = None - self._killed = False - self._stopped = False - - def __repr__(self) -> str: - info = [f'TestWorkerProcess #{self.worker_id}'] - if self.is_alive(): - info.append("running") - else: - info.append('stopped') - test = self.current_test_name - if test: - info.append(f'test={test}') - popen = self._popen - if popen is not None: - dt = time.monotonic() - self.start_time - info.extend((f'pid={self._popen.pid}', - f'time={format_duration(dt)}')) - return '<%s>' % ' '.join(info) - - def _kill(self) -> None: - popen = self._popen - if popen is None: - return - - if self._killed: - return - self._killed = True - - if USE_PROCESS_GROUP: - what = f"{self} process group" - else: - what = f"{self}" - - print(f"Kill {what}", file=sys.stderr, flush=True) - try: - if USE_PROCESS_GROUP: - os.killpg(popen.pid, signal.SIGKILL) - else: - popen.kill() - except ProcessLookupError: - # popen.kill(): the process completed, the TestWorkerProcess thread - # read its exit status, but Popen.send_signal() read the returncode - # just before Popen.wait() set returncode. - pass - except OSError as exc: - print_warning(f"Failed to kill {what}: {exc!r}") - - def stop(self) -> None: - # Method called from a different thread to stop this thread - self._stopped = True - self._kill() - - def mp_result_error( - self, - test_result: TestResult, - stdout: str | None = None, - err_msg=None - ) -> MultiprocessResult: - return MultiprocessResult(test_result, stdout, err_msg) - - def _run_process(self, test_name: str, tmp_dir: str, stdout_fh: TextIO) -> int: - self.current_test_name = test_name - try: - popen = run_test_in_subprocess(test_name, self.ns, tmp_dir, stdout_fh) - - self._killed = False - self._popen = popen - except: - self.current_test_name = None - raise - - try: - if self._stopped: - # If kill() has been called before self._popen is set, - # self._popen is still running. Call again kill() - # to ensure that the process is killed. - self._kill() - raise ExitThread - - try: - # gh-94026: stdout+stderr are written to tempfile - retcode = popen.wait(timeout=self.timeout) - assert retcode is not None - return retcode - except subprocess.TimeoutExpired: - if self._stopped: - # kill() has been called: communicate() fails on reading - # closed stdout - raise ExitThread - - # On timeout, kill the process - self._kill() - - # None means TIMEOUT for the caller - retcode = None - # bpo-38207: Don't attempt to call communicate() again: on it - # can hang until all child processes using stdout - # pipes completes. - except OSError: - if self._stopped: - # kill() has been called: communicate() fails - # on reading closed stdout - raise ExitThread - raise - except: - self._kill() - raise - finally: - self._wait_completed() - self._popen = None - self.current_test_name = None - - def _runtest(self, test_name: str) -> MultiprocessResult: - if sys.platform == 'win32': - # gh-95027: When stdout is not a TTY, Python uses the ANSI code - # page for the sys.stdout encoding. If the main process runs in a - # terminal, sys.stdout uses WindowsConsoleIO with UTF-8 encoding. - encoding = locale.getencoding() - else: - encoding = sys.stdout.encoding - - # gh-94026: Write stdout+stderr to a tempfile as workaround for - # non-blocking pipes on Emscripten with NodeJS. - with tempfile.TemporaryFile('w+', encoding=encoding) as stdout_fh: - # gh-93353: Check for leaked temporary files in the parent process, - # since the deletion of temporary files can happen late during - # Python finalization: too late for libregrtest. - if not support.is_wasi: - # Don't check for leaked temporary files and directories if Python is - # run on WASI. WASI don't pass environment variables like TMPDIR to - # worker processes. - tmp_dir = tempfile.mkdtemp(prefix="test_python_") - tmp_dir = os.path.abspath(tmp_dir) - try: - retcode = self._run_process(test_name, tmp_dir, stdout_fh) - finally: - tmp_files = os.listdir(tmp_dir) - os_helper.rmtree(tmp_dir) - else: - retcode = self._run_process(test_name, None, stdout_fh) - tmp_files = () - stdout_fh.seek(0) - - try: - stdout = stdout_fh.read().strip() - except Exception as exc: - # gh-101634: Catch UnicodeDecodeError if stdout cannot be - # decoded from encoding - err_msg = f"Cannot read process stdout: {exc}" - result = TestResult(test_name, state=State.MULTIPROCESSING_ERROR) - return self.mp_result_error(result, err_msg=err_msg) - - if retcode is None: - result = TestResult(test_name, state=State.TIMEOUT) - return self.mp_result_error(result, stdout) - - err_msg = None - if retcode != 0: - err_msg = "Exit code %s" % retcode - else: - stdout, _, worker_json = stdout.rpartition("\n") - stdout = stdout.rstrip() - if not worker_json: - err_msg = "Failed to parse worker stdout" - else: - try: - # deserialize run_tests_worker() output - result = json.loads(worker_json, - object_hook=decode_test_result) - except Exception as exc: - err_msg = "Failed to parse worker JSON: %s" % exc - - if err_msg: - result = TestResult(test_name, state=State.MULTIPROCESSING_ERROR) - return self.mp_result_error(result, stdout, err_msg) - - if tmp_files: - msg = (f'\n\n' - f'Warning -- {test_name} leaked temporary files ' - f'({len(tmp_files)}): {", ".join(sorted(tmp_files))}') - stdout += msg - result.set_env_changed() - - return MultiprocessResult(result, stdout) - - def run(self) -> None: - while not self._stopped: - try: - try: - test_name = next(self.pending) - except StopIteration: - break - - self.start_time = time.monotonic() - mp_result = self._runtest(test_name) - mp_result.result.duration = time.monotonic() - self.start_time - self.output.put((False, mp_result)) - - if must_stop(mp_result.result, self.ns): - break - except ExitThread: - break - except BaseException: - self.output.put((True, traceback.format_exc())) - break - - def _wait_completed(self) -> None: - popen = self._popen - - try: - popen.wait(JOIN_TIMEOUT) - except (subprocess.TimeoutExpired, OSError) as exc: - print_warning(f"Failed to wait for {self} completion " - f"(timeout={format_duration(JOIN_TIMEOUT)}): " - f"{exc!r}") - - def wait_stopped(self, start_time: float) -> None: - # bpo-38207: MultiprocessTestRunner.stop_workers() called self.stop() - # which killed the process. Sometimes, killing the process from the - # main thread does not interrupt popen.communicate() in - # TestWorkerProcess thread. This loop with a timeout is a workaround - # for that. - # - # Moreover, if this method fails to join the thread, it is likely - # that Python will hang at exit while calling threading._shutdown() - # which tries again to join the blocked thread. Regrtest.main() - # uses EXIT_TIMEOUT to workaround this second bug. - while True: - # Write a message every second - self.join(1.0) - if not self.is_alive(): - break - dt = time.monotonic() - start_time - self.regrtest.log(f"Waiting for {self} thread " - f"for {format_duration(dt)}") - if dt > JOIN_TIMEOUT: - print_warning(f"Failed to join {self} in {format_duration(dt)}") - break - - -def get_running(workers: list[TestWorkerProcess]) -> list[TestWorkerProcess]: - running = [] - for worker in workers: - current_test_name = worker.current_test_name - if not current_test_name: - continue - dt = time.monotonic() - worker.start_time - if dt >= PROGRESS_MIN_TIME: - text = '%s (%s)' % (current_test_name, format_duration(dt)) - running.append(text) - return running - - -class MultiprocessTestRunner: - def __init__(self, regrtest: Regrtest) -> None: - self.regrtest = regrtest - self.log = self.regrtest.log - self.ns = regrtest.ns - self.output: queue.Queue[QueueOutput] = queue.Queue() - self.pending = MultiprocessIterator(self.regrtest.tests) - if self.ns.timeout is not None: - # Rely on faulthandler to kill a worker process. This timouet is - # when faulthandler fails to kill a worker process. Give a maximum - # of 5 minutes to faulthandler to kill the worker. - self.worker_timeout = min(self.ns.timeout * 1.5, - self.ns.timeout + 5 * 60) - else: - self.worker_timeout = None - self.workers = None - - def start_workers(self) -> None: - self.workers = [TestWorkerProcess(index, self) - for index in range(1, self.ns.use_mp + 1)] - msg = f"Run tests in parallel using {len(self.workers)} child processes" - if self.ns.timeout: - msg += (" (timeout: %s, worker timeout: %s)" - % (format_duration(self.ns.timeout), - format_duration(self.worker_timeout))) - self.log(msg) - for worker in self.workers: - worker.start() - - def stop_workers(self) -> None: - start_time = time.monotonic() - for worker in self.workers: - worker.stop() - for worker in self.workers: - worker.wait_stopped(start_time) - - def _get_result(self) -> QueueOutput | None: - use_faulthandler = (self.ns.timeout is not None) - timeout = PROGRESS_UPDATE - - # bpo-46205: check the status of workers every iteration to avoid - # waiting forever on an empty queue. - while any(worker.is_alive() for worker in self.workers): - if use_faulthandler: - faulthandler.dump_traceback_later(MAIN_PROCESS_TIMEOUT, - exit=True) - - # wait for a thread - try: - return self.output.get(timeout=timeout) - except queue.Empty: - pass - - # display progress - running = get_running(self.workers) - if running and not self.ns.pgo: - self.log('running: %s' % ', '.join(running)) - - # all worker threads are done: consume pending results - try: - return self.output.get(timeout=0) - except queue.Empty: - return None - - def display_result(self, mp_result: MultiprocessResult) -> None: - result = mp_result.result - - text = str(result) - if mp_result.err_msg: - # MULTIPROCESSING_ERROR - text += ' (%s)' % mp_result.err_msg - elif (result.duration >= PROGRESS_MIN_TIME and not self.ns.pgo): - text += ' (%s)' % format_duration(result.duration) - running = get_running(self.workers) - if running and not self.ns.pgo: - text += ' -- running: %s' % ', '.join(running) - self.regrtest.display_progress(self.test_index, text) - - def _process_result(self, item: QueueOutput) -> bool: - """Returns True if test runner must stop.""" - if item[0]: - # Thread got an exception - format_exc = item[1] - print_warning(f"regrtest worker thread failed: {format_exc}") - result = TestResult("", state=State.MULTIPROCESSING_ERROR) - self.regrtest.accumulate_result(result) - return True - - self.test_index += 1 - mp_result = item[1] - self.regrtest.accumulate_result(mp_result.result) - self.display_result(mp_result) - - if mp_result.worker_stdout: - print(mp_result.worker_stdout, flush=True) - - if must_stop(mp_result.result, self.ns): - return True - - return False - - def run_tests(self) -> None: - self.start_workers() - - self.test_index = 0 - try: - while True: - item = self._get_result() - if item is None: - break - - stop = self._process_result(item) - if stop: - break - except KeyboardInterrupt: - print() - self.regrtest.interrupted = True - finally: - if self.ns.timeout is not None: - faulthandler.cancel_dump_traceback_later() - - # Always ensure that all worker processes are no longer - # worker when we exit this function - self.pending.stop() - self.stop_workers() - - -def run_tests_multiprocess(regrtest: Regrtest) -> None: - MultiprocessTestRunner(regrtest).run_tests() - - -class EncodeTestResult(json.JSONEncoder): - """Encode a TestResult (sub)class object into a JSON dict.""" - - def default(self, o: Any) -> dict[str, Any]: - if isinstance(o, TestResult): - result = dataclasses.asdict(o) - result["__test_result__"] = o.__class__.__name__ - return result - - return super().default(o) - - -def decode_test_result(d: dict[str, Any]) -> TestResult | TestStats | dict[str, Any]: - """Decode a TestResult (sub)class object from a JSON dict.""" - - if "__test_result__" not in d: - return d - - d.pop('__test_result__') - if d['stats'] is not None: - d['stats'] = TestStats(**d['stats']) - return TestResult(**d) diff --git a/Lib/test/libregrtest/runtests.py b/Lib/test/libregrtest/runtests.py new file mode 100644 index 00000000000000..4da312db4cb02e --- /dev/null +++ b/Lib/test/libregrtest/runtests.py @@ -0,0 +1,162 @@ +import contextlib +import dataclasses +import json +import os +import subprocess +from typing import Any + +from test import support + +from .utils import ( + StrPath, StrJSON, TestTuple, FilterTuple, FilterDict) + + +class JsonFileType: + UNIX_FD = "UNIX_FD" + WINDOWS_HANDLE = "WINDOWS_HANDLE" + STDOUT = "STDOUT" + + +@dataclasses.dataclass(slots=True, frozen=True) +class JsonFile: + # file type depends on file_type: + # - UNIX_FD: file descriptor (int) + # - WINDOWS_HANDLE: handle (int) + # - STDOUT: use process stdout (None) + file: int | None + file_type: str + + def configure_subprocess(self, popen_kwargs: dict) -> None: + match self.file_type: + case JsonFileType.UNIX_FD: + # Unix file descriptor + popen_kwargs['pass_fds'] = [self.file] + case JsonFileType.WINDOWS_HANDLE: + # Windows handle + startupinfo = subprocess.STARTUPINFO() + startupinfo.lpAttributeList = {"handle_list": [self.file]} + popen_kwargs['startupinfo'] = startupinfo + + @contextlib.contextmanager + def inherit_subprocess(self): + if self.file_type == JsonFileType.WINDOWS_HANDLE: + os.set_handle_inheritable(self.file, True) + try: + yield + finally: + os.set_handle_inheritable(self.file, False) + else: + yield + + def open(self, mode='r', *, encoding): + if self.file_type == JsonFileType.STDOUT: + raise ValueError("for STDOUT file type, just use sys.stdout") + + file = self.file + if self.file_type == JsonFileType.WINDOWS_HANDLE: + import msvcrt + # Create a file descriptor from the handle + file = msvcrt.open_osfhandle(file, os.O_WRONLY) + return open(file, mode, encoding=encoding) + + +@dataclasses.dataclass(slots=True, frozen=True) +class HuntRefleak: + warmups: int + runs: int + filename: StrPath + + +@dataclasses.dataclass(slots=True, frozen=True) +class RunTests: + tests: TestTuple + fail_fast: bool + fail_env_changed: bool + match_tests: FilterTuple | None + ignore_tests: FilterTuple | None + match_tests_dict: FilterDict | None + rerun: bool + forever: bool + pgo: bool + pgo_extended: bool + output_on_failure: bool + timeout: float | None + verbose: int + quiet: bool + hunt_refleak: HuntRefleak | None + test_dir: StrPath | None + use_junit: bool + memory_limit: str | None + gc_threshold: int | None + use_resources: tuple[str, ...] + python_cmd: tuple[str, ...] | None + randomize: bool + random_seed: int | None + json_file: JsonFile | None + + def copy(self, **override): + state = dataclasses.asdict(self) + state.update(override) + return RunTests(**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) + else: + return None + + def get_jobs(self): + # Number of run_single_test() calls needed to run all tests. + # None means that there is not bound limit (--forever option). + if self.forever: + return None + return len(self.tests) + + def iter_tests(self): + if self.forever: + while True: + yield from self.tests + 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: + # + # - If --python command line option is used; + # - On Emscripten and WASI. + # + # On other platforms, UNIX_FD or WINDOWS_HANDLE can be used. + return ( + bool(self.python_cmd) + or support.is_emscripten + or support.is_wasi + ) + + +class _EncodeRunTests(json.JSONEncoder): + def default(self, o: Any) -> dict[str, Any]: + if isinstance(o, RunTests): + result = dataclasses.asdict(o) + result["__runtests__"] = True + return result + else: + return super().default(o) + + +def _decode_runtests(data: dict[str, Any]) -> RunTests | dict[str, Any]: + if "__runtests__" in data: + data.pop('__runtests__') + if data['hunt_refleak']: + data['hunt_refleak'] = HuntRefleak(**data['hunt_refleak']) + if data['json_file']: + data['json_file'] = JsonFile(**data['json_file']) + return RunTests(**data) + else: + return data diff --git a/Lib/test/libregrtest/save_env.py b/Lib/test/libregrtest/save_env.py index 7e801a591325e1..b2cc381344b2ef 100644 --- a/Lib/test/libregrtest/save_env.py +++ b/Lib/test/libregrtest/save_env.py @@ -3,9 +3,11 @@ import os import sys import threading + from test import support from test.support import os_helper -from test.libregrtest.utils import print_warning + +from .utils import print_warning class SkipTestEnvironment(Exception): @@ -34,7 +36,7 @@ class saved_test_environment: items is also printed. """ - def __init__(self, test_name, verbose=0, quiet=False, *, pgo=False): + def __init__(self, test_name, verbose, quiet, *, pgo): self.test_name = test_name self.verbose = verbose self.quiet = quiet @@ -161,11 +163,11 @@ def restore_warnings_filters(self, saved_filters): warnings.filters[:] = saved_filters[2] def get_asyncore_socket_map(self): - asyncore = sys.modules.get('asyncore') + asyncore = sys.modules.get('test.support.asyncore') # XXX Making a copy keeps objects alive until __exit__ gets called. return asyncore and asyncore.socket_map.copy() or {} def restore_asyncore_socket_map(self, saved_map): - asyncore = sys.modules.get('asyncore') + asyncore = sys.modules.get('test.support.asyncore') if asyncore is not None: asyncore.close_all(ignore_all=True) asyncore.socket_map.update(saved_map) @@ -257,8 +259,10 @@ def restore_sysconfig__INSTALL_SCHEMES(self, saved): sysconfig._INSTALL_SCHEMES.update(saved[2]) def get_files(self): + # XXX: Maybe add an allow-list here? return sorted(fn + ('/' if os.path.isdir(fn) else '') - for fn in os.listdir()) + for fn in os.listdir() + if not fn.startswith(".hypothesis")) def restore_files(self, saved_value): fn = os_helper.TESTFN if fn not in saved_value and (fn + '/') not in saved_value: diff --git a/Lib/test/libregrtest/setup.py b/Lib/test/libregrtest/setup.py index ecd7fa33107311..793347f60ad93c 100644 --- a/Lib/test/libregrtest/setup.py +++ b/Lib/test/libregrtest/setup.py @@ -1,24 +1,32 @@ -import atexit import faulthandler +import gc import os +import random import signal import sys import unittest from test import support from test.support.os_helper import TESTFN_UNDECODABLE, FS_NONASCII -try: - import gc -except ImportError: - gc = None -from test.libregrtest.utils import (setup_unraisable_hook, - setup_threading_excepthook) +from .runtests import RunTests +from .utils import ( + setup_unraisable_hook, setup_threading_excepthook, fix_umask, + adjust_rlimit_nofile) UNICODE_GUARD_ENV = "PYTHONREGRTEST_UNICODE_GUARD" -def setup_tests(ns): +def setup_test_dir(testdir: str | None) -> None: + if testdir: + # Prepend test directory to sys.path, so runtest() will be able + # to locate tests + sys.path.insert(0, os.path.abspath(testdir)) + + +def setup_process(): + fix_umask() + try: stderr_fd = sys.__stderr__.fileno() except (ValueError, AttributeError): @@ -40,14 +48,9 @@ def setup_tests(ns): for signum in signals: faulthandler.register(signum, chain=True, file=stderr_fd) - _adjust_resource_limits() - replace_stdout() - support.record_original_stdout(sys.stdout) + adjust_rlimit_nofile() - if ns.testdir: - # Prepend test directory to sys.path, so runtest() will be able - # to locate tests - sys.path.insert(0, os.path.abspath(ns.testdir)) + support.record_original_stdout(sys.stdout) # Some times __path__ and __file__ are not absolute (e.g. while running from # Lib/) and, if we change the CWD to run the tests in a temporary dir, some @@ -66,19 +69,6 @@ def setup_tests(ns): if getattr(module, '__file__', None): module.__file__ = os.path.abspath(module.__file__) - if ns.huntrleaks: - unittest.BaseTestSuite._cleanup = False - - if ns.memlimit is not None: - support.set_memlimit(ns.memlimit) - - if ns.threshold is not None: - gc.set_threshold(ns.threshold) - - support.suppress_msvcrt_asserts(ns.verbose and ns.verbose >= 2) - - support.use_resources = ns.use_resources - if hasattr(sys, 'addaudithook'): # Add an auditing hook for all tests to ensure PySys_Audit is tested def _test_audit_hook(name, args): @@ -88,7 +78,37 @@ def _test_audit_hook(name, args): setup_unraisable_hook() setup_threading_excepthook() - timeout = ns.timeout + # Ensure there's a non-ASCII character in env vars at all times to force + # tests consider this case. See BPO-44647 for details. + if TESTFN_UNDECODABLE and os.supports_bytes_environ: + os.environb.setdefault(UNICODE_GUARD_ENV.encode(), TESTFN_UNDECODABLE) + elif FS_NONASCII: + os.environ.setdefault(UNICODE_GUARD_ENV, FS_NONASCII) + + +def setup_tests(runtests: RunTests): + support.verbose = runtests.verbose + support.failfast = runtests.fail_fast + support.PGO = runtests.pgo + support.PGO_EXTENDED = runtests.pgo_extended + + support.set_match_tests(runtests.match_tests, runtests.ignore_tests) + + if runtests.use_junit: + support.junit_xml_list = [] + from test.support.testresult import RegressionTestResult + RegressionTestResult.USE_XML = True + else: + support.junit_xml_list = None + + if runtests.memory_limit is not None: + support.set_memlimit(runtests.memory_limit) + + support.suppress_msvcrt_asserts(runtests.verbose >= 2) + + support.use_resources = runtests.use_resources + + timeout = runtests.timeout if timeout is not None: # For a slow buildbot worker, increase SHORT_TIMEOUT and LONG_TIMEOUT support.LOOPBACK_TIMEOUT = max(support.LOOPBACK_TIMEOUT, timeout / 120) @@ -102,61 +122,10 @@ def _test_audit_hook(name, args): support.SHORT_TIMEOUT = min(support.SHORT_TIMEOUT, timeout) support.LONG_TIMEOUT = min(support.LONG_TIMEOUT, timeout) - if ns.xmlpath: - from test.support.testresult import RegressionTestResult - RegressionTestResult.USE_XML = True - - # Ensure there's a non-ASCII character in env vars at all times to force - # tests consider this case. See BPO-44647 for details. - if TESTFN_UNDECODABLE and os.supports_bytes_environ: - os.environb.setdefault(UNICODE_GUARD_ENV.encode(), TESTFN_UNDECODABLE) - elif FS_NONASCII: - os.environ.setdefault(UNICODE_GUARD_ENV, FS_NONASCII) - - -def replace_stdout(): - """Set stdout encoder error handler to backslashreplace (as stderr error - handler) to avoid UnicodeEncodeError when printing a traceback""" - stdout = sys.stdout - try: - fd = stdout.fileno() - except ValueError: - # On IDLE, sys.stdout has no file descriptor and is not a TextIOWrapper - # object. Leaving sys.stdout unchanged. - # - # Catch ValueError to catch io.UnsupportedOperation on TextIOBase - # and ValueError on a closed stream. - return - - sys.stdout = open(fd, 'w', - encoding=stdout.encoding, - errors="backslashreplace", - closefd=False, - newline='\n') - - def restore_stdout(): - sys.stdout.close() - sys.stdout = stdout - atexit.register(restore_stdout) + if runtests.hunt_refleak: + unittest.BaseTestSuite._cleanup = False + if runtests.gc_threshold is not None: + gc.set_threshold(runtests.gc_threshold) -def _adjust_resource_limits(): - """Adjust the system resource limits (ulimit) if needed.""" - try: - import resource - from resource import RLIMIT_NOFILE, RLIM_INFINITY - except ImportError: - return - fd_limit, max_fds = resource.getrlimit(RLIMIT_NOFILE) - # On macOS the default fd limit is sometimes too low (256) for our - # test suite to succeed. Raise it to something more reasonable. - # 1024 is a common Linux default. - desired_fds = 1024 - if fd_limit < desired_fds and fd_limit < max_fds: - new_fd_limit = min(desired_fds, max_fds) - try: - resource.setrlimit(RLIMIT_NOFILE, (new_fd_limit, max_fds)) - print(f"Raised RLIMIT_NOFILE: {fd_limit} -> {new_fd_limit}") - except (ValueError, OSError) as err: - print(f"Unable to raise RLIMIT_NOFILE from {fd_limit} to " - f"{new_fd_limit}: {err}.") + random.seed(runtests.random_seed) diff --git a/Lib/test/libregrtest/single.py b/Lib/test/libregrtest/single.py new file mode 100644 index 00000000000000..0304f858edf42c --- /dev/null +++ b/Lib/test/libregrtest/single.py @@ -0,0 +1,278 @@ +import doctest +import faulthandler +import gc +import importlib +import io +import sys +import time +import traceback +import unittest + +from test import support +from test.support import TestStats +from test.support import threading_helper + +from .result import State, TestResult +from .runtests import RunTests +from .save_env import saved_test_environment +from .setup import setup_tests +from .utils import ( + TestName, + clear_caches, remove_testfn, abs_module_name, print_warning) + + +# Minimum duration of a test to display its duration or to mention that +# the test is running in background +PROGRESS_MIN_TIME = 30.0 # seconds + + +def run_unittest(test_mod): + loader = unittest.TestLoader() + tests = loader.loadTestsFromModule(test_mod) + for error in loader.errors: + print(error, file=sys.stderr) + if loader.errors: + raise Exception("errors while loading tests") + return support.run_unittest(tests) + + +def regrtest_runner(result: TestResult, test_func, runtests: RunTests) -> None: + # Run test_func(), collect statistics, and detect reference and memory + # leaks. + if runtests.hunt_refleak: + from .refleak import runtest_refleak + refleak, test_result = runtest_refleak(result.test_name, test_func, + runtests.hunt_refleak, + runtests.quiet) + else: + test_result = test_func() + refleak = False + + if refleak: + result.state = State.REFLEAK + + stats: TestStats | None + + match test_result: + case TestStats(): + stats = test_result + case unittest.TestResult(): + stats = TestStats.from_unittest(test_result) + case doctest.TestResults(): + stats = TestStats.from_doctest(test_result) + case None: + print_warning(f"{result.test_name} test runner returned None: {test_func}") + stats = None + case _: + print_warning(f"Unknown test result type: {type(test_result)}") + stats = None + + result.stats = stats + + +# Storage of uncollectable GC objects (gc.garbage) +GC_GARBAGE = [] + + +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"): + # https://github.com/python/cpython/issues/89392 + raise Exception(f"Module {test_name} defines test_main() which " + f"is no longer supported by regrtest") + def test_func(): + return run_unittest(test_mod) + + try: + regrtest_runner(result, test_func, runtests) + finally: + # First kill any dangling references to open files etc. + # This can also issue some ResourceWarnings which would otherwise get + # triggered during the following test run, and possibly produce + # failures. + support.gc_collect() + + remove_testfn(test_name, runtests.verbose) + + if gc.garbage: + support.environment_altered = True + print_warning(f"{test_name} created {len(gc.garbage)} " + f"uncollectable object(s)") + + # move the uncollectable objects somewhere, + # so we don't see them again + GC_GARBAGE.extend(gc.garbage) + gc.garbage.clear() + + support.reap_children() + + +def _runtest_env_changed_exc(result: TestResult, runtests: RunTests, + display_failure: bool = True) -> None: + # Handle exceptions, detect environment changes. + + # Reset the environment_altered flag to detect if a test altered + # the environment + support.environment_altered = False + + pgo = runtests.pgo + if pgo: + display_failure = False + quiet = runtests.quiet + + test_name = result.test_name + try: + clear_caches() + support.gc_collect() + + with saved_test_environment(test_name, + runtests.verbose, quiet, pgo=pgo): + _load_run_test(result, runtests) + except support.ResourceDenied as exc: + if not quiet and not pgo: + print(f"{test_name} skipped -- {exc}", flush=True) + result.state = State.RESOURCE_DENIED + return + except unittest.SkipTest as exc: + if not quiet and not pgo: + print(f"{test_name} skipped -- {exc}", flush=True) + result.state = State.SKIPPED + return + except support.TestFailedWithDetails as exc: + msg = f"test {test_name} failed" + if display_failure: + msg = f"{msg} -- {exc}" + print(msg, file=sys.stderr, flush=True) + result.state = State.FAILED + result.errors = exc.errors + result.failures = exc.failures + result.stats = exc.stats + return + except support.TestFailed as exc: + msg = f"test {test_name} failed" + if display_failure: + msg = f"{msg} -- {exc}" + print(msg, file=sys.stderr, flush=True) + result.state = State.FAILED + result.stats = exc.stats + return + except support.TestDidNotRun: + result.state = State.DID_NOT_RUN + return + except KeyboardInterrupt: + print() + result.state = State.INTERRUPTED + return + except: + if not pgo: + msg = traceback.format_exc() + print(f"test {test_name} crashed -- {msg}", + file=sys.stderr, flush=True) + result.state = State.UNCAUGHT_EXC + return + + if support.environment_altered: + result.set_env_changed() + # Don't override the state if it was already set (REFLEAK or ENV_CHANGED) + if result.state is None: + result.state = State.PASSED + + +def _runtest(result: TestResult, runtests: RunTests) -> None: + # Capture stdout and stderr, set faulthandler timeout, + # and create JUnit XML report. + verbose = runtests.verbose + 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: + faulthandler.dump_traceback_later(timeout, exit=True) + + try: + setup_tests(runtests) + + if output_on_failure: + support.verbose = True + + stream = io.StringIO() + orig_stdout = sys.stdout + orig_stderr = sys.stderr + print_warning = support.print_warning + orig_print_warnings_stderr = print_warning.orig_stderr + + output = None + try: + sys.stdout = stream + sys.stderr = stream + # print_warning() writes into the temporary stream to preserve + # messages order. If support.environment_altered becomes true, + # warnings will be written to sys.stderr below. + print_warning.orig_stderr = stream + + _runtest_env_changed_exc(result, runtests, display_failure=False) + # Ignore output if the test passed successfully + if result.state != State.PASSED: + output = stream.getvalue() + finally: + sys.stdout = orig_stdout + sys.stderr = orig_stderr + print_warning.orig_stderr = orig_print_warnings_stderr + + if output is not None: + sys.stderr.write(output) + sys.stderr.flush() + else: + # Tell tests to be moderately quiet + support.verbose = verbose + _runtest_env_changed_exc(result, runtests, + display_failure=not verbose) + + xml_list = support.junit_xml_list + if xml_list: + import xml.etree.ElementTree as ET + result.xml_data = [ET.tostring(x).decode('us-ascii') + for x in xml_list] + finally: + if use_timeout: + faulthandler.cancel_dump_traceback_later() + support.junit_xml_list = None + + +def run_single_test(test_name: TestName, runtests: RunTests) -> TestResult: + """Run a single test. + + test_name -- the name of the test + + Returns a TestResult. + + If runtests.use_junit, xml_data is a list containing each generated + testsuite element. + """ + start_time = time.perf_counter() + result = TestResult(test_name) + pgo = runtests.pgo + try: + _runtest(result, runtests) + except: + if not pgo: + msg = traceback.format_exc() + print(f"test {test_name} crashed -- {msg}", + file=sys.stderr, flush=True) + result.state = State.UNCAUGHT_EXC + + sys.stdout.flush() + sys.stderr.flush() + + result.duration = time.perf_counter() - start_time + return result diff --git a/Lib/test/libregrtest/utils.py b/Lib/test/libregrtest/utils.py index 49f3a236f3d14b..b5bbe0ef6596a7 100644 --- a/Lib/test/libregrtest/utils.py +++ b/Lib/test/libregrtest/utils.py @@ -1,9 +1,59 @@ +import contextlib +import faulthandler +import locale import math import os.path +import platform +import random +import shlex +import signal +import subprocess import sys import sysconfig +import tempfile import textwrap +from collections.abc import Callable + from test import support +from test.support import os_helper +from test.support import threading_helper + + +# All temporary files and temporary directories created by libregrtest should +# use TMP_PREFIX so cleanup_temp_dir() can remove them all. +TMP_PREFIX = 'test_python_' +WORK_DIR_PREFIX = TMP_PREFIX +WORKER_WORK_DIR_PREFIX = WORK_DIR_PREFIX + 'worker_' + +# bpo-38203: Maximum delay in seconds to exit Python (call Py_Finalize()). +# Used to protect against threading._shutdown() hang. +# Must be smaller than buildbot "1200 seconds without output" limit. +EXIT_TIMEOUT = 120.0 + + +ALL_RESOURCES = ('audio', 'curses', 'largefile', 'network', + 'decimal', 'cpu', 'subprocess', 'urlfetch', 'gui', 'walltime') + +# Other resources excluded from --use=all: +# +# - extralagefile (ex: test_zipfile64): really too slow to be enabled +# "by default" +# - tzdata: while needed to validate fully test_datetime, it makes +# test_datetime too slow (15-20 min on some buildbots) and so is disabled by +# default (see bpo-30822). +RESOURCE_NAMES = ALL_RESOURCES + ('extralargefile', 'tzdata') + + +# Types for types hints +StrPath = str +TestName = str +StrJSON = str +TestTuple = tuple[TestName, ...] +TestList = list[TestName] +# --match and --ignore options: list of patterns +# ('*' joker character can be used) +FilterTuple = tuple[TestName, ...] +FilterDict = dict[TestName, FilterTuple] def format_duration(seconds): @@ -31,7 +81,7 @@ def format_duration(seconds): return ' '.join(parts) -def removepy(names): +def strip_py_suffix(names: list[str] | None) -> None: if not names: return for idx, name in enumerate(names): @@ -40,11 +90,20 @@ def removepy(names): names[idx] = basename +def plural(n, singular, plural=None): + if n == 1: + return singular + elif plural is not None: + return plural + else: + return singular + 's' + + def count(n, word): if n == 1: - return "%d %s" % (n, word) + return f"{n} {word}" else: - return "%d %ss" % (n, word) + return f"{n} {word}s" def printlist(x, width=70, indent=4, file=None): @@ -125,15 +184,6 @@ def clear_caches(): if stream is not None: stream.flush() - # Clear assorted module caches. - # Don't worry about resetting the cache if the module is not loaded - try: - distutils_dir_util = sys.modules['distutils.dir_util'] - except KeyError: - pass - else: - distutils_dir_util._path_created.clear() - try: re = sys.modules['re'] except KeyError: @@ -212,6 +262,13 @@ def clear_caches(): for f in typing._cleanups: f() + try: + fractions = sys.modules['fractions'] + except KeyError: + pass + else: + fractions._hash_algorithm.cache_clear() + def get_build_info(): # Get most important configure and build options as a list of strings. @@ -292,3 +349,331 @@ def get_build_info(): build.append("dtrace") return build + + +def get_temp_dir(tmp_dir: StrPath | None = None) -> StrPath: + if tmp_dir: + tmp_dir = os.path.expanduser(tmp_dir) + else: + # When tests are run from the Python build directory, it is best practice + # to keep the test files in a subfolder. This eases the cleanup of leftover + # files using the "make distclean" command. + if sysconfig.is_python_build(): + if not support.is_wasi: + tmp_dir = sysconfig.get_config_var('abs_builddir') + if tmp_dir is None: + tmp_dir = sysconfig.get_config_var('abs_srcdir') + if not tmp_dir: + # gh-74470: On Windows, only srcdir is available. Using + # abs_builddir mostly matters on UNIX when building + # Python out of the source tree, especially when the + # source tree is read only. + tmp_dir = sysconfig.get_config_var('srcdir') + tmp_dir = os.path.join(tmp_dir, 'build') + else: + # WASI platform + tmp_dir = sysconfig.get_config_var('projectbase') + tmp_dir = os.path.join(tmp_dir, 'build') + + # When get_temp_dir() is called in a worker process, + # get_temp_dir() path is different than in the parent process + # which is not a WASI process. So the parent does not create + # the same "tmp_dir" than the test worker process. + os.makedirs(tmp_dir, exist_ok=True) + else: + tmp_dir = tempfile.gettempdir() + + return os.path.abspath(tmp_dir) + + +def fix_umask(): + if support.is_emscripten: + # Emscripten has default umask 0o777, which breaks some tests. + # see https://github.com/emscripten-core/emscripten/issues/17269 + old_mask = os.umask(0) + if old_mask == 0o777: + os.umask(0o027) + else: + os.umask(old_mask) + + +def get_work_dir(parent_dir: StrPath, worker: bool = False) -> StrPath: + # Define a writable temp dir that will be used as cwd while running + # the tests. The name of the dir includes the pid to allow parallel + # testing (see the -j option). + # Emscripten and WASI have stubbed getpid(), Emscripten has only + # milisecond clock resolution. Use randint() instead. + if support.is_emscripten or support.is_wasi: + nounce = random.randint(0, 1_000_000) + else: + nounce = os.getpid() + + if worker: + work_dir = WORK_DIR_PREFIX + str(nounce) + else: + work_dir = WORKER_WORK_DIR_PREFIX + str(nounce) + work_dir += os_helper.FS_NONASCII + work_dir = os.path.join(parent_dir, work_dir) + return work_dir + + +@contextlib.contextmanager +def exit_timeout(): + try: + yield + except SystemExit as exc: + # bpo-38203: Python can hang at exit in Py_Finalize(), especially + # on threading._shutdown() call: put a timeout + if threading_helper.can_start_thread: + faulthandler.dump_traceback_later(EXIT_TIMEOUT, exit=True) + sys.exit(exc.code) + + +def remove_testfn(test_name: TestName, verbose: int) -> None: + # Try to clean up os_helper.TESTFN if left behind. + # + # While tests shouldn't leave any files or directories behind, when a test + # fails that can be tedious for it to arrange. The consequences can be + # especially nasty on Windows, since if a test leaves a file open, it + # cannot be deleted by name (while there's nothing we can do about that + # here either, we can display the name of the offending test, which is a + # real help). + name = os_helper.TESTFN + if not os.path.exists(name): + return + + nuker: Callable[[str], None] + if os.path.isdir(name): + import shutil + kind, nuker = "directory", shutil.rmtree + elif os.path.isfile(name): + kind, nuker = "file", os.unlink + else: + raise RuntimeError(f"os.path says {name!r} exists but is neither " + f"directory nor file") + + if verbose: + print_warning(f"{test_name} left behind {kind} {name!r}") + support.environment_altered = True + + try: + import stat + # fix possible permissions problems that might prevent cleanup + os.chmod(name, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + nuker(name) + except Exception as exc: + print_warning(f"{test_name} left behind {kind} {name!r} " + f"and it couldn't be removed: {exc}") + + +def abs_module_name(test_name: TestName, test_dir: StrPath | None) -> TestName: + if test_name.startswith('test.') or test_dir: + return test_name + else: + # Import it from the test package + return 'test.' + test_name + + +# gh-90681: When rerunning tests, we might need to rerun the whole +# class or module suite if some its life-cycle hooks fail. +# Test level hooks are not affected. +_TEST_LIFECYCLE_HOOKS = frozenset(( + 'setUpClass', 'tearDownClass', + 'setUpModule', 'tearDownModule', +)) + +def normalize_test_name(test_full_name, *, is_error=False): + short_name = test_full_name.split(" ")[0] + if is_error and short_name in _TEST_LIFECYCLE_HOOKS: + if test_full_name.startswith(('setUpModule (', 'tearDownModule (')): + # if setUpModule() or tearDownModule() failed, don't filter + # tests with the test file name, don't use use filters. + return None + + # This means that we have a failure in a life-cycle hook, + # we need to rerun the whole module or class suite. + # Basically the error looks like this: + # ERROR: setUpClass (test.test_reg_ex.RegTest) + # or + # ERROR: setUpModule (test.test_reg_ex) + # So, we need to parse the class / module name. + lpar = test_full_name.index('(') + rpar = test_full_name.index(')') + return test_full_name[lpar + 1: rpar].split('.')[-1] + return short_name + + +def adjust_rlimit_nofile(): + """ + On macOS the default fd limit (RLIMIT_NOFILE) is sometimes too low (256) + for our test suite to succeed. Raise it to something more reasonable. 1024 + is a common Linux default. + """ + try: + import resource + except ImportError: + return + + fd_limit, max_fds = resource.getrlimit(resource.RLIMIT_NOFILE) + + desired_fds = 1024 + + if fd_limit < desired_fds and fd_limit < max_fds: + new_fd_limit = min(desired_fds, max_fds) + try: + resource.setrlimit(resource.RLIMIT_NOFILE, + (new_fd_limit, max_fds)) + print(f"Raised RLIMIT_NOFILE: {fd_limit} -> {new_fd_limit}") + except (ValueError, OSError) as err: + print_warning(f"Unable to raise RLIMIT_NOFILE from {fd_limit} to " + f"{new_fd_limit}: {err}.") + + +def get_host_runner(): + if (hostrunner := os.environ.get("_PYTHON_HOSTRUNNER")) is None: + hostrunner = sysconfig.get_config_var("HOSTRUNNER") + return hostrunner + + +def is_cross_compiled(): + return ('_PYTHON_HOST_PLATFORM' in os.environ) + + +def format_resources(use_resources: tuple[str, ...]): + use_resources = set(use_resources) + all_resources = set(ALL_RESOURCES) + + # Express resources relative to "all" + relative_all = ['all'] + for name in sorted(all_resources - use_resources): + relative_all.append(f'-{name}') + for name in sorted(use_resources - all_resources): + relative_all.append(f'{name}') + all_text = ','.join(relative_all) + all_text = f"resources: {all_text}" + + # List of enabled resources + text = ','.join(sorted(use_resources)) + text = f"resources ({len(use_resources)}): {text}" + + # Pick the shortest string (prefer relative to all if lengths are equal) + if len(all_text) <= len(text): + return all_text + else: + return text + + +def process_cpu_count(): + if hasattr(os, 'sched_getaffinity'): + return len(os.sched_getaffinity(0)) + else: + return os.cpu_count() + + +def display_header(use_resources: tuple[str, ...], + python_cmd: tuple[str, ...] | None): + # Print basic platform information + print("==", platform.python_implementation(), *sys.version.split()) + print("==", platform.platform(aliased=True), + "%s-endian" % sys.byteorder) + print("== Python build:", ' '.join(get_build_info())) + print("== cwd:", os.getcwd()) + + cpu_count = os.cpu_count() + if cpu_count: + affinity = process_cpu_count() + if affinity and affinity != cpu_count: + cpu_count = f"{affinity} (process) / {cpu_count} (system)" + print("== CPU count:", cpu_count) + print("== encodings: locale=%s FS=%s" + % (locale.getencoding(), sys.getfilesystemencoding())) + + if use_resources: + text = format_resources(use_resources) + print(f"== {text}") + else: + print("== resources: all test resources are disabled, " + "use -u option to unskip tests") + + cross_compile = is_cross_compiled() + if cross_compile: + print("== cross compiled: Yes") + if python_cmd: + cmd = shlex.join(python_cmd) + print(f"== host python: {cmd}") + + get_cmd = [*python_cmd, '-m', 'platform'] + proc = subprocess.run( + get_cmd, + stdout=subprocess.PIPE, + text=True, + cwd=os_helper.SAVEDCWD) + stdout = proc.stdout.replace('\n', ' ').strip() + if stdout: + print(f"== host platform: {stdout}") + elif proc.returncode: + print(f"== host platform: ") + else: + hostrunner = get_host_runner() + if hostrunner: + print(f"== host runner: {hostrunner}") + + # This makes it easier to remember what to set in your local + # environment when trying to reproduce a sanitizer failure. + asan = support.check_sanitizer(address=True) + msan = support.check_sanitizer(memory=True) + ubsan = support.check_sanitizer(ub=True) + sanitizers = [] + if asan: + sanitizers.append("address") + if msan: + sanitizers.append("memory") + if ubsan: + sanitizers.append("undefined behavior") + if sanitizers: + print(f"== sanitizers: {', '.join(sanitizers)}") + for sanitizer, env_var in ( + (asan, "ASAN_OPTIONS"), + (msan, "MSAN_OPTIONS"), + (ubsan, "UBSAN_OPTIONS"), + ): + options= os.environ.get(env_var) + if sanitizer and options is not None: + print(f"== {env_var}={options!r}") + + print(flush=True) + + +def cleanup_temp_dir(tmp_dir: StrPath): + import glob + + path = os.path.join(glob.escape(tmp_dir), TMP_PREFIX + '*') + print("Cleanup %s directory" % tmp_dir) + for name in glob.glob(path): + if os.path.isdir(name): + print("Remove directory: %s" % name) + os_helper.rmtree(name) + else: + print("Remove file: %s" % name) + os_helper.unlink(name) + +WINDOWS_STATUS = { + 0xC0000005: "STATUS_ACCESS_VIOLATION", + 0xC00000FD: "STATUS_STACK_OVERFLOW", + 0xC000013A: "STATUS_CONTROL_C_EXIT", +} + +def get_signal_name(exitcode): + if exitcode < 0: + signum = -exitcode + try: + return signal.Signals(signum).name + except ValueError: + pass + + try: + return WINDOWS_STATUS[exitcode] + except KeyError: + pass + + return None diff --git a/Lib/test/libregrtest/worker.py b/Lib/test/libregrtest/worker.py new file mode 100644 index 00000000000000..a9c8be0bb65d08 --- /dev/null +++ b/Lib/test/libregrtest/worker.py @@ -0,0 +1,116 @@ +import subprocess +import sys +import os +from typing import Any, NoReturn + +from test import support +from test.support import os_helper + +from .setup import setup_process, setup_test_dir +from .runtests import RunTests, JsonFile, JsonFileType +from .single import run_single_test +from .utils import ( + StrPath, StrJSON, FilterTuple, + get_temp_dir, get_work_dir, exit_timeout) + + +USE_PROCESS_GROUP = (hasattr(os, "setsid") and hasattr(os, "killpg")) + + +def create_worker_process(runtests: RunTests, output_fd: int, + tmp_dir: StrPath | None = None) -> subprocess.Popen: + python_cmd = runtests.python_cmd + worker_json = runtests.as_json() + + python_opts = support.args_from_interpreter_flags() + if python_cmd is not None: + executable = python_cmd + # Remove -E option, since --python=COMMAND can set PYTHON environment + # variables, such as PYTHONPATH, in the worker process. + python_opts = [opt for opt in python_opts if opt != "-E"] + else: + executable = (sys.executable,) + cmd = [*executable, *python_opts, + '-u', # Unbuffered stdout and stderr + '-m', 'test.libregrtest.worker', + worker_json] + + env = dict(os.environ) + if tmp_dir is not None: + env['TMPDIR'] = tmp_dir + env['TEMP'] = tmp_dir + env['TMP'] = tmp_dir + + # Running the child from the same working directory as regrtest's original + # invocation ensures that TEMPDIR for the child is the same when + # sysconfig.is_python_build() is true. See issue 15300. + # + # Emscripten and WASI Python must start in the Python source code directory + # to get 'python.js' or 'python.wasm' file. Then worker_process() changes + # to a temporary directory created to run tests. + work_dir = os_helper.SAVEDCWD + + kwargs: dict[str, Any] = dict( + env=env, + stdout=output_fd, + # bpo-45410: Write stderr into stdout to keep messages order + stderr=output_fd, + text=True, + close_fds=True, + cwd=work_dir, + ) + if USE_PROCESS_GROUP: + kwargs['start_new_session'] = True + + # Pass json_file to the worker process + json_file = runtests.json_file + json_file.configure_subprocess(kwargs) + + with json_file.inherit_subprocess(): + return subprocess.Popen(cmd, **kwargs) + + +def worker_process(worker_json: StrJSON) -> NoReturn: + runtests = RunTests.from_json(worker_json) + test_name = runtests.tests[0] + match_tests: FilterTuple | None = runtests.match_tests + json_file: JsonFile = runtests.json_file + + setup_test_dir(runtests.test_dir) + setup_process() + + if runtests.rerun: + if match_tests: + matching = "matching: " + ", ".join(match_tests) + print(f"Re-running {test_name} in verbose mode ({matching})", flush=True) + else: + print(f"Re-running {test_name} in verbose mode", flush=True) + + result = run_single_test(test_name, runtests) + + if json_file.file_type == JsonFileType.STDOUT: + print() + result.write_json_into(sys.stdout) + else: + with json_file.open('w', encoding='utf-8') as json_fp: + result.write_json_into(json_fp) + + sys.exit(0) + + +def main(): + if len(sys.argv) != 2: + print("usage: python -m test.libregrtest.worker JSON") + sys.exit(1) + worker_json = sys.argv[1] + + tmp_dir = get_temp_dir() + work_dir = get_work_dir(tmp_dir, worker=True) + + with exit_timeout(): + with os_helper.temp_cwd(work_dir, quiet=True): + worker_process(worker_json) + + +if __name__ == "__main__": + main() diff --git a/Lib/test/pythoninfo.py b/Lib/test/pythoninfo.py index 3dbcac2f86e81e..4f3ebb12ed957d 100644 --- a/Lib/test/pythoninfo.py +++ b/Lib/test/pythoninfo.py @@ -1,18 +1,13 @@ """ Collect various information about Python to help debugging test failures. """ -from __future__ import print_function import errno import re import sys import traceback -import unittest import warnings -MS_WINDOWS = (sys.platform == 'win32') - - def normalize_text(text): if text is None: return None @@ -244,6 +239,7 @@ def format_attr(attr, value): 'getresgid', 'getresuid', 'getuid', + 'process_cpu_count', 'uname', ): call_func(info_add, 'os.%s' % func, os, func) @@ -273,6 +269,7 @@ def format_groups(groups): "ARCHFLAGS", "ARFLAGS", "AUDIODEV", + "BUILDPYTHON", "CC", "CFLAGS", "COLUMNS", @@ -317,7 +314,6 @@ def format_groups(groups): "TEMP", "TERM", "TILE_LIBRARY", - "TIX_LIBRARY", "TMP", "TMPDIR", "TRAVIS", @@ -326,6 +322,7 @@ def format_groups(groups): "VIRTUAL_ENV", "WAYLAND_DISPLAY", "WINDIR", + "_PYTHON_HOSTRUNNER", "_PYTHON_HOST_PLATFORM", "_PYTHON_PROJECT_BASE", "_PYTHON_SYSCONFIGDATA_NAME", @@ -341,7 +338,8 @@ def format_groups(groups): for name, value in os.environ.items(): uname = name.upper() if (uname in ENV_VARS - # Copy PYTHON* and LC_* variables + # Copy PYTHON* variables like PYTHONPATH + # Copy LC_* variables like LC_ALL or uname.startswith(("PYTHON", "LC_")) # Visual Studio: VS140COMNTOOLS or (uname.startswith("VS") and uname.endswith("COMNTOOLS"))): @@ -494,13 +492,10 @@ def collect_datetime(info_add): def collect_sysconfig(info_add): - # On Windows, sysconfig is not reliable to get macros used - # to build Python - if MS_WINDOWS: - return - import sysconfig + info_add('sysconfig.is_python_build', sysconfig.is_python_build()) + for name in ( 'ABIFLAGS', 'ANDROID_API_LEVEL', @@ -509,6 +504,7 @@ def collect_sysconfig(info_add): 'CFLAGS', 'CFLAGSFORSHARED', 'CONFIG_ARGS', + 'HOSTRUNNER', 'HOST_GNU_TYPE', 'MACHDEP', 'MULTIARCH', @@ -634,7 +630,7 @@ def collect_sqlite(info_add): except ImportError: return - attributes = ('version', 'sqlite_version') + attributes = ('sqlite_version',) copy_attributes(info_add, sqlite3, 'sqlite3.%s', attributes) @@ -674,7 +670,29 @@ def collect_testcapi(info_add): except ImportError: return - call_func(info_add, 'pymem.allocator', _testcapi, 'pymem_getallocatorsname') + for name in ( + 'LONG_MAX', # always 32-bit on Windows, 64-bit on 64-bit Unix + 'PY_SSIZE_T_MAX', + 'Py_C_RECURSION_LIMIT', + 'SIZEOF_TIME_T', # 32-bit or 64-bit depending on the platform + 'SIZEOF_WCHAR_T', # 16-bit or 32-bit depending on the platform + ): + copy_attr(info_add, f'_testcapi.{name}', _testcapi, name) + + +def collect_testinternalcapi(info_add): + try: + import _testinternalcapi + except ImportError: + return + + call_func(info_add, 'pymem.allocator', _testinternalcapi, 'pymem_getallocatorsname') + + for name in ( + 'SIZEOF_PYGC_HEAD', + 'SIZEOF_PYOBJECT', + ): + copy_attr(info_add, f'_testinternalcapi.{name}', _testinternalcapi, name) def collect_resource(info_add): @@ -693,6 +711,7 @@ def collect_resource(info_add): def collect_test_socket(info_add): + import unittest try: from test import test_socket except (ImportError, unittest.SkipTest): @@ -704,15 +723,12 @@ def collect_test_socket(info_add): copy_attributes(info_add, test_socket, 'test_socket.%s', attributes) -def collect_test_support(info_add): +def collect_support(info_add): try: from test import support except ImportError: return - attributes = ('IPV6_ENABLED',) - copy_attributes(info_add, support, 'test_support.%s', attributes) - attributes = ( 'MS_WINDOWS', 'has_fork_support', @@ -726,17 +742,64 @@ def collect_test_support(info_add): ) copy_attributes(info_add, support, 'support.%s', attributes) - call_func(info_add, 'test_support._is_gui_available', support, '_is_gui_available') - call_func(info_add, 'test_support.python_is_optimized', support, 'python_is_optimized') + call_func(info_add, 'support._is_gui_available', support, '_is_gui_available') + call_func(info_add, 'support.python_is_optimized', support, 'python_is_optimized') - info_add('test_support.check_sanitizer(address=True)', + info_add('support.check_sanitizer(address=True)', support.check_sanitizer(address=True)) - info_add('test_support.check_sanitizer(memory=True)', + info_add('support.check_sanitizer(memory=True)', support.check_sanitizer(memory=True)) - info_add('test_support.check_sanitizer(ub=True)', + info_add('support.check_sanitizer(ub=True)', support.check_sanitizer(ub=True)) +def collect_support_os_helper(info_add): + try: + from test.support import os_helper + except ImportError: + return + + for name in ( + 'can_symlink', + 'can_xattr', + 'can_chmod', + 'can_dac_override', + ): + func = getattr(os_helper, name) + info_add(f'support_os_helper.{name}', func()) + + +def collect_support_socket_helper(info_add): + try: + from test.support import socket_helper + except ImportError: + return + + attributes = ( + 'IPV6_ENABLED', + 'has_gethostname', + ) + copy_attributes(info_add, socket_helper, 'support_socket_helper.%s', attributes) + + for name in ( + 'tcp_blackhole', + ): + func = getattr(socket_helper, name) + info_add(f'support_socket_helper.{name}', func()) + + +def collect_support_threading_helper(info_add): + try: + from test.support import threading_helper + except ImportError: + return + + attributes = ( + 'can_start_thread', + ) + copy_attributes(info_add, threading_helper, 'support_threading_helper.%s', attributes) + + def collect_cc(info_add): import subprocess import sysconfig @@ -891,6 +954,12 @@ def collect_fips(info_add): pass +def collect_tempfile(info_add): + import tempfile + + info_add('tempfile.gettempdir', tempfile.gettempdir()) + + def collect_libregrtest_utils(info_add): try: from test.libregrtest import utils @@ -933,6 +1002,8 @@ def collect_info(info): collect_sys, collect_sysconfig, collect_testcapi, + collect_testinternalcapi, + collect_tempfile, collect_time, collect_tkinter, collect_windows, @@ -941,7 +1012,10 @@ def collect_info(info): # Collecting from tests should be last as they have side effects. collect_test_socket, - collect_test_support, + collect_support, + collect_support_os_helper, + collect_support_socket_helper, + collect_support_threading_helper, ): try: collect_func(info_add) diff --git a/Lib/test/regrtest.py b/Lib/test/regrtest.py index 0ffb3ed454eda0..46a74fe276f553 100755 --- a/Lib/test/regrtest.py +++ b/Lib/test/regrtest.py @@ -8,7 +8,7 @@ import os import sys -from test.libregrtest import main +from test.libregrtest.main import main # Alias for backward compatibility (just in case) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index f5307b6442bf66..9066f0cd5b3819 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -431,6 +431,14 @@ def skip_if_sanitizer(reason=None, *, address=False, memory=False, ub=False): HAVE_ASAN_FORK_BUG = check_sanitizer(address=True) +def set_sanitizer_env_var(env, option): + for name in ('ASAN_OPTIONS', 'MSAN_OPTIONS', 'UBSAN_OPTIONS'): + if name in env: + env[name] += f':{option}' + else: + env[name] = option + + def system_must_validate_cert(f): """Skip the test on TLS certificate validation failures.""" @functools.wraps(f) @@ -892,27 +900,31 @@ def inner(*args, **kwds): MAX_Py_ssize_t = sys.maxsize -def set_memlimit(limit): - global max_memuse - global real_max_memuse +def _parse_memlimit(limit: str) -> int: sizes = { 'k': 1024, 'm': _1M, 'g': _1G, 't': 1024*_1G, } - m = re.match(r'(\d+(\.\d+)?) (K|M|G|T)b?$', limit, + m = re.match(r'(\d+(?:\.\d+)?) (K|M|G|T)b?$', limit, re.IGNORECASE | re.VERBOSE) if m is None: - raise ValueError('Invalid memory limit %r' % (limit,)) - memlimit = int(float(m.group(1)) * sizes[m.group(3).lower()]) - real_max_memuse = memlimit - if memlimit > MAX_Py_ssize_t: - memlimit = MAX_Py_ssize_t + raise ValueError(f'Invalid memory limit: {limit!r}') + return int(float(m.group(1)) * sizes[m.group(2).lower()]) + +def set_memlimit(limit: str) -> None: + global max_memuse + global real_max_memuse + memlimit = _parse_memlimit(limit) if memlimit < _2G - 1: - raise ValueError('Memory limit %r too low to be useful' % (limit,)) + raise ValueError('Memory limit {limit!r} too low to be useful') + + real_max_memuse = memlimit + memlimit = min(memlimit, MAX_Py_ssize_t) max_memuse = memlimit + class _MemoryWatchdog: """An object which periodically watches the process' memory consumption and prints it out. @@ -1187,7 +1199,6 @@ def _is_full_match_test(pattern): def set_match_tests(accept_patterns=None, ignore_patterns=None): global _match_test_func, _accept_test_patterns, _ignore_test_patterns - if accept_patterns is None: accept_patterns = () if ignore_patterns is None: @@ -2139,31 +2150,26 @@ def wait_process(pid, *, exitcode, timeout=None): if timeout is None: timeout = LONG_TIMEOUT - t0 = time.monotonic() - sleep = 0.001 - max_sleep = 0.1 - while True: + + start_time = time.monotonic() + for _ in sleeping_retry(timeout, error=False): pid2, status = os.waitpid(pid, os.WNOHANG) if pid2 != 0: break - # process is still running - - dt = time.monotonic() - t0 - if dt > timeout: - try: - os.kill(pid, signal.SIGKILL) - os.waitpid(pid, 0) - except OSError: - # Ignore errors like ChildProcessError or PermissionError - pass - - raise AssertionError(f"process {pid} is still running " - f"after {dt:.1f} seconds") + # rety: the process is still running + else: + try: + os.kill(pid, signal.SIGKILL) + os.waitpid(pid, 0) + except OSError: + # Ignore errors like ChildProcessError or PermissionError + pass - sleep = min(sleep * 2, max_sleep) - time.sleep(sleep) + dt = time.monotonic() - start_time + raise AssertionError(f"process {pid} is still running " + f"after {dt:.1f} seconds") else: - # Windows implementation + # Windows implementation: don't support timeout :-( pid2, status = os.waitpid(pid, 0) exitcode2 = os.waitstatus_to_exitcode(status) @@ -2327,6 +2333,11 @@ def requires_venv_with_pip(): return unittest.skipUnless(ctypes, 'venv: pip requires ctypes') +# True if Python is built with the Py_DEBUG macro defined: if +# Python is built in debug mode (./configure --with-pydebug). +Py_DEBUG = hasattr(sys, 'gettotalrefcount') + + def busy_retry(timeout, err_msg=None, /, *, error=True): """ Run the loop body until "break" stops the loop. diff --git a/Lib/test/support/os_helper.py b/Lib/test/support/os_helper.py index f599cc752196e1..f77cce89a37d57 100644 --- a/Lib/test/support/os_helper.py +++ b/Lib/test/support/os_helper.py @@ -567,7 +567,7 @@ def fs_is_case_insensitive(directory): class FakePath: - """Simple implementing of the path protocol. + """Simple implementation of the path protocol. """ def __init__(self, path): self.path = path diff --git a/Lib/test/support/testresult.py b/Lib/test/support/testresult.py index 2cd1366cd8a9e1..c5c297f1057b17 100644 --- a/Lib/test/support/testresult.py +++ b/Lib/test/support/testresult.py @@ -8,6 +8,7 @@ import time import traceback import unittest +from test import support class RegressionTestResult(unittest.TextTestResult): USE_XML = False @@ -109,6 +110,8 @@ def addExpectedFailure(self, test, err): def addFailure(self, test, err): self._add_result(test, True, failure=self.__makeErrorDict(*err)) super().addFailure(test, err) + if support.failfast: + self.stop() def addSkip(self, test, reason): self._add_result(test, skipped=reason) diff --git a/Lib/test/support/threading_helper.py b/Lib/test/support/threading_helper.py index 26cbc6f4d2439c..b9973c8bf5c914 100644 --- a/Lib/test/support/threading_helper.py +++ b/Lib/test/support/threading_helper.py @@ -88,19 +88,17 @@ def wait_threads_exit(timeout=None): yield finally: start_time = time.monotonic() - deadline = start_time + timeout - while True: + for _ in support.sleeping_retry(timeout, error=False): + support.gc_collect() count = _thread._count() if count <= old_count: break - if time.monotonic() > deadline: - dt = time.monotonic() - start_time - msg = (f"wait_threads() failed to cleanup {count - old_count} " - f"threads after {dt:.1f} seconds " - f"(count: {count}, old count: {old_count})") - raise AssertionError(msg) - time.sleep(0.010) - support.gc_collect() + else: + dt = time.monotonic() - start_time + msg = (f"wait_threads() failed to cleanup {count - old_count} " + f"threads after {dt:.1f} seconds " + f"(count: {count}, old count: {old_count})") + raise AssertionError(msg) def join_thread(thread, timeout=None): diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py index 50b40d336e4dca..2d190647f2834b 100644 --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py @@ -8,7 +8,6 @@ import sys from test import support from test.support import os_helper, script_helper, is_android, MS_WINDOWS -from test.support import skip_if_sanitizer import tempfile import unittest from textwrap import dedent @@ -34,7 +33,7 @@ def expected_traceback(lineno1, lineno2, header, min_count=1): return '^' + regex + '$' def skip_segfault_on_android(test): - # Issue #32138: Raising SIGSEGV on Android may not cause a crash. + # gh-76319: Raising SIGSEGV on Android may not cause a crash. return unittest.skipIf(is_android, 'raising SIGSEGV on Android is unreliable')(test) @@ -62,8 +61,16 @@ def get_output(self, code, filename=None, fd=None): pass_fds = [] if fd is not None: pass_fds.append(fd) + env = dict(os.environ) + + # Sanitizers must not handle SIGSEGV (ex: for test_enable_fd()) + option = 'handle_segv=0' + support.set_sanitizer_env_var(env, option) + with support.SuppressCrashReport(): - process = script_helper.spawn_python('-c', code, pass_fds=pass_fds) + process = script_helper.spawn_python('-c', code, + pass_fds=pass_fds, + env=env) with process: output, stderr = process.communicate() exitcode = process.wait() @@ -302,8 +309,6 @@ def test_gil_released(self): 3, 'Segmentation fault') - @skip_if_sanitizer(memory=True, ub=True, reason="sanitizer " - "builds change crashing process output.") @skip_segfault_on_android def test_enable_file(self): with temporary_filename() as filename: @@ -319,8 +324,6 @@ def test_enable_file(self): @unittest.skipIf(sys.platform == "win32", "subprocess doesn't support pass_fds on Windows") - @skip_if_sanitizer(memory=True, ub=True, reason="sanitizer " - "builds change crashing process output.") @skip_segfault_on_android def test_enable_fd(self): with tempfile.TemporaryFile('wb+') as fp: diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py index 88c59b79dd5a91..45573a2719c543 100644 --- a/Lib/test/test_regrtest.py +++ b/Lib/test/test_regrtest.py @@ -5,28 +5,33 @@ """ import contextlib +import dataclasses import glob import io import locale import os.path import platform +import random import re +import shlex +import signal import subprocess import sys import sysconfig import tempfile import textwrap -import time import unittest -from test import libregrtest from test import support from test.support import os_helper, TestStats -from test.libregrtest import utils, setup +from test.libregrtest import cmdline +from test.libregrtest import main +from test.libregrtest import setup +from test.libregrtest import utils +from test.libregrtest.utils import normalize_test_name if not support.has_subprocess_support: raise unittest.SkipTest("test module requires subprocess") -Py_DEBUG = hasattr(sys, 'gettotalrefcount') ROOT_DIR = os.path.join(os.path.dirname(__file__), '..', '..') ROOT_DIR = os.path.abspath(os.path.normpath(ROOT_DIR)) LOG_PREFIX = r'[0-9]+:[0-9]+:[0-9]+ (?:load avg: [0-9]+\.[0-9]{2} )?' @@ -34,6 +39,7 @@ EXITCODE_BAD_TEST = 2 EXITCODE_ENV_CHANGED = 3 EXITCODE_NO_TESTS_RAN = 4 +EXITCODE_RERUN_FAIL = 5 EXITCODE_INTERRUPTED = 130 TEST_INTERRUPTED = textwrap.dedent(""" @@ -51,9 +57,13 @@ class ParseArgsTestCase(unittest.TestCase): Test regrtest's argument parsing, function _parse_args(). """ + @staticmethod + def parse_args(args): + return cmdline._parse_args(args) + def checkError(self, args, msg): with support.captured_stderr() as err, self.assertRaises(SystemExit): - libregrtest._parse_args(args) + self.parse_args(args) self.assertIn(msg, err.getvalue()) def test_help(self): @@ -61,83 +71,93 @@ def test_help(self): with self.subTest(opt=opt): with support.captured_stdout() as out, \ self.assertRaises(SystemExit): - libregrtest._parse_args([opt]) + self.parse_args([opt]) self.assertIn('Run Python regression tests.', out.getvalue()) def test_timeout(self): - ns = libregrtest._parse_args(['--timeout', '4.2']) + ns = self.parse_args(['--timeout', '4.2']) self.assertEqual(ns.timeout, 4.2) + + # negative, zero and empty string are treated as "no timeout" + for value in ('-1', '0', ''): + with self.subTest(value=value): + ns = self.parse_args([f'--timeout={value}']) + self.assertEqual(ns.timeout, None) + self.checkError(['--timeout'], 'expected one argument') - self.checkError(['--timeout', 'foo'], 'invalid float value') + self.checkError(['--timeout', 'foo'], 'invalid timeout value:') def test_wait(self): - ns = libregrtest._parse_args(['--wait']) + ns = self.parse_args(['--wait']) self.assertTrue(ns.wait) - def test_worker_args(self): - ns = libregrtest._parse_args(['--worker-args', '[[], {}]']) - self.assertEqual(ns.worker_args, '[[], {}]') - self.checkError(['--worker-args'], 'expected one argument') - def test_start(self): for opt in '-S', '--start': with self.subTest(opt=opt): - ns = libregrtest._parse_args([opt, 'foo']) + ns = self.parse_args([opt, 'foo']) self.assertEqual(ns.start, 'foo') self.checkError([opt], 'expected one argument') def test_verbose(self): - ns = libregrtest._parse_args(['-v']) + ns = self.parse_args(['-v']) self.assertEqual(ns.verbose, 1) - ns = libregrtest._parse_args(['-vvv']) + ns = self.parse_args(['-vvv']) self.assertEqual(ns.verbose, 3) - ns = libregrtest._parse_args(['--verbose']) + ns = self.parse_args(['--verbose']) self.assertEqual(ns.verbose, 1) - ns = libregrtest._parse_args(['--verbose'] * 3) + ns = self.parse_args(['--verbose'] * 3) self.assertEqual(ns.verbose, 3) - ns = libregrtest._parse_args([]) + ns = self.parse_args([]) self.assertEqual(ns.verbose, 0) - def test_verbose2(self): - for opt in '-w', '--verbose2': + def test_rerun(self): + for opt in '-w', '--rerun', '--verbose2': with self.subTest(opt=opt): - ns = libregrtest._parse_args([opt]) - self.assertTrue(ns.verbose2) + ns = self.parse_args([opt]) + self.assertTrue(ns.rerun) def test_verbose3(self): for opt in '-W', '--verbose3': with self.subTest(opt=opt): - ns = libregrtest._parse_args([opt]) + ns = self.parse_args([opt]) self.assertTrue(ns.verbose3) def test_quiet(self): for opt in '-q', '--quiet': with self.subTest(opt=opt): - ns = libregrtest._parse_args([opt]) + ns = self.parse_args([opt]) self.assertTrue(ns.quiet) self.assertEqual(ns.verbose, 0) def test_slowest(self): for opt in '-o', '--slowest': with self.subTest(opt=opt): - ns = libregrtest._parse_args([opt]) + ns = self.parse_args([opt]) self.assertTrue(ns.print_slow) def test_header(self): - ns = libregrtest._parse_args(['--header']) + ns = self.parse_args(['--header']) self.assertTrue(ns.header) - ns = libregrtest._parse_args(['--verbose']) + ns = self.parse_args(['--verbose']) self.assertTrue(ns.header) def test_randomize(self): for opt in '-r', '--randomize': with self.subTest(opt=opt): - ns = libregrtest._parse_args([opt]) + ns = self.parse_args([opt]) self.assertTrue(ns.randomize) + with os_helper.EnvironmentVarGuard() as env: + env['SOURCE_DATE_EPOCH'] = '1' + + ns = self.parse_args(['--randomize']) + regrtest = main.Regrtest(ns) + self.assertFalse(regrtest.randomize) + self.assertIsNone(regrtest.random_seed) + def test_randseed(self): - ns = libregrtest._parse_args(['--randseed', '12345']) + ns = self.parse_args(['--randseed', '12345']) self.assertEqual(ns.random_seed, 12345) self.assertTrue(ns.randomize) self.checkError(['--randseed'], 'expected one argument') @@ -146,7 +166,7 @@ def test_randseed(self): def test_fromfile(self): for opt in '-f', '--fromfile': with self.subTest(opt=opt): - ns = libregrtest._parse_args([opt, 'foo']) + ns = self.parse_args([opt, 'foo']) self.assertEqual(ns.fromfile, 'foo') self.checkError([opt], 'expected one argument') self.checkError([opt, 'foo', '-s'], "don't go together") @@ -154,20 +174,20 @@ def test_fromfile(self): def test_exclude(self): for opt in '-x', '--exclude': with self.subTest(opt=opt): - ns = libregrtest._parse_args([opt]) + ns = self.parse_args([opt]) self.assertTrue(ns.exclude) def test_single(self): for opt in '-s', '--single': with self.subTest(opt=opt): - ns = libregrtest._parse_args([opt]) + ns = self.parse_args([opt]) self.assertTrue(ns.single) self.checkError([opt, '-f', 'foo'], "don't go together") def test_ignore(self): for opt in '-i', '--ignore': with self.subTest(opt=opt): - ns = libregrtest._parse_args([opt, 'pattern']) + ns = self.parse_args([opt, 'pattern']) self.assertEqual(ns.ignore_tests, ['pattern']) self.checkError([opt], 'expected one argument') @@ -177,7 +197,7 @@ def test_ignore(self): print('matchfile2', file=fp) filename = os.path.abspath(os_helper.TESTFN) - ns = libregrtest._parse_args(['-m', 'match', + ns = self.parse_args(['-m', 'match', '--ignorefile', filename]) self.assertEqual(ns.ignore_tests, ['matchfile1', 'matchfile2']) @@ -185,11 +205,11 @@ def test_ignore(self): def test_match(self): for opt in '-m', '--match': with self.subTest(opt=opt): - ns = libregrtest._parse_args([opt, 'pattern']) + ns = self.parse_args([opt, 'pattern']) self.assertEqual(ns.match_tests, ['pattern']) self.checkError([opt], 'expected one argument') - ns = libregrtest._parse_args(['-m', 'pattern1', + ns = self.parse_args(['-m', 'pattern1', '-m', 'pattern2']) self.assertEqual(ns.match_tests, ['pattern1', 'pattern2']) @@ -199,7 +219,7 @@ def test_match(self): print('matchfile2', file=fp) filename = os.path.abspath(os_helper.TESTFN) - ns = libregrtest._parse_args(['-m', 'match', + ns = self.parse_args(['-m', 'match', '--matchfile', filename]) self.assertEqual(ns.match_tests, ['match', 'matchfile1', 'matchfile2']) @@ -207,65 +227,65 @@ def test_match(self): def test_failfast(self): for opt in '-G', '--failfast': with self.subTest(opt=opt): - ns = libregrtest._parse_args([opt, '-v']) + ns = self.parse_args([opt, '-v']) self.assertTrue(ns.failfast) - ns = libregrtest._parse_args([opt, '-W']) + ns = self.parse_args([opt, '-W']) self.assertTrue(ns.failfast) self.checkError([opt], '-G/--failfast needs either -v or -W') def test_use(self): for opt in '-u', '--use': with self.subTest(opt=opt): - ns = libregrtest._parse_args([opt, 'gui,network']) + ns = self.parse_args([opt, 'gui,network']) self.assertEqual(ns.use_resources, ['gui', 'network']) - ns = libregrtest._parse_args([opt, 'gui,none,network']) + ns = self.parse_args([opt, 'gui,none,network']) self.assertEqual(ns.use_resources, ['network']) - expected = list(libregrtest.ALL_RESOURCES) + expected = list(cmdline.ALL_RESOURCES) expected.remove('gui') - ns = libregrtest._parse_args([opt, 'all,-gui']) + ns = self.parse_args([opt, 'all,-gui']) self.assertEqual(ns.use_resources, expected) self.checkError([opt], 'expected one argument') self.checkError([opt, 'foo'], 'invalid resource') # all + a resource not part of "all" - ns = libregrtest._parse_args([opt, 'all,tzdata']) + ns = self.parse_args([opt, 'all,tzdata']) self.assertEqual(ns.use_resources, - list(libregrtest.ALL_RESOURCES) + ['tzdata']) + list(cmdline.ALL_RESOURCES) + ['tzdata']) # test another resource which is not part of "all" - ns = libregrtest._parse_args([opt, 'extralargefile']) + ns = self.parse_args([opt, 'extralargefile']) self.assertEqual(ns.use_resources, ['extralargefile']) def test_memlimit(self): for opt in '-M', '--memlimit': with self.subTest(opt=opt): - ns = libregrtest._parse_args([opt, '4G']) + ns = self.parse_args([opt, '4G']) self.assertEqual(ns.memlimit, '4G') self.checkError([opt], 'expected one argument') def test_testdir(self): - ns = libregrtest._parse_args(['--testdir', 'foo']) + ns = self.parse_args(['--testdir', 'foo']) self.assertEqual(ns.testdir, os.path.join(os_helper.SAVEDCWD, 'foo')) self.checkError(['--testdir'], 'expected one argument') def test_runleaks(self): for opt in '-L', '--runleaks': with self.subTest(opt=opt): - ns = libregrtest._parse_args([opt]) + ns = self.parse_args([opt]) self.assertTrue(ns.runleaks) def test_huntrleaks(self): for opt in '-R', '--huntrleaks': with self.subTest(opt=opt): - ns = libregrtest._parse_args([opt, ':']) + ns = self.parse_args([opt, ':']) self.assertEqual(ns.huntrleaks, (5, 4, 'reflog.txt')) - ns = libregrtest._parse_args([opt, '6:']) + ns = self.parse_args([opt, '6:']) self.assertEqual(ns.huntrleaks, (6, 4, 'reflog.txt')) - ns = libregrtest._parse_args([opt, ':3']) + ns = self.parse_args([opt, ':3']) self.assertEqual(ns.huntrleaks, (5, 3, 'reflog.txt')) - ns = libregrtest._parse_args([opt, '6:3:leaks.log']) + ns = self.parse_args([opt, '6:3:leaks.log']) self.assertEqual(ns.huntrleaks, (6, 3, 'leaks.log')) self.checkError([opt], 'expected one argument') self.checkError([opt, '6'], @@ -276,7 +296,7 @@ def test_huntrleaks(self): def test_multiprocess(self): for opt in '-j', '--multiprocess': with self.subTest(opt=opt): - ns = libregrtest._parse_args([opt, '2']) + ns = self.parse_args([opt, '2']) self.assertEqual(ns.use_mp, 2) self.checkError([opt], 'expected one argument') self.checkError([opt, 'foo'], 'invalid int value') @@ -286,13 +306,13 @@ def test_multiprocess(self): def test_coverage(self): for opt in '-T', '--coverage': with self.subTest(opt=opt): - ns = libregrtest._parse_args([opt]) + ns = self.parse_args([opt]) self.assertTrue(ns.trace) def test_coverdir(self): for opt in '-D', '--coverdir': with self.subTest(opt=opt): - ns = libregrtest._parse_args([opt, 'foo']) + ns = self.parse_args([opt, 'foo']) self.assertEqual(ns.coverdir, os.path.join(os_helper.SAVEDCWD, 'foo')) self.checkError([opt], 'expected one argument') @@ -300,13 +320,13 @@ def test_coverdir(self): def test_nocoverdir(self): for opt in '-N', '--nocoverdir': with self.subTest(opt=opt): - ns = libregrtest._parse_args([opt]) + ns = self.parse_args([opt]) self.assertIsNone(ns.coverdir) def test_threshold(self): for opt in '-t', '--threshold': with self.subTest(opt=opt): - ns = libregrtest._parse_args([opt, '1000']) + ns = self.parse_args([opt, '1000']) self.assertEqual(ns.threshold, 1000) self.checkError([opt], 'expected one argument') self.checkError([opt, 'foo'], 'invalid int value') @@ -315,7 +335,7 @@ def test_nowindows(self): for opt in '-n', '--nowindows': with self.subTest(opt=opt): with contextlib.redirect_stderr(io.StringIO()) as stderr: - ns = libregrtest._parse_args([opt]) + ns = self.parse_args([opt]) self.assertTrue(ns.nowindows) err = stderr.getvalue() self.assertIn('the --nowindows (-n) option is deprecated', err) @@ -323,39 +343,39 @@ def test_nowindows(self): def test_forever(self): for opt in '-F', '--forever': with self.subTest(opt=opt): - ns = libregrtest._parse_args([opt]) + ns = self.parse_args([opt]) self.assertTrue(ns.forever) def test_unrecognized_argument(self): self.checkError(['--xxx'], 'usage:') def test_long_option__partial(self): - ns = libregrtest._parse_args(['--qui']) + ns = self.parse_args(['--qui']) self.assertTrue(ns.quiet) self.assertEqual(ns.verbose, 0) def test_two_options(self): - ns = libregrtest._parse_args(['--quiet', '--exclude']) + ns = self.parse_args(['--quiet', '--exclude']) self.assertTrue(ns.quiet) self.assertEqual(ns.verbose, 0) self.assertTrue(ns.exclude) def test_option_with_empty_string_value(self): - ns = libregrtest._parse_args(['--start', '']) + ns = self.parse_args(['--start', '']) self.assertEqual(ns.start, '') def test_arg(self): - ns = libregrtest._parse_args(['foo']) + ns = self.parse_args(['foo']) self.assertEqual(ns.args, ['foo']) def test_option_and_arg(self): - ns = libregrtest._parse_args(['--quiet', 'foo']) + ns = self.parse_args(['--quiet', 'foo']) self.assertTrue(ns.quiet) self.assertEqual(ns.verbose, 0) self.assertEqual(ns.args, ['foo']) def test_arg_option_arg(self): - ns = libregrtest._parse_args(['test_unaryop', '-v', 'test_binop']) + ns = self.parse_args(['test_unaryop', '-v', 'test_binop']) self.assertEqual(ns.verbose, 1) self.assertEqual(ns.args, ['test_unaryop', 'test_binop']) @@ -363,6 +383,62 @@ def test_unknown_option(self): self.checkError(['--unknown-option'], 'unrecognized arguments: --unknown-option') + def check_ci_mode(self, args, use_resources, rerun=True): + ns = cmdline._parse_args(args) + + # Check Regrtest attributes which are more reliable than Namespace + # which has an unclear API + regrtest = main.Regrtest(ns) + self.assertEqual(regrtest.num_workers, -1) + self.assertEqual(regrtest.want_rerun, rerun) + self.assertTrue(regrtest.randomize) + self.assertIsInstance(regrtest.random_seed, int) + self.assertTrue(regrtest.fail_env_changed) + self.assertTrue(regrtest.fail_rerun) + self.assertTrue(regrtest.print_slowest) + self.assertTrue(regrtest.output_on_failure) + self.assertEqual(sorted(regrtest.use_resources), sorted(use_resources)) + return regrtest + + def test_fast_ci(self): + args = ['--fast-ci'] + use_resources = sorted(cmdline.ALL_RESOURCES) + use_resources.remove('cpu') + regrtest = self.check_ci_mode(args, use_resources) + self.assertEqual(regrtest.timeout, 10 * 60) + + def test_fast_ci_python_cmd(self): + args = ['--fast-ci', '--python', 'python -X dev'] + use_resources = sorted(cmdline.ALL_RESOURCES) + use_resources.remove('cpu') + regrtest = self.check_ci_mode(args, use_resources, rerun=False) + self.assertEqual(regrtest.timeout, 10 * 60) + self.assertEqual(regrtest.python_cmd, ('python', '-X', 'dev')) + + def test_fast_ci_resource(self): + # it should be possible to override resources + args = ['--fast-ci', '-u', 'network'] + use_resources = ['network'] + self.check_ci_mode(args, use_resources) + + def test_slow_ci(self): + args = ['--slow-ci'] + use_resources = sorted(cmdline.ALL_RESOURCES) + regrtest = self.check_ci_mode(args, use_resources) + self.assertEqual(regrtest.timeout, 20 * 60) + + def test_dont_add_python_opts(self): + args = ['--dont-add-python-opts'] + ns = cmdline._parse_args(args) + self.assertFalse(ns._add_python_opts) + + +@dataclasses.dataclass(slots=True) +class Rerun: + name: str + match: str | None + success: bool + class BaseTestCase(unittest.TestCase): TEST_UNIQUE_ID = 1 @@ -411,10 +487,12 @@ def regex_search(self, regex, output): self.fail("%r not found in %r" % (regex, output)) return match - def check_line(self, output, regex, full=False): + def check_line(self, output, pattern, full=False, regex=True): + if not regex: + pattern = re.escape(pattern) if full: - regex += '\n' - regex = re.compile(r'^' + regex, re.MULTILINE) + pattern += '\n' + regex = re.compile(r'^' + pattern, re.MULTILINE) self.assertRegex(output, regex) def parse_executed_tests(self, output): @@ -423,13 +501,14 @@ def parse_executed_tests(self, output): parser = re.finditer(regex, output, re.MULTILINE) return list(match.group(1) for match in parser) - def check_executed_tests(self, output, tests, skipped=(), failed=(), + def check_executed_tests(self, output, tests, *, stats, + skipped=(), failed=(), env_changed=(), omitted=(), - rerun={}, run_no_tests=(), + rerun=None, run_no_tests=(), resource_denied=(), - randomize=False, interrupted=False, + randomize=False, parallel=False, interrupted=False, fail_env_changed=False, - *, stats): + forever=False, filtered=False): if isinstance(tests, str): tests = [tests] if isinstance(skipped, str): @@ -446,12 +525,23 @@ def check_executed_tests(self, output, tests, skipped=(), failed=(), run_no_tests = [run_no_tests] if isinstance(stats, int): stats = TestStats(stats) + if parallel: + randomize = True + + rerun_failed = [] + if rerun is not None and not env_changed: + failed = [rerun.name] + if not rerun.success: + rerun_failed.append(rerun.name) executed = self.parse_executed_tests(output) + total_tests = list(tests) + if rerun is not None: + total_tests.append(rerun.name) if randomize: - self.assertEqual(set(executed), set(tests), output) + self.assertEqual(set(executed), set(total_tests), output) else: - self.assertEqual(executed, tests, output) + self.assertEqual(executed, total_tests, output) def plural(count): return 's' if count != 1 else '' @@ -467,12 +557,17 @@ def list_regex(line_format, tests): regex = list_regex('%s test%s skipped', skipped) self.check_line(output, regex) + if resource_denied: + regex = list_regex(r'%s test%s skipped \(resource denied\)', resource_denied) + self.check_line(output, regex) + if failed: regex = list_regex('%s test%s failed', failed) self.check_line(output, regex) if env_changed: - regex = list_regex('%s test%s altered the execution environment', + regex = list_regex(r'%s test%s altered the execution environment ' + r'\(env changed\)', env_changed) self.check_line(output, regex) @@ -480,32 +575,36 @@ def list_regex(line_format, tests): regex = list_regex('%s test%s omitted', omitted) self.check_line(output, regex) - if rerun: - regex = list_regex('%s re-run test%s', rerun.keys()) + if rerun is not None: + regex = list_regex('%s re-run test%s', [rerun.name]) self.check_line(output, regex) - regex = LOG_PREFIX + r"Re-running failed tests in verbose mode" + regex = LOG_PREFIX + r"Re-running 1 failed tests in verbose mode" + self.check_line(output, regex) + regex = fr"Re-running {rerun.name} in verbose mode" + if rerun.match: + regex = fr"{regex} \(matching: {rerun.match}\)" self.check_line(output, regex) - for name, match in rerun.items(): - regex = LOG_PREFIX + f"Re-running {name} in verbose mode \\(matching: {match}\\)" - self.check_line(output, regex) if run_no_tests: regex = list_regex('%s test%s run no tests', run_no_tests) self.check_line(output, regex) - good = (len(tests) - len(skipped) - len(failed) + good = (len(tests) - len(skipped) - len(resource_denied) - len(failed) - len(omitted) - len(env_changed) - len(run_no_tests)) if good: - regex = r'%s test%s OK\.$' % (good, plural(good)) - if not skipped and not failed and good > 1: + regex = r'%s test%s OK\.' % (good, plural(good)) + if not skipped and not failed and (rerun is None or rerun.success) and good > 1: regex = 'All %s' % regex - self.check_line(output, regex) + self.check_line(output, regex, full=True) if interrupted: self.check_line(output, 'Test suite interrupted by signal SIGINT.') # Total tests - parts = [f'run={stats.tests_run:,}'] + text = f'run={stats.tests_run:,}' + if filtered: + text = fr'{text} \(filtered\)' + parts = [text] if stats.failures: parts.append(f'failures={stats.failures:,}') if stats.skipped: @@ -514,44 +613,57 @@ def list_regex(line_format, tests): self.check_line(output, line, full=True) # Total test files - report = [f'success={good}'] - if failed: - report.append(f'failed={len(failed)}') - if env_changed: - report.append(f'env_changed={len(env_changed)}') - if skipped: - report.append(f'skipped={len(skipped)}') - if resource_denied: - report.append(f'resource_denied={len(resource_denied)}') - if rerun: - report.append(f'rerun={len(rerun)}') - if run_no_tests: - report.append(f'run_no_tests={len(run_no_tests)}') + run = len(total_tests) - len(resource_denied) + if rerun is not None: + total_failed = len(rerun_failed) + total_rerun = 1 + else: + total_failed = len(failed) + total_rerun = 0 + if interrupted: + run = 0 + text = f'run={run}' + if not forever: + text = f'{text}/{len(tests)}' + if filtered: + text = fr'{text} \(filtered\)' + report = [text] + for name, ntest in ( + ('failed', total_failed), + ('env_changed', len(env_changed)), + ('skipped', len(skipped)), + ('resource_denied', len(resource_denied)), + ('rerun', total_rerun), + ('run_no_tests', len(run_no_tests)), + ): + if ntest: + report.append(f'{name}={ntest}') line = fr'Total test files: {" ".join(report)}' self.check_line(output, line, full=True) # Result - result = [] + state = [] if failed: - result.append('FAILURE') + state.append('FAILURE') elif fail_env_changed and env_changed: - result.append('ENV CHANGED') + state.append('ENV CHANGED') if interrupted: - result.append('INTERRUPTED') - if not any((good, result, failed, interrupted, skipped, + state.append('INTERRUPTED') + if not any((good, failed, interrupted, skipped, env_changed, fail_env_changed)): - result.append("NO TESTS RAN") - elif not result: - result.append('SUCCESS') - result = ', '.join(result) - if rerun: - result = 'FAILURE then %s' % result - self.check_line(output, f'Result: {result}', full=True) + state.append("NO TESTS RAN") + elif not state: + state.append('SUCCESS') + state = ', '.join(state) + if rerun is not None: + new_state = 'SUCCESS' if rerun.success else 'FAILURE' + state = f'{state} then {new_state}' + self.check_line(output, f'Result: {state}', full=True) def parse_random_seed(self, output): match = self.regex_search(r'Using random seed ([0-9]+)', output) randseed = int(match.group(1)) - self.assertTrue(0 <= randseed <= 10000000, randseed) + self.assertTrue(0 <= randseed, randseed) return randseed def run_command(self, args, input=None, exitcode=0, **kw): @@ -560,18 +672,18 @@ def run_command(self, args, input=None, exitcode=0, **kw): if 'stderr' not in kw: kw['stderr'] = subprocess.STDOUT proc = subprocess.run(args, - universal_newlines=True, + text=True, input=input, stdout=subprocess.PIPE, **kw) if proc.returncode != exitcode: - msg = ("Command %s failed with exit code %s\n" + msg = ("Command %s failed with exit code %s, but exit code %s expected!\n" "\n" "stdout:\n" "---\n" "%s\n" "---\n" - % (str(args), proc.returncode, proc.stdout)) + % (str(args), proc.returncode, exitcode, proc.stdout)) if proc.stderr: msg += ("\n" "stderr:\n" @@ -583,7 +695,11 @@ def run_command(self, args, input=None, exitcode=0, **kw): return proc def run_python(self, args, **kw): - args = [sys.executable, '-X', 'faulthandler', '-I', *args] + extraargs = [] + if 'uops' in sys._xoptions: + # Pass -X uops along + extraargs.extend(['-X', 'uops']) + args = [sys.executable, *extraargs, '-X', 'faulthandler', '-I', *args] proc = self.run_command(args, **kw) return proc.stdout @@ -638,8 +754,8 @@ def check_output(self, output): self.check_executed_tests(output, self.tests, randomize=True, stats=len(self.tests)) - def run_tests(self, args): - output = self.run_python(args) + def run_tests(self, args, env=None): + output = self.run_python(args, env=env) self.check_output(output) def test_script_regrtest(self): @@ -680,14 +796,6 @@ def test_script_autotest(self): args = [*self.python_args, script, *self.regrtest_args, *self.tests] self.run_tests(args) - @unittest.skipUnless(sysconfig.is_python_build(), - 'run_tests.py script is not installed') - def test_tools_script_run_tests(self): - # Tools/scripts/run_tests.py - script = os.path.join(ROOT_DIR, 'Tools', 'scripts', 'run_tests.py') - args = [script, *self.regrtest_args, *self.tests] - self.run_tests(args) - def run_batch(self, *args): proc = self.run_command(args) self.check_output(proc.stdout) @@ -705,7 +813,7 @@ def test_tools_buildbot_test(self): test_args.append('-arm32') # 32-bit ARM build elif platform.architecture()[0] == '64bit': test_args.append('-x64') # 64-bit build - if not Py_DEBUG: + if not support.Py_DEBUG: test_args.append('+d') # Release build, use python.exe self.run_batch(script, *test_args, *self.tests) @@ -722,7 +830,7 @@ def test_pcbuild_rt(self): rt_args.append('-arm32') # 32-bit ARM build elif platform.architecture()[0] == '64bit': rt_args.append('-x64') # 64-bit build - if Py_DEBUG: + if support.Py_DEBUG: rt_args.append('-d') # Debug build, use python_d.exe self.run_batch(script, *rt_args, *self.regrtest_args, *self.tests) @@ -736,6 +844,40 @@ def run_tests(self, *testargs, **kw): cmdargs = ['-m', 'test', '--testdir=%s' % self.tmptestdir, *testargs] return self.run_python(cmdargs, **kw) + def test_success(self): + code = textwrap.dedent(""" + import unittest + + class PassingTests(unittest.TestCase): + def test_test1(self): + pass + + def test_test2(self): + pass + + def test_test3(self): + pass + """) + tests = [self.create_test(f'ok{i}', code=code) for i in range(1, 6)] + + output = self.run_tests(*tests) + self.check_executed_tests(output, tests, + stats=3 * len(tests)) + + def test_skip(self): + code = textwrap.dedent(""" + import unittest + raise unittest.SkipTest("nope") + """) + test_ok = self.create_test('ok') + test_skip = self.create_test('skip', code=code) + tests = [test_ok, test_skip] + + output = self.run_tests(*tests) + self.check_executed_tests(output, tests, + skipped=[test_skip], + stats=1) + def test_failing_test(self): # test a failing test code = textwrap.dedent(""" @@ -775,14 +917,12 @@ def test_pass(self): # -u audio: 1 resource enabled output = self.run_tests('-uaudio', *test_names) self.check_executed_tests(output, test_names, - skipped=tests['network'], resource_denied=tests['network'], stats=1) # no option: 0 resources enabled - output = self.run_tests(*test_names) + output = self.run_tests(*test_names, exitcode=EXITCODE_NO_TESTS_RAN) self.check_executed_tests(output, test_names, - skipped=test_names, resource_denied=test_names, stats=0) @@ -810,6 +950,10 @@ def test_random(self): test_random2 = int(match.group(1)) self.assertEqual(test_random2, test_random) + # check that random.seed is used by default + output = self.run_tests(test, exitcode=EXITCODE_NO_TESTS_RAN) + self.assertIsInstance(self.parse_random_seed(output), int) + def test_fromfile(self): # test --fromfile tests = [self.create_test() for index in range(5)] @@ -928,16 +1072,32 @@ def test_run(self): builtins.__dict__['RUN'] = 1 """) test = self.create_test('forever', code=code) + + # --forever output = self.run_tests('--forever', test, exitcode=EXITCODE_BAD_TEST) self.check_executed_tests(output, [test]*3, failed=test, - stats=TestStats(1, 1)) - - def check_leak(self, code, what): + stats=TestStats(3, 1), + forever=True) + + # --forever --rerun + output = self.run_tests('--forever', '--rerun', test, exitcode=0) + self.check_executed_tests(output, [test]*3, + rerun=Rerun(test, + match='test_run', + success=True), + stats=TestStats(4, 1), + forever=True) + + def check_leak(self, code, what, *, run_workers=False): test = self.create_test('huntrleaks', code=code) filename = 'reflog.txt' self.addCleanup(os_helper.unlink, filename) - output = self.run_tests('--huntrleaks', '3:3:', test, + cmd = ['--huntrleaks', '3:3:'] + if run_workers: + cmd.append('-j1') + cmd.append(test) + output = self.run_tests(*cmd, exitcode=EXITCODE_BAD_TEST, stderr=subprocess.STDOUT) self.check_executed_tests(output, [test], failed=test, stats=1) @@ -952,8 +1112,8 @@ def check_leak(self, code, what): reflog = fp.read() self.assertIn(line2, reflog) - @unittest.skipUnless(Py_DEBUG, 'need a debug build') - def test_huntrleaks(self): + @unittest.skipUnless(support.Py_DEBUG, 'need a debug build') + def check_huntrleaks(self, *, run_workers: bool): # test --huntrleaks code = textwrap.dedent(""" import unittest @@ -964,9 +1124,15 @@ class RefLeakTest(unittest.TestCase): def test_leak(self): GLOBAL_LIST.append(object()) """) - self.check_leak(code, 'references') + self.check_leak(code, 'references', run_workers=run_workers) - @unittest.skipUnless(Py_DEBUG, 'need a debug build') + def test_huntrleaks(self): + self.check_huntrleaks(run_workers=False) + + def test_huntrleaks_mp(self): + self.check_huntrleaks(run_workers=True) + + @unittest.skipUnless(support.Py_DEBUG, 'need a debug build') def test_huntrleaks_fd_leak(self): # test --huntrleaks for file descriptor leak code = textwrap.dedent(""" @@ -1022,7 +1188,7 @@ def test_crashed(self): tests = [crash_test] output = self.run_tests("-j2", *tests, exitcode=EXITCODE_BAD_TEST) self.check_executed_tests(output, tests, failed=crash_test, - randomize=True, stats=0) + parallel=True, stats=0) def parse_methods(self, output): regex = re.compile("^(test[^ ]+).*ok$", flags=re.MULTILINE) @@ -1042,8 +1208,6 @@ def test_method3(self): def test_method4(self): pass """) - all_methods = ['test_method1', 'test_method2', - 'test_method3', 'test_method4'] testname = self.create_test(code=code) # only run a subset @@ -1126,6 +1290,15 @@ def test_env_changed(self): self.check_executed_tests(output, [testname], env_changed=testname, fail_env_changed=True, stats=1) + # rerun + output = self.run_tests("--rerun", testname) + self.check_executed_tests(output, [testname], + env_changed=testname, + rerun=Rerun(testname, + match=None, + success=True), + stats=2) + def test_rerun_fail(self): # FAILURE then FAILURE code = textwrap.dedent(""" @@ -1141,33 +1314,55 @@ def test_fail_always(self): """) testname = self.create_test(code=code) - output = self.run_tests("-w", testname, exitcode=EXITCODE_BAD_TEST) + output = self.run_tests("--rerun", testname, exitcode=EXITCODE_BAD_TEST) self.check_executed_tests(output, [testname], - failed=testname, - rerun={testname: "test_fail_always"}, - stats=TestStats(1, 1)) + rerun=Rerun(testname, + "test_fail_always", + success=False), + stats=TestStats(3, 2)) def test_rerun_success(self): # FAILURE then SUCCESS - code = textwrap.dedent(""" - import builtins + marker_filename = os.path.abspath("regrtest_marker_filename") + self.addCleanup(os_helper.unlink, marker_filename) + self.assertFalse(os.path.exists(marker_filename)) + + code = textwrap.dedent(f""" + import os.path import unittest + marker_filename = {marker_filename!r} + class Tests(unittest.TestCase): def test_succeed(self): return def test_fail_once(self): - if not hasattr(builtins, '_test_failed'): - builtins._test_failed = True + if not os.path.exists(marker_filename): + open(marker_filename, "w").close() self.fail("bug") """) testname = self.create_test(code=code) - output = self.run_tests("-w", testname, exitcode=0) + # FAILURE then SUCCESS => exit code 0 + output = self.run_tests("--rerun", testname, exitcode=0) self.check_executed_tests(output, [testname], - rerun={testname: "test_fail_once"}, - stats=1) + rerun=Rerun(testname, + match="test_fail_once", + success=True), + stats=TestStats(3, 1)) + os_helper.unlink(marker_filename) + + # with --fail-rerun, exit code EXITCODE_RERUN_FAIL + # on "FAILURE then SUCCESS" state. + output = self.run_tests("--rerun", "--fail-rerun", testname, + exitcode=EXITCODE_RERUN_FAIL) + self.check_executed_tests(output, [testname], + rerun=Rerun(testname, + match="test_fail_once", + success=True), + stats=TestStats(3, 1)) + os_helper.unlink(marker_filename) def test_rerun_setup_class_hook_failure(self): # FAILURE then FAILURE @@ -1184,10 +1379,12 @@ def test_success(self): """) testname = self.create_test(code=code) - output = self.run_tests("-w", testname, exitcode=EXITCODE_BAD_TEST) + output = self.run_tests("--rerun", testname, exitcode=EXITCODE_BAD_TEST) self.check_executed_tests(output, testname, failed=[testname], - rerun={testname: "ExampleTests"}, + rerun=Rerun(testname, + match="ExampleTests", + success=False), stats=0) def test_rerun_teardown_class_hook_failure(self): @@ -1205,11 +1402,13 @@ def test_success(self): """) testname = self.create_test(code=code) - output = self.run_tests("-w", testname, exitcode=EXITCODE_BAD_TEST) + output = self.run_tests("--rerun", testname, exitcode=EXITCODE_BAD_TEST) self.check_executed_tests(output, testname, failed=[testname], - rerun={testname: "ExampleTests"}, - stats=1) + rerun=Rerun(testname, + match="ExampleTests", + success=False), + stats=2) def test_rerun_setup_module_hook_failure(self): # FAILURE then FAILURE @@ -1225,10 +1424,12 @@ def test_success(self): """) testname = self.create_test(code=code) - output = self.run_tests("-w", testname, exitcode=EXITCODE_BAD_TEST) + output = self.run_tests("--rerun", testname, exitcode=EXITCODE_BAD_TEST) self.check_executed_tests(output, testname, failed=[testname], - rerun={testname: testname}, + rerun=Rerun(testname, + match=None, + success=False), stats=0) def test_rerun_teardown_module_hook_failure(self): @@ -1245,11 +1446,13 @@ def test_success(self): """) testname = self.create_test(code=code) - output = self.run_tests("-w", testname, exitcode=EXITCODE_BAD_TEST) - self.check_executed_tests(output, testname, + output = self.run_tests("--rerun", testname, exitcode=EXITCODE_BAD_TEST) + self.check_executed_tests(output, [testname], failed=[testname], - rerun={testname: testname}, - stats=1) + rerun=Rerun(testname, + match=None, + success=False), + stats=2) def test_rerun_setup_hook_failure(self): # FAILURE then FAILURE @@ -1265,11 +1468,13 @@ def test_success(self): """) testname = self.create_test(code=code) - output = self.run_tests("-w", testname, exitcode=EXITCODE_BAD_TEST) + output = self.run_tests("--rerun", testname, exitcode=EXITCODE_BAD_TEST) self.check_executed_tests(output, testname, failed=[testname], - rerun={testname: "test_success"}, - stats=1) + rerun=Rerun(testname, + match="test_success", + success=False), + stats=2) def test_rerun_teardown_hook_failure(self): # FAILURE then FAILURE @@ -1285,11 +1490,13 @@ def test_success(self): """) testname = self.create_test(code=code) - output = self.run_tests("-w", testname, exitcode=EXITCODE_BAD_TEST) + output = self.run_tests("--rerun", testname, exitcode=EXITCODE_BAD_TEST) self.check_executed_tests(output, testname, failed=[testname], - rerun={testname: "test_success"}, - stats=1) + rerun=Rerun(testname, + match="test_success", + success=False), + stats=2) def test_rerun_async_setup_hook_failure(self): # FAILURE then FAILURE @@ -1305,11 +1512,12 @@ async def test_success(self): """) testname = self.create_test(code=code) - output = self.run_tests("-w", testname, exitcode=EXITCODE_BAD_TEST) + output = self.run_tests("--rerun", testname, exitcode=EXITCODE_BAD_TEST) self.check_executed_tests(output, testname, - failed=[testname], - rerun={testname: "test_success"}, - stats=1) + rerun=Rerun(testname, + match="test_success", + success=False), + stats=2) def test_rerun_async_teardown_hook_failure(self): # FAILURE then FAILURE @@ -1325,11 +1533,13 @@ async def test_success(self): """) testname = self.create_test(code=code) - output = self.run_tests("-w", testname, exitcode=EXITCODE_BAD_TEST) + output = self.run_tests("--rerun", testname, exitcode=EXITCODE_BAD_TEST) self.check_executed_tests(output, testname, failed=[testname], - rerun={testname: "test_success"}, - stats=1) + rerun=Rerun(testname, + match="test_success", + success=False), + stats=2) def test_no_tests_ran(self): code = textwrap.dedent(""" @@ -1345,7 +1555,7 @@ def test_bug(self): exitcode=EXITCODE_NO_TESTS_RAN) self.check_executed_tests(output, [testname], run_no_tests=testname, - stats=0) + stats=0, filtered=True) def test_no_tests_ran_skip(self): code = textwrap.dedent(""" @@ -1376,7 +1586,7 @@ def test_bug(self): exitcode=EXITCODE_NO_TESTS_RAN) self.check_executed_tests(output, [testname, testname2], run_no_tests=[testname, testname2], - stats=0) + stats=0, filtered=True) def test_no_test_ran_some_test_exist_some_not(self): code = textwrap.dedent(""" @@ -1400,7 +1610,7 @@ def test_other_bug(self): "-m", "test_other_bug", exitcode=0) self.check_executed_tests(output, [testname, testname2], run_no_tests=[testname], - stats=1) + stats=1, filtered=True) @support.cpython_only def test_uncollectable(self): @@ -1611,16 +1821,15 @@ def test_leak_tmp_file(self): self.check_executed_tests(output, testnames, env_changed=testnames, fail_env_changed=True, - randomize=True, + parallel=True, stats=len(testnames)) for testname in testnames: self.assertIn(f"Warning -- {testname} leaked temporary " f"files (1): mytmpfile", output) - def test_mp_decode_error(self): - # gh-101634: If a worker stdout cannot be decoded, report a failed test - # and a non-zero exit code. + def test_worker_decode_error(self): + # gh-109425: Use "backslashreplace" error handler to decode stdout. if sys.platform == 'win32': encoding = locale.getencoding() else: @@ -1628,34 +1837,46 @@ def test_mp_decode_error(self): if encoding is None: encoding = sys.__stdout__.encoding if encoding is None: - self.skipTest(f"cannot get regrtest worker encoding") - - nonascii = b"byte:\xa0\xa9\xff\n" + self.skipTest("cannot get regrtest worker encoding") + + nonascii = bytes(ch for ch in range(128, 256)) + corrupted_output = b"nonascii:%s\n" % (nonascii,) + # gh-108989: On Windows, assertion errors are written in UTF-16: when + # decoded each letter is follow by a NUL character. + assertion_failed = 'Assertion failed: tstate_is_alive(tstate)\n' + corrupted_output += assertion_failed.encode('utf-16-le') try: - nonascii.decode(encoding) + corrupted_output.decode(encoding) except UnicodeDecodeError: pass else: - self.skipTest(f"{encoding} can decode non-ASCII bytes {nonascii!a}") + self.skipTest(f"{encoding} can decode non-ASCII bytes") + + expected_line = corrupted_output.decode(encoding, 'backslashreplace') code = textwrap.dedent(fr""" import sys + import unittest + + class Tests(unittest.TestCase): + def test_pass(self): + pass + # bytes which cannot be decoded from UTF-8 - nonascii = {nonascii!a} - sys.stdout.buffer.write(nonascii) + corrupted_output = {corrupted_output!a} + sys.stdout.buffer.write(corrupted_output) sys.stdout.buffer.flush() """) testname = self.create_test(code=code) - output = self.run_tests("--fail-env-changed", "-v", "-j1", testname, - exitcode=EXITCODE_BAD_TEST) + output = self.run_tests("--fail-env-changed", "-v", "-j1", testname) self.check_executed_tests(output, [testname], - failed=[testname], - randomize=True, - stats=0) + parallel=True, + stats=1) + self.check_line(output, expected_line, regex=False) def test_doctest(self): - code = textwrap.dedent(fr''' + code = textwrap.dedent(r''' import doctest import sys from test import support @@ -1690,9 +1911,169 @@ def load_tests(loader, tests, pattern): exitcode=EXITCODE_BAD_TEST) self.check_executed_tests(output, [testname], failed=[testname], - randomize=True, + parallel=True, stats=TestStats(1, 1, 0)) + def _check_random_seed(self, run_workers: bool): + # gh-109276: When -r/--randomize is used, random.seed() is called + # with the same random seed before running each test file. + code = textwrap.dedent(r''' + import random + import unittest + + class RandomSeedTest(unittest.TestCase): + def test_randint(self): + numbers = [random.randint(0, 1000) for _ in range(10)] + print(f"Random numbers: {numbers}") + ''') + tests = [self.create_test(name=f'test_random{i}', code=code) + for i in range(1, 3+1)] + + random_seed = 856_656_202 + cmd = ["--randomize", f"--randseed={random_seed}"] + if run_workers: + # run as many worker processes than the number of tests + cmd.append(f'-j{len(tests)}') + cmd.extend(tests) + output = self.run_tests(*cmd) + + random.seed(random_seed) + # Make the assumption that nothing consume entropy between libregrest + # setup_tests() which calls random.seed() and RandomSeedTest calling + # random.randint(). + numbers = [random.randint(0, 1000) for _ in range(10)] + expected = f"Random numbers: {numbers}" + + regex = r'^Random numbers: .*$' + matches = re.findall(regex, output, flags=re.MULTILINE) + self.assertEqual(matches, [expected] * len(tests)) + + def test_random_seed(self): + self._check_random_seed(run_workers=False) + + def test_random_seed_workers(self): + self._check_random_seed(run_workers=True) + + def test_python_command(self): + code = textwrap.dedent(r""" + import sys + import unittest + + class WorkerTests(unittest.TestCase): + def test_dev_mode(self): + self.assertTrue(sys.flags.dev_mode) + """) + tests = [self.create_test(code=code) for _ in range(3)] + + # Custom Python command: "python -X dev" + python_cmd = [sys.executable, '-X', 'dev'] + # test.libregrtest.cmdline uses shlex.split() to parse the Python + # command line string + python_cmd = shlex.join(python_cmd) + + output = self.run_tests("--python", python_cmd, "-j0", *tests) + self.check_executed_tests(output, tests, + stats=len(tests), parallel=True) + + 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""" + import sys + import unittest + from test import support + try: + from _testinternalcapi import get_config + except ImportError: + get_config = None + + # WASI/WASM buildbots don't use -E option + use_environment = (support.is_emscripten or support.is_wasi) + + class WorkerTests(unittest.TestCase): + @unittest.skipUnless(get_config is None, 'need get_config()') + def test_config(self): + config = get_config()['config'] + # -u option + self.assertEqual(config['buffered_stdio'], 0) + # -W default option + self.assertTrue(config['warnoptions'], ['default']) + # -bb option + self.assertTrue(config['bytes_warning'], 2) + # -E option + self.assertTrue(config['use_environment'], use_environment) + + def test_python_opts(self): + # -u option + self.assertTrue(sys.__stdout__.write_through) + self.assertTrue(sys.__stderr__.write_through) + + # -W default option + self.assertTrue(sys.warnoptions, ['default']) + + # -bb option + self.assertEqual(sys.flags.bytes_warning, 2) + + # -E option + self.assertEqual(not sys.flags.ignore_environment, + use_environment) + """) + testname = self.create_test(code=code) + + # Use directly subprocess to control the exact command line + cmd = [sys.executable, + "-m", "test", option, + f'--testdir={self.tmptestdir}', + testname] + proc = subprocess.run(cmd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True) + self.assertEqual(proc.returncode, 0, proc) + + def test_add_python_opts(self): + for opt in ("--fast-ci", "--slow-ci"): + with self.subTest(opt=opt): + self.check_add_python_opts(opt) + + # gh-76319: Raising SIGSEGV on Android may not cause a crash. + @unittest.skipIf(support.is_android, + 'raising SIGSEGV on Android is unreliable') + def test_worker_output_on_failure(self): + try: + from faulthandler import _sigsegv + except ImportError: + self.skipTest("need faulthandler._sigsegv") + + code = textwrap.dedent(r""" + import faulthandler + import unittest + from test import support + + class CrashTests(unittest.TestCase): + def test_crash(self): + print("just before crash!", flush=True) + + with support.SuppressCrashReport(): + faulthandler._sigsegv(True) + """) + testname = self.create_test(code=code) + + # Sanitizers must not handle SIGSEGV (ex: for test_enable_fd()) + env = dict(os.environ) + option = 'handle_segv=0' + support.set_sanitizer_env_var(env, option) + + output = self.run_tests("-j1", testname, + exitcode=EXITCODE_BAD_TEST, + env=env) + self.check_executed_tests(output, testname, + failed=[testname], + stats=0, parallel=True) + if not support.MS_WINDOWS: + exitcode = -int(signal.SIGSEGV) + self.assertIn(f"Exit code {exitcode} (SIGSEGV)", output) + self.check_line(output, "just before crash!", full=True, regex=False) + class TestUtils(unittest.TestCase): def test_format_duration(self): @@ -1717,6 +2098,46 @@ def test_format_duration(self): self.assertEqual(utils.format_duration(3 * 3600 + 1), '3 hour 1 sec') + def test_normalize_test_name(self): + normalize = normalize_test_name + self.assertEqual(normalize('test_access (test.test_os.FileTests.test_access)'), + 'test_access') + self.assertEqual(normalize('setUpClass (test.test_os.ChownFileTests)', is_error=True), + 'ChownFileTests') + self.assertEqual(normalize('test_success (test.test_bug.ExampleTests.test_success)', is_error=True), + 'test_success') + self.assertIsNone(normalize('setUpModule (test.test_x)', is_error=True)) + self.assertIsNone(normalize('tearDownModule (test.test_module)', is_error=True)) + + def test_get_signal_name(self): + for exitcode, expected in ( + (-int(signal.SIGINT), 'SIGINT'), + (-int(signal.SIGSEGV), 'SIGSEGV'), + (3221225477, "STATUS_ACCESS_VIOLATION"), + (0xC00000FD, "STATUS_STACK_OVERFLOW"), + ): + self.assertEqual(utils.get_signal_name(exitcode), expected, exitcode) + + def test_format_resources(self): + format_resources = utils.format_resources + ALL_RESOURCES = utils.ALL_RESOURCES + self.assertEqual( + format_resources(("network",)), + 'resources (1): network') + self.assertEqual( + format_resources(("audio", "decimal", "network")), + 'resources (3): audio,decimal,network') + self.assertEqual( + format_resources(ALL_RESOURCES), + 'resources: all') + self.assertEqual( + format_resources(tuple(name for name in ALL_RESOURCES + if name != "cpu")), + 'resources: all,-cpu') + self.assertEqual( + format_resources((*ALL_RESOURCES, "tzdata")), + 'resources: all,tzdata') + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index fec96bef4bbd22..01ceeec3c0a73c 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -31,7 +31,7 @@ def setUpClass(cls): "test.support.warnings_helper", like=".*used in test_support.*" ) cls._test_support_token = support.ignore_deprecations_from( - "test.test_support", like=".*You should NOT be seeing this.*" + __name__, like=".*You should NOT be seeing this.*" ) assert len(warnings.filters) == orig_filter_len + 2 @@ -775,7 +775,45 @@ def recursive_function(depth): else: self.fail("RecursionError was not raised") - #self.assertEqual(available, 2) + def test_parse_memlimit(self): + parse = support._parse_memlimit + KiB = 1024 + MiB = KiB * 1024 + GiB = MiB * 1024 + TiB = GiB * 1024 + self.assertEqual(parse('0k'), 0) + self.assertEqual(parse('3k'), 3 * KiB) + self.assertEqual(parse('2.4m'), int(2.4 * MiB)) + self.assertEqual(parse('4g'), int(4 * GiB)) + self.assertEqual(parse('1t'), TiB) + + for limit in ('', '3', '3.5.10k', '10x'): + with self.subTest(limit=limit): + with self.assertRaises(ValueError): + parse(limit) + + def test_set_memlimit(self): + _4GiB = 4 * 1024 ** 3 + TiB = 1024 ** 4 + old_max_memuse = support.max_memuse + old_real_max_memuse = support.real_max_memuse + try: + if sys.maxsize > 2**32: + support.set_memlimit('4g') + self.assertEqual(support.max_memuse, _4GiB) + self.assertEqual(support.real_max_memuse, _4GiB) + + big = 2**100 // TiB + support.set_memlimit(f'{big}t') + self.assertEqual(support.max_memuse, sys.maxsize) + self.assertEqual(support.real_max_memuse, big * TiB) + else: + support.set_memlimit('4g') + self.assertEqual(support.max_memuse, sys.maxsize) + self.assertEqual(support.real_max_memuse, _4GiB) + finally: + support.max_memuse = old_max_memuse + support.real_max_memuse = old_real_max_memuse def test_copy_python_src_ignore(self): # Get source directory @@ -824,7 +862,6 @@ def test_copy_python_src_ignore(self): # EnvironmentVarGuard # transient_internet # run_with_locale - # set_memlimit # bigmemtest # precisionbigmemtest # bigaddrspacetest From b2a95f694a81d75fbdbc60c111c0557edd2ea81c Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 13 Oct 2023 10:32:42 +0200 Subject: [PATCH 471/632] [3.11] gh-107450: Fix parser column offset overflow test on Windows (GH-110768) (#110809) (cherry picked from commit 05439d308740b621d03562451a7608eb725937ae) Co-authored-by: Lysandros Nikolaou Co-authored-by: Nikita Sobolev --- Lib/test/test_exceptions.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 7f6eaa34e11406..c7c63e5e4ae29d 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -318,8 +318,10 @@ def baz(): check('(yield i) = 2', 1, 2) check('def f(*):\n pass', 1, 7) - def testMemoryErrorBigSource(self): - with self.assertRaisesRegex(OverflowError, "column offset overflow"): + @support.requires_resource('cpu') + @support.bigmemtest(support._2G, memuse=1.5) + def testMemoryErrorBigSource(self, _size): + with self.assertRaises(OverflowError): exec(f"if True:\n {' ' * 2**31}print('hello world')") @cpython_only From c0a77eb4425f0ef716393b11865cc2cb79c647c6 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 13 Oct 2023 15:24:29 +0200 Subject: [PATCH 472/632] [3.11] gh-110703: Add asyncio.wait_for() change notes for 3.11 (GH-110818) (#110827) gh-110703: Add asyncio.wait_for() change notes for 3.11 (GH-110818) * Remove redundant versionchanged * Add missing versionchanged * Update Doc/library/asyncio-task.rst --------- (cherry picked from commit f81e36f700ac8c6766207fcf3bc2540692af868b) Co-authored-by: paskozdilar <53006174+paskozdilar@users.noreply.github.com> Co-authored-by: Kumar Aditya --- Doc/library/asyncio-task.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst index ef17f384c1bfeb..0944aef0ab619d 100644 --- a/Doc/library/asyncio-task.rst +++ b/Doc/library/asyncio-task.rst @@ -714,9 +714,6 @@ Timeouts If the wait is cancelled, the future *aw* is also cancelled. - .. versionchanged:: 3.10 - Removed the *loop* parameter. - .. _asyncio_example_waitfor: Example:: @@ -747,6 +744,9 @@ Timeouts .. versionchanged:: 3.10 Removed the *loop* parameter. + .. versionchanged:: 3.11 + Raises :exc:`TimeoutError` instead of :exc:`asyncio.TimeoutError`. + Waiting Primitives ================== From d0ef4f83e8bb981467220047691a8bc52da8f3af Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 13 Oct 2023 15:59:25 +0200 Subject: [PATCH 473/632] [3.11] Bump sphinx-lint to 0.7.0 (GH-110830) (#110834) Bump sphinx-lint to 0.7.0 (GH-110830) (cherry picked from commit 0ed2329a1627fc8ae97b009114cd960c25567f75) Co-authored-by: Alex Waygood --- .pre-commit-config.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1ae3dd71354e63..5d9deaa7ca8547 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,13 +17,12 @@ repos: types_or: [c, inc, python, rst] - repo: https://github.com/sphinx-contrib/sphinx-lint - rev: v0.6.8 + rev: v0.7.0 hooks: - id: sphinx-lint - args: [--enable=default-role] + args: [--enable=default-role, -j1] files: ^Doc/|^Misc/NEWS.d/next/ types: [rst] - require_serial: true - repo: meta hooks: From 2d615e1c260de8f3a414c20beecb4c5d238d1140 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 14 Oct 2023 08:28:52 +0200 Subject: [PATCH 474/632] [3.11] gh-101100: Fix sphinx warnings in `usage/cmdline.rst` (GH-110841) (#110856) Co-authored-by: Nikita Sobolev --- Doc/tools/.nitignore | 1 - Doc/using/cmdline.rst | 17 +++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 34975d5312f6e7..728d378a72ab5e 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -149,7 +149,6 @@ Doc/reference/import.rst Doc/reference/simple_stmts.rst Doc/tutorial/datastructures.rst Doc/tutorial/introduction.rst -Doc/using/cmdline.rst Doc/using/windows.rst Doc/whatsnew/2.0.rst Doc/whatsnew/2.1.rst diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst index 79aefdaf389de5..c1a6f50ca34979 100644 --- a/Doc/using/cmdline.rst +++ b/Doc/using/cmdline.rst @@ -103,7 +103,7 @@ source. :option:`-I` option can be used to run the script in isolated mode where :data:`sys.path` contains neither the current directory nor the user's - site-packages directory. All :envvar:`PYTHON*` environment variables are + site-packages directory. All ``PYTHON*`` environment variables are ignored, too. Many standard library modules contain code that is invoked on their execution @@ -161,7 +161,7 @@ source. :option:`-I` option can be used to run the script in isolated mode where :data:`sys.path` contains neither the script's directory nor the user's - site-packages directory. All :envvar:`PYTHON*` environment variables are + site-packages directory. All ``PYTHON*`` environment variables are ignored, too. .. audit-event:: cpython.run_file filename @@ -277,7 +277,7 @@ Miscellaneous options .. option:: -E - Ignore all :envvar:`PYTHON*` environment variables, e.g. + Ignore all ``PYTHON*`` environment variables, e.g. :envvar:`PYTHONPATH` and :envvar:`PYTHONHOME`, that might be set. See also the :option:`-P` and :option:`-I` (isolated) options. @@ -300,7 +300,7 @@ Miscellaneous options and :option:`-s` options. In isolated mode :data:`sys.path` contains neither the script's directory nor - the user's site-packages directory. All :envvar:`PYTHON*` environment + the user's site-packages directory. All ``PYTHON*`` environment variables are ignored, too. Further restrictions may be imposed to prevent the user from injecting malicious code. @@ -359,7 +359,7 @@ Miscellaneous options randomization is enabled by default. On previous versions of Python, this option turns on hash randomization, - so that the :meth:`__hash__` values of str and bytes objects + so that the :meth:`~object.__hash__` values of str and bytes objects are "salted" with an unpredictable random value. Although they remain constant within an individual Python process, they are not predictable between repeated invocations of Python. @@ -837,9 +837,10 @@ conflict. If this environment variable is set to a non-empty string, :func:`faulthandler.enable` is called at startup: install a handler for - :const:`SIGSEGV`, :const:`SIGFPE`, :const:`SIGABRT`, :const:`SIGBUS` and - :const:`SIGILL` signals to dump the Python traceback. This is equivalent to - :option:`-X` ``faulthandler`` option. + :const:`~signal.SIGSEGV`, :const:`~signal.SIGFPE`, + :const:`~signal.SIGABRT`, :const:`~signal.SIGBUS` and + :const:`~signal.SIGILL` signals to dump the Python traceback. + This is equivalent to :option:`-X` ``faulthandler`` option. .. versionadded:: 3.3 From 035f9e0cc5ecda552a7326186cabbcd4dda5408d Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 14 Oct 2023 08:40:18 +0200 Subject: [PATCH 475/632] [3.11] gh-107705: Fix file leak in test_tkinter in the C locale (GH-110507) (GH-110858) (cherry picked from commit ca0f3d858d069231ce7c5b382790a774f385b467) Co-authored-by: Serhiy Storchaka --- Lib/tkinter/test/test_tkinter/test_images.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Lib/tkinter/test/test_tkinter/test_images.py b/Lib/tkinter/test/test_tkinter/test_images.py index 5b3566a3611eb5..add9c8d6638e47 100644 --- a/Lib/tkinter/test/test_tkinter/test_images.py +++ b/Lib/tkinter/test/test_tkinter/test_images.py @@ -357,13 +357,18 @@ def test_get(self): self.assertRaises(tkinter.TclError, image.get, 15, 16) def test_write(self): + filename = os_helper.TESTFN + import locale + if locale.getlocale()[0] is None: + # Tcl uses Latin1 in the C locale + filename = os_helper.TESTFN_ASCII image = self.create() - self.addCleanup(os_helper.unlink, os_helper.TESTFN) + self.addCleanup(os_helper.unlink, filename) - image.write(os_helper.TESTFN) + image.write(filename) image2 = tkinter.PhotoImage('::img::test2', master=self.root, format='ppm', - file=os_helper.TESTFN) + file=filename) self.assertEqual(str(image2), '::img::test2') self.assertEqual(image2.type(), 'photo') self.assertEqual(image2.width(), 16) @@ -371,10 +376,10 @@ def test_write(self): self.assertEqual(image2.get(0, 0), image.get(0, 0)) self.assertEqual(image2.get(15, 8), image.get(15, 8)) - image.write(os_helper.TESTFN, format='gif', from_coords=(4, 6, 6, 9)) + image.write(filename, format='gif', from_coords=(4, 6, 6, 9)) image3 = tkinter.PhotoImage('::img::test3', master=self.root, format='gif', - file=os_helper.TESTFN) + file=filename) self.assertEqual(str(image3), '::img::test3') self.assertEqual(image3.type(), 'photo') self.assertEqual(image3.width(), 2) From 1c26f1ce6c3965b1f7395b60bf45d7fc0bc6de57 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 14 Oct 2023 08:51:24 +0200 Subject: [PATCH 476/632] [3.11] gh-109747: Improve errors for unsupported look-behind patterns (GH-109859) (GH-110860) Now re.error is raised instead of OverflowError or RuntimeError for too large width of look-behind pattern. The limit is increased to 2**32-1 (was 2**31-1). (cherry picked from commit e2b3d831fd2824d8a5713e3ed2a64aad0fb6b62d) Co-authored-by: Serhiy Storchaka --- Lib/re/_compiler.py | 4 +++- Lib/re/_parser.py | 13 ++++++++--- Lib/test/test_re.py | 23 +++++++++++++++++++ ...-09-25-20-05-41.gh-issue-109747._cRJH8.rst | 3 +++ Modules/_sre/sre.c | 2 -- Modules/_sre/sre_lib.h | 14 +++++------ 6 files changed, 46 insertions(+), 13 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-09-25-20-05-41.gh-issue-109747._cRJH8.rst diff --git a/Lib/re/_compiler.py b/Lib/re/_compiler.py index d8e0d2fdefdcca..285c21936f2cfa 100644 --- a/Lib/re/_compiler.py +++ b/Lib/re/_compiler.py @@ -149,6 +149,8 @@ def _compile(code, pattern, flags): emit(0) # look ahead else: lo, hi = av[1].getwidth() + if lo > MAXCODE: + raise error("looks too much behind") if lo != hi: raise error("look-behind requires fixed-width pattern") emit(lo) # look behind @@ -549,7 +551,7 @@ def _compile_info(code, pattern, flags): else: emit(MAXCODE) prefix = prefix[:MAXCODE] - emit(min(hi, MAXCODE)) + emit(hi) # add literal prefix if prefix: emit(len(prefix)) # length diff --git a/Lib/re/_parser.py b/Lib/re/_parser.py index 6b715f54032f91..c795a7fb00fc97 100644 --- a/Lib/re/_parser.py +++ b/Lib/re/_parser.py @@ -68,6 +68,10 @@ TYPE_FLAGS = SRE_FLAG_ASCII | SRE_FLAG_LOCALE | SRE_FLAG_UNICODE GLOBAL_FLAGS = SRE_FLAG_DEBUG | SRE_FLAG_TEMPLATE +# Maximal value returned by SubPattern.getwidth(). +# Must be larger than MAXREPEAT, MAXCODE and sys.maxsize. +MAXWIDTH = 1 << 64 + class State: # keeps track of state for parsing def __init__(self): @@ -178,7 +182,7 @@ def getwidth(self): lo = hi = 0 for op, av in self.data: if op is BRANCH: - i = MAXREPEAT - 1 + i = MAXWIDTH j = 0 for av in av[1]: l, h = av.getwidth() @@ -197,7 +201,10 @@ def getwidth(self): elif op in _REPEATCODES: i, j = av[2].getwidth() lo = lo + i * av[0] - hi = hi + j * av[1] + if av[1] == MAXREPEAT and j: + hi = MAXWIDTH + else: + hi = hi + j * av[1] elif op in _UNITCODES: lo = lo + 1 hi = hi + 1 @@ -217,7 +224,7 @@ def getwidth(self): hi = hi + j elif op is SUCCESS: break - self.width = min(lo, MAXREPEAT - 1), min(hi, MAXREPEAT) + self.width = min(lo, MAXWIDTH), min(hi, MAXWIDTH) return self.width class Tokenizer: diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index b41bcd49385453..3c02d005b49a2a 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -1830,6 +1830,29 @@ def test_repeat_minmax_overflow(self): self.assertRaises(OverflowError, re.compile, r".{%d,}?" % 2**128) self.assertRaises(OverflowError, re.compile, r".{%d,%d}" % (2**129, 2**128)) + def test_look_behind_overflow(self): + string = "x" * 2_500_000 + p1 = r"(?<=((.{%d}){%d}){%d})" + p2 = r"(?)', 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 new file mode 100644 index 00000000000000..b64ba627897a1a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-09-25-20-05-41.gh-issue-109747._cRJH8.rst @@ -0,0 +1,3 @@ +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/Modules/_sre/sre.c b/Modules/_sre/sre.c index 81629f0007e6a1..3bad9d8d0ffa28 100644 --- a/Modules/_sre/sre.c +++ b/Modules/_sre/sre.c @@ -1939,8 +1939,6 @@ _validate_inner(SRE_CODE *code, SRE_CODE *end, Py_ssize_t groups) GET_SKIP; GET_ARG; /* 0 for lookahead, width for lookbehind */ code--; /* Back up over arg to simplify math below */ - if (arg & 0x80000000) - FAIL; /* Width too large */ /* Stop 1 before the end; we check the SUCCESS below */ if (_validate_inner(code+1, code+skip-2, groups)) FAIL; diff --git a/Modules/_sre/sre_lib.h b/Modules/_sre/sre_lib.h index f8d556b2db85c0..95c1ada908d222 100644 --- a/Modules/_sre/sre_lib.h +++ b/Modules/_sre/sre_lib.h @@ -589,8 +589,8 @@ SRE(match)(SRE_STATE* state, const SRE_CODE* pattern, int toplevel) /* optimization info block */ /* <1=skip> <2=flags> <3=min> ... */ if (pattern[3] && (uintptr_t)(end - ptr) < pattern[3]) { - TRACE(("reject (got %zd chars, need %zd)\n", - end - ptr, (Py_ssize_t) pattern[3])); + TRACE(("reject (got %tu chars, need %zu)\n", + end - ptr, (size_t) pattern[3])); RETURN_FAILURE; } pattern += pattern[1] + 1; @@ -1507,7 +1507,7 @@ SRE(match)(SRE_STATE* state, const SRE_CODE* pattern, int toplevel) /* */ TRACE(("|%p|%p|ASSERT %d\n", pattern, ptr, pattern[1])); - if (ptr - (SRE_CHAR *)state->beginning < (Py_ssize_t)pattern[1]) + if ((uintptr_t)(ptr - (SRE_CHAR *)state->beginning) < pattern[1]) RETURN_FAILURE; state->ptr = ptr - pattern[1]; DO_JUMP0(JUMP_ASSERT, jump_assert, pattern+2); @@ -1520,7 +1520,7 @@ SRE(match)(SRE_STATE* state, const SRE_CODE* pattern, int toplevel) /* */ TRACE(("|%p|%p|ASSERT_NOT %d\n", pattern, ptr, pattern[1])); - if (ptr - (SRE_CHAR *)state->beginning >= (Py_ssize_t)pattern[1]) { + if ((uintptr_t)(ptr - (SRE_CHAR *)state->beginning) >= pattern[1]) { state->ptr = ptr - pattern[1]; LASTMARK_SAVE(); if (state->repeat) @@ -1655,9 +1655,9 @@ SRE(search)(SRE_STATE* state, SRE_CODE* pattern) flags = pattern[2]; - if (pattern[3] && end - ptr < (Py_ssize_t)pattern[3]) { - TRACE(("reject (got %u chars, need %u)\n", - (unsigned int)(end - ptr), pattern[3])); + if (pattern[3] && (uintptr_t)(end - ptr) < pattern[3]) { + TRACE(("reject (got %tu chars, need %zu)\n", + end - ptr, (size_t) pattern[3])); return 0; } if (pattern[3] > 1) { From 44558a9ba8f5097f4c0ee0c3b28423c4850fb4f1 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 14 Oct 2023 16:28:23 +0200 Subject: [PATCH 477/632] [3.11] gh-101100: Fix sphinx warnings in `library/time.rst` (GH-110862) (#110878) gh-101100: Fix sphinx warnings in `library/time.rst` (GH-110862) (cherry picked from commit 12deda763359d46d4eccbb8991afed71fa31a68b) Co-authored-by: Nikita Sobolev --- Doc/library/time.rst | 93 ++++++++++++++++++++++++++++---------------- Doc/tools/.nitignore | 1 - 2 files changed, 60 insertions(+), 34 deletions(-) diff --git a/Doc/library/time.rst b/Doc/library/time.rst index 9f23a6fc7d5341..93eceed29d2b5b 100644 --- a/Doc/library/time.rst +++ b/Doc/library/time.rst @@ -71,8 +71,8 @@ An explanation of some terminology and conventions is in order. * On the other hand, the precision of :func:`.time` and :func:`sleep` is better than their Unix equivalents: times are expressed as floating point numbers, :func:`.time` returns the most accurate time available (using Unix - :c:func:`gettimeofday` where available), and :func:`sleep` will accept a time - with a nonzero fraction (Unix :c:func:`select` is used to implement this, where + :c:func:`!gettimeofday` where available), and :func:`sleep` will accept a time + with a nonzero fraction (Unix :c:func:`!select` is used to implement this, where available). * The time value as returned by :func:`gmtime`, :func:`localtime`, and @@ -84,12 +84,14 @@ An explanation of some terminology and conventions is in order. See :class:`struct_time` for a description of these objects. .. versionchanged:: 3.3 - The :class:`struct_time` type was extended to provide the :attr:`tm_gmtoff` - and :attr:`tm_zone` attributes when platform supports corresponding + The :class:`struct_time` type was extended to provide + the :attr:`~struct_time.tm_gmtoff` and :attr:`~struct_time.tm_zone` + attributes when platform supports corresponding ``struct tm`` members. .. versionchanged:: 3.6 - The :class:`struct_time` attributes :attr:`tm_gmtoff` and :attr:`tm_zone` + The :class:`struct_time` attributes + :attr:`~struct_time.tm_gmtoff` and :attr:`~struct_time.tm_zone` are now available on all platforms. * Use the following functions to convert between time representations: @@ -496,6 +498,8 @@ Functions When used with the :func:`strptime` function, the ``%p`` directive only affects the output hour field if the ``%I`` directive is used to parse the hour. + .. _leap-second: + (2) The range really is ``0`` to ``61``; value ``60`` is valid in timestamps representing `leap seconds`_ and value ``61`` is supported @@ -566,32 +570,55 @@ Functions tuple` interface: values can be accessed by index and by attribute name. The following values are present: - +-------+-------------------+---------------------------------+ - | Index | Attribute | Values | - +=======+===================+=================================+ - | 0 | :attr:`tm_year` | (for example, 1993) | - +-------+-------------------+---------------------------------+ - | 1 | :attr:`tm_mon` | range [1, 12] | - +-------+-------------------+---------------------------------+ - | 2 | :attr:`tm_mday` | range [1, 31] | - +-------+-------------------+---------------------------------+ - | 3 | :attr:`tm_hour` | range [0, 23] | - +-------+-------------------+---------------------------------+ - | 4 | :attr:`tm_min` | range [0, 59] | - +-------+-------------------+---------------------------------+ - | 5 | :attr:`tm_sec` | range [0, 61]; see **(2)** in | - | | | :func:`strftime` description | - +-------+-------------------+---------------------------------+ - | 6 | :attr:`tm_wday` | range [0, 6], Monday is 0 | - +-------+-------------------+---------------------------------+ - | 7 | :attr:`tm_yday` | range [1, 366] | - +-------+-------------------+---------------------------------+ - | 8 | :attr:`tm_isdst` | 0, 1 or -1; see below | - +-------+-------------------+---------------------------------+ - | N/A | :attr:`tm_zone` | abbreviation of timezone name | - +-------+-------------------+---------------------------------+ - | N/A | :attr:`tm_gmtoff` | offset east of UTC in seconds | - +-------+-------------------+---------------------------------+ + .. list-table:: + + * - Index + - Attribute + - Values + + * - 0 + - .. attribute:: tm_year + - (for example, 1993) + + * - 1 + - .. attribute:: tm_mon + - range [1, 12] + + * - 2 + - .. attribute:: tm_day + - range [1, 31] + + * - 3 + - .. attribute:: tm_hour + - range [0, 23] + + * - 4 + - .. attribute:: tm_min + - range [0, 59] + + * - 5 + - .. attribute:: tm_sec + - range [0, 61]; see :ref:`Note (2) ` in :func:`strftime` + + * - 6 + - .. attribute:: tm_wday + - range [0, 6]; Monday is 0 + + * - 7 + - .. attribute:: tm_yday + - range [1, 366] + + * - 8 + - .. attribute:: tm_isdst + - 0, 1 or -1; see below + + * - N/A + - .. attribute:: tm_zone + - abbreviation of timezone name + + * - N/A + - .. attribute:: tm_gmtoff + - offset east of UTC in seconds Note that unlike the C structure, the month value is a range of [1, 12], not [0, 11]. @@ -912,8 +939,8 @@ Timezone Constants For the above Timezone constants (:data:`altzone`, :data:`daylight`, :data:`timezone`, and :data:`tzname`), the value is determined by the timezone rules in effect at module load time or the last time :func:`tzset` is called and may be incorrect - for times in the past. It is recommended to use the :attr:`tm_gmtoff` and - :attr:`tm_zone` results from :func:`localtime` to obtain timezone information. + for times in the past. It is recommended to use the :attr:`~struct_time.tm_gmtoff` and + :attr:`~struct_time.tm_zone` results from :func:`localtime` to obtain timezone information. .. seealso:: diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 728d378a72ab5e..0ff65ccb21d378 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -121,7 +121,6 @@ Doc/library/tarfile.rst Doc/library/tempfile.rst Doc/library/termios.rst Doc/library/test.rst -Doc/library/time.rst Doc/library/tkinter.rst Doc/library/tkinter.scrolledtext.rst Doc/library/tkinter.ttk.rst From 5c55f50a71008af5fe9ebf998722297a0591ed86 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 15 Oct 2023 11:32:26 +0300 Subject: [PATCH 478/632] [3.11] [3.12] bpo-42663: Fix parsing TZ strings in zoneinfo module (GH-23825) (GH-110882) (GH-110889) 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. (cherry picked from commit ab08ff7882b6181fb785eed7410dbf8030aded70) (cherry picked from commit 72b0f0eaf52ac46d5f6e165f737d6f891cf8d172) --- Lib/test/test_zoneinfo/test_zoneinfo.py | 125 +++++- Lib/zoneinfo/_zoneinfo.py | 86 ++-- ...2-05-06-15-49-57.gh-issue-86826.rf006W.rst | 4 + Modules/_zoneinfo.c | 371 +++++++----------- 4 files changed, 327 insertions(+), 259 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-05-06-15-49-57.gh-issue-86826.rf006W.rst diff --git a/Lib/test/test_zoneinfo/test_zoneinfo.py b/Lib/test/test_zoneinfo/test_zoneinfo.py index fc19247b262e01..0b6ab09eff357c 100644 --- a/Lib/test/test_zoneinfo/test_zoneinfo.py +++ b/Lib/test/test_zoneinfo/test_zoneinfo.py @@ -988,6 +988,80 @@ def test_tzstr_from_utc(self): self.assertEqual(dt_act, dt_utc) + def test_extreme_tzstr(self): + tzstrs = [ + # Extreme offset hour + "AAA24", + "AAA+24", + "AAA-24", + "AAA24BBB,J60/2,J300/2", + "AAA+24BBB,J60/2,J300/2", + "AAA-24BBB,J60/2,J300/2", + "AAA4BBB24,J60/2,J300/2", + "AAA4BBB+24,J60/2,J300/2", + "AAA4BBB-24,J60/2,J300/2", + # Extreme offset minutes + "AAA4:00BBB,J60/2,J300/2", + "AAA4:59BBB,J60/2,J300/2", + "AAA4BBB5:00,J60/2,J300/2", + "AAA4BBB5:59,J60/2,J300/2", + # Extreme offset seconds + "AAA4:00:00BBB,J60/2,J300/2", + "AAA4:00:59BBB,J60/2,J300/2", + "AAA4BBB5:00:00,J60/2,J300/2", + "AAA4BBB5:00:59,J60/2,J300/2", + # Extreme total offset + "AAA24:59:59BBB5,J60/2,J300/2", + "AAA-24:59:59BBB5,J60/2,J300/2", + "AAA4BBB24:59:59,J60/2,J300/2", + "AAA4BBB-24:59:59,J60/2,J300/2", + # Extreme months + "AAA4BBB,M12.1.1/2,M1.1.1/2", + "AAA4BBB,M1.1.1/2,M12.1.1/2", + # Extreme weeks + "AAA4BBB,M1.5.1/2,M1.1.1/2", + "AAA4BBB,M1.1.1/2,M1.5.1/2", + # Extreme weekday + "AAA4BBB,M1.1.6/2,M2.1.1/2", + "AAA4BBB,M1.1.1/2,M2.1.6/2", + # Extreme numeric offset + "AAA4BBB,0/2,20/2", + "AAA4BBB,0/2,0/14", + "AAA4BBB,20/2,365/2", + "AAA4BBB,365/2,365/14", + # Extreme julian offset + "AAA4BBB,J1/2,J20/2", + "AAA4BBB,J1/2,J1/14", + "AAA4BBB,J20/2,J365/2", + "AAA4BBB,J365/2,J365/14", + # Extreme transition hour + "AAA4BBB,J60/167,J300/2", + "AAA4BBB,J60/+167,J300/2", + "AAA4BBB,J60/-167,J300/2", + "AAA4BBB,J60/2,J300/167", + "AAA4BBB,J60/2,J300/+167", + "AAA4BBB,J60/2,J300/-167", + # Extreme transition minutes + "AAA4BBB,J60/2:00,J300/2", + "AAA4BBB,J60/2:59,J300/2", + "AAA4BBB,J60/2,J300/2:00", + "AAA4BBB,J60/2,J300/2:59", + # Extreme transition seconds + "AAA4BBB,J60/2:00:00,J300/2", + "AAA4BBB,J60/2:00:59,J300/2", + "AAA4BBB,J60/2,J300/2:00:00", + "AAA4BBB,J60/2,J300/2:00:59", + # Extreme total transition time + "AAA4BBB,J60/167:59:59,J300/2", + "AAA4BBB,J60/-167:59:59,J300/2", + "AAA4BBB,J60/2,J300/167:59:59", + "AAA4BBB,J60/2,J300/-167:59:59", + ] + + for tzstr in tzstrs: + with self.subTest(tzstr=tzstr): + self.zone_from_tzstr(tzstr) + def test_invalid_tzstr(self): invalid_tzstrs = [ "PST8PDT", # DST but no transition specified @@ -995,16 +1069,33 @@ def test_invalid_tzstr(self): "GMT,M3.2.0/2,M11.1.0/3", # Transition rule but no DST "GMT0+11,M3.2.0/2,M11.1.0/3", # Unquoted alphanumeric in DST "PST8PDT,M3.2.0/2", # Only one transition rule - # Invalid offsets - "STD+25", - "STD-25", - "STD+374", - "STD+374DST,M3.2.0/2,M11.1.0/3", - "STD+23DST+25,M3.2.0/2,M11.1.0/3", - "STD-23DST-25,M3.2.0/2,M11.1.0/3", + # Invalid offset hours + "AAA168", + "AAA+168", + "AAA-168", + "AAA168BBB,J60/2,J300/2", + "AAA+168BBB,J60/2,J300/2", + "AAA-168BBB,J60/2,J300/2", + "AAA4BBB168,J60/2,J300/2", + "AAA4BBB+168,J60/2,J300/2", + "AAA4BBB-168,J60/2,J300/2", + # Invalid offset minutes + "AAA4:0BBB,J60/2,J300/2", + "AAA4:100BBB,J60/2,J300/2", + "AAA4BBB5:0,J60/2,J300/2", + "AAA4BBB5:100,J60/2,J300/2", + # Invalid offset seconds + "AAA4:00:0BBB,J60/2,J300/2", + "AAA4:00:100BBB,J60/2,J300/2", + "AAA4BBB5:00:0,J60/2,J300/2", + "AAA4BBB5:00:100,J60/2,J300/2", # Completely invalid dates "AAA4BBB,M1443339,M11.1.0/3", "AAA4BBB,M3.2.0/2,0349309483959c", + "AAA4BBB,,J300/2", + "AAA4BBB,z,J300/2", + "AAA4BBB,J60/2,", + "AAA4BBB,J60/2,z", # Invalid months "AAA4BBB,M13.1.1/2,M1.1.1/2", "AAA4BBB,M1.1.1/2,M13.1.1/2", @@ -1024,6 +1115,26 @@ def test_invalid_tzstr(self): # Invalid julian offset "AAA4BBB,J0/2,J20/2", "AAA4BBB,J20/2,J366/2", + # Invalid transition time + "AAA4BBB,J60/2/3,J300/2", + "AAA4BBB,J60/2,J300/2/3", + # Invalid transition hour + "AAA4BBB,J60/168,J300/2", + "AAA4BBB,J60/+168,J300/2", + "AAA4BBB,J60/-168,J300/2", + "AAA4BBB,J60/2,J300/168", + "AAA4BBB,J60/2,J300/+168", + "AAA4BBB,J60/2,J300/-168", + # Invalid transition minutes + "AAA4BBB,J60/2:0,J300/2", + "AAA4BBB,J60/2:100,J300/2", + "AAA4BBB,J60/2,J300/2:0", + "AAA4BBB,J60/2,J300/2:100", + # Invalid transition seconds + "AAA4BBB,J60/2:00:0,J300/2", + "AAA4BBB,J60/2:00:100,J300/2", + "AAA4BBB,J60/2,J300/2:00:0", + "AAA4BBB,J60/2,J300/2:00:100", ] for invalid_tzstr in invalid_tzstrs: diff --git a/Lib/zoneinfo/_zoneinfo.py b/Lib/zoneinfo/_zoneinfo.py index eede15b8271058..b77dc0ed391bb3 100644 --- a/Lib/zoneinfo/_zoneinfo.py +++ b/Lib/zoneinfo/_zoneinfo.py @@ -517,8 +517,8 @@ class _DayOffset: __slots__ = ["d", "julian", "hour", "minute", "second"] def __init__(self, d, julian, hour=2, minute=0, second=0): - if not (0 + julian) <= d <= 365: - min_day = 0 + julian + min_day = 0 + julian # convert bool to int + if not min_day <= d <= 365: raise ValueError(f"d must be in [{min_day}, 365], not: {d}") self.d = d @@ -560,11 +560,11 @@ class _CalendarOffset: ) def __init__(self, m, w, d, hour=2, minute=0, second=0): - if not 0 < m <= 12: - raise ValueError("m must be in (0, 12]") + if not 1 <= m <= 12: + raise ValueError("m must be in [1, 12]") - if not 0 < w <= 5: - raise ValueError("w must be in (0, 5]") + if not 1 <= w <= 5: + raise ValueError("w must be in [1, 5]") if not 0 <= d <= 6: raise ValueError("d must be in [0, 6]") @@ -634,18 +634,21 @@ def _parse_tz_str(tz_str): offset_str, *start_end_str = tz_str.split(",", 1) - # fmt: off parser_re = re.compile( - r"(?P[^<0-9:.+-]+|<[a-zA-Z0-9+\-]+>)" + - r"((?P[+-]?\d{1,2}(:\d{2}(:\d{2})?)?)" + - r"((?P[^0-9:.+-]+|<[a-zA-Z0-9+\-]+>)" + - r"((?P[+-]?\d{1,2}(:\d{2}(:\d{2})?)?))?" + - r")?" + # dst - r")?$" # stdoff + r""" + (?P[^<0-9:.+-]+|<[a-zA-Z0-9+-]+>) + (?: + (?P[+-]?\d{1,3}(?::\d{2}(?::\d{2})?)?) + (?: + (?P[^0-9:.+-]+|<[a-zA-Z0-9+-]+>) + (?P[+-]?\d{1,3}(?::\d{2}(?::\d{2})?)?)? + )? # dst + )? # stdoff + """, + re.ASCII|re.VERBOSE ) - # fmt: on - m = parser_re.match(offset_str) + m = parser_re.fullmatch(offset_str) if m is None: raise ValueError(f"{tz_str} is not a valid TZ string") @@ -696,16 +699,17 @@ def _parse_tz_str(tz_str): def _parse_dst_start_end(dststr): - date, *time = dststr.split("/") - if date[0] == "M": + date, *time = dststr.split("/", 1) + type = date[:1] + if type == "M": n_is_julian = False - m = re.match(r"M(\d{1,2})\.(\d).(\d)$", date) + m = re.fullmatch(r"M(\d{1,2})\.(\d).(\d)", date, re.ASCII) if m is None: raise ValueError(f"Invalid dst start/end date: {dststr}") date_offset = tuple(map(int, m.groups())) offset = _CalendarOffset(*date_offset) else: - if date[0] == "J": + if type == "J": n_is_julian = True date = date[1:] else: @@ -715,38 +719,54 @@ def _parse_dst_start_end(dststr): offset = _DayOffset(doy, n_is_julian) if time: - time_components = list(map(int, time[0].split(":"))) - n_components = len(time_components) - if n_components < 3: - time_components.extend([0] * (3 - n_components)) - offset.hour, offset.minute, offset.second = time_components + offset.hour, offset.minute, offset.second = _parse_transition_time(time[0]) return offset +def _parse_transition_time(time_str): + match = re.fullmatch( + r"(?P[+-])?(?P\d{1,3})(:(?P\d{2})(:(?P\d{2}))?)?", + time_str, + re.ASCII + ) + if match is None: + raise ValueError(f"Invalid time: {time_str}") + + h, m, s = (int(v or 0) for v in match.group("h", "m", "s")) + + if h > 167: + raise ValueError( + f"Hour must be in [0, 167]: {time_str}" + ) + + if match.group("sign") == "-": + h, m, s = -h, -m, -s + + return h, m, s + + def _parse_tz_delta(tz_delta): - match = re.match( - r"(?P[+-])?(?P\d{1,2})(:(?P\d{2})(:(?P\d{2}))?)?", + match = re.fullmatch( + r"(?P[+-])?(?P\d{1,3})(:(?P\d{2})(:(?P\d{2}))?)?", tz_delta, + re.ASCII ) # Anything passed to this function should already have hit an equivalent # regular expression to find the section to parse. assert match is not None, tz_delta - h, m, s = ( - int(v) if v is not None else 0 - for v in map(match.group, ("h", "m", "s")) - ) + h, m, s = (int(v or 0) for v in match.group("h", "m", "s")) total = h * 3600 + m * 60 + s - if not -86400 < total < 86400: + if h > 24: raise ValueError( - f"Offset must be strictly between -24h and +24h: {tz_delta}" + f"Offset hours must be in [0, 24]: {tz_delta}" ) # Yes, +5 maps to an offset of -5h if match.group("sign") != "-": - total *= -1 + total = -total return total 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 new file mode 100644 index 00000000000000..02cd75eec4be9e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-05-06-15-49-57.gh-issue-86826.rf006W.rst @@ -0,0 +1,4 @@ +: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/Modules/_zoneinfo.c b/Modules/_zoneinfo.c index 5f584ff93e9a59..e2ed67c026c2a5 100644 --- a/Modules/_zoneinfo.c +++ b/Modules/_zoneinfo.c @@ -59,21 +59,21 @@ struct TransitionRuleType { typedef struct { TransitionRuleType base; - uint8_t month; - uint8_t week; - uint8_t day; - int8_t hour; - int8_t minute; - int8_t second; + uint8_t month; /* 1 - 12 */ + uint8_t week; /* 1 - 5 */ + uint8_t day; /* 0 - 6 */ + int16_t hour; /* -167 - 167, RFC 8536 §3.3.1 */ + int8_t minute; /* signed 2 digits */ + int8_t second; /* signed 2 digits */ } CalendarRule; typedef struct { TransitionRuleType base; - uint8_t julian; - unsigned int day; - int8_t hour; - int8_t minute; - int8_t second; + uint8_t julian; /* 0, 1 */ + uint16_t day; /* 0 - 365 */ + int16_t hour; /* -167 - 167, RFC 8536 §3.3.1 */ + int8_t minute; /* signed 2 digits */ + int8_t second; /* signed 2 digits */ } DayRule; struct StrongCacheNode { @@ -122,15 +122,14 @@ ts_to_local(size_t *trans_idx, int64_t *trans_utc, long *utcoff, static int parse_tz_str(PyObject *tz_str_obj, _tzrule *out); -static Py_ssize_t -parse_abbr(const char *const p, PyObject **abbr); -static Py_ssize_t -parse_tz_delta(const char *const p, long *total_seconds); -static Py_ssize_t -parse_transition_time(const char *const p, int8_t *hour, int8_t *minute, - int8_t *second); -static Py_ssize_t -parse_transition_rule(const char *const p, TransitionRuleType **out); +static int +parse_abbr(const char **p, PyObject **abbr); +static int +parse_tz_delta(const char **p, long *total_seconds); +static int +parse_transition_time(const char **p, int *hour, int *minute, int *second); +static int +parse_transition_rule(const char **p, TransitionRuleType **out); static _ttinfo * find_tzrule_ttinfo(_tzrule *rule, int64_t ts, unsigned char fold, int year); @@ -1212,14 +1211,14 @@ calendarrule_year_to_timestamp(TransitionRuleType *base_self, int year) } int64_t ordinal = ymd_to_ord(year, self->month, month_day) - EPOCHORDINAL; - return ((ordinal * 86400) + (int64_t)(self->hour * 3600) + - (int64_t)(self->minute * 60) + (int64_t)(self->second)); + return ordinal * 86400 + (int64_t)self->hour * 3600 + + (int64_t)self->minute * 60 + self->second; } /* Constructor for CalendarRule. */ int -calendarrule_new(uint8_t month, uint8_t week, uint8_t day, int8_t hour, - int8_t minute, int8_t second, CalendarRule *out) +calendarrule_new(int month, int week, int day, int hour, + int minute, int second, CalendarRule *out) { // These bounds come from the POSIX standard, which describes an Mm.n.d // rule as: @@ -1228,33 +1227,36 @@ calendarrule_new(uint8_t month, uint8_t week, uint8_t day, int8_t hour, // 5, 1 <= m <= 12, where week 5 means "the last d day in month m" which // may occur in either the fourth or the fifth week). Week 1 is the first // week in which the d'th day occurs. Day zero is Sunday. - if (month <= 0 || month > 12) { - PyErr_Format(PyExc_ValueError, "Month must be in (0, 12]"); + if (month < 1 || month > 12) { + PyErr_Format(PyExc_ValueError, "Month must be in [1, 12]"); return -1; } - if (week <= 0 || week > 5) { - PyErr_Format(PyExc_ValueError, "Week must be in (0, 5]"); + if (week < 1 || week > 5) { + PyErr_Format(PyExc_ValueError, "Week must be in [1, 5]"); return -1; } - // If the 'day' parameter type is changed to a signed type, - // "day < 0" check must be added. - if (/* day < 0 || */ day > 6) { + if (day < 0 || day > 6) { PyErr_Format(PyExc_ValueError, "Day must be in [0, 6]"); return -1; } + if (hour < -167 || hour > 167) { + PyErr_Format(PyExc_ValueError, "Hour must be in [0, 167]"); + return -1; + } + TransitionRuleType base = {&calendarrule_year_to_timestamp}; CalendarRule new_offset = { .base = base, - .month = month, - .week = week, - .day = day, - .hour = hour, - .minute = minute, - .second = second, + .month = (uint8_t)month, + .week = (uint8_t)week, + .day = (uint8_t)day, + .hour = (int16_t)hour, + .minute = (int8_t)minute, + .second = (int8_t)second, }; *out = new_offset; @@ -1294,40 +1296,45 @@ dayrule_year_to_timestamp(TransitionRuleType *base_self, int year) // always transitions on a given calendar day (other than February 29th), // you would use a Julian day, e.g. J91 always refers to April 1st and J365 // always refers to December 31st. - unsigned int day = self->day; + uint16_t day = self->day; if (self->julian && day >= 59 && is_leap_year(year)) { day += 1; } - return ((days_before_year + day) * 86400) + (self->hour * 3600) + - (self->minute * 60) + self->second; + return (days_before_year + day) * 86400 + (int64_t)self->hour * 3600 + + (int64_t)self->minute * 60 + self->second; } /* Constructor for DayRule. */ static int -dayrule_new(uint8_t julian, unsigned int day, int8_t hour, int8_t minute, - int8_t second, DayRule *out) +dayrule_new(int julian, int day, int hour, int minute, + int second, DayRule *out) { // The POSIX standard specifies that Julian days must be in the range (1 <= // n <= 365) and that non-Julian (they call it "0-based Julian") days must // be in the range (0 <= n <= 365). if (day < julian || day > 365) { - PyErr_Format(PyExc_ValueError, "day must be in [%u, 365], not: %u", + PyErr_Format(PyExc_ValueError, "day must be in [%d, 365], not: %d", julian, day); return -1; } + if (hour < -167 || hour > 167) { + PyErr_Format(PyExc_ValueError, "Hour must be in [0, 167]"); + return -1; + } + TransitionRuleType base = { &dayrule_year_to_timestamp, }; DayRule tmp = { .base = base, - .julian = julian, - .day = day, - .hour = hour, - .minute = minute, - .second = second, + .julian = (uint8_t)julian, + .day = (int16_t)day, + .hour = (int16_t)hour, + .minute = (int8_t)minute, + .second = (int8_t)second, }; *out = tmp; @@ -1484,21 +1491,18 @@ parse_tz_str(PyObject *tz_str_obj, _tzrule *out) const char *p = tz_str; // Read the `std` abbreviation, which must be at least 3 characters long. - Py_ssize_t num_chars = parse_abbr(p, &std_abbr); - if (num_chars < 1) { - PyErr_Format(PyExc_ValueError, "Invalid STD format in %R", tz_str_obj); + if (parse_abbr(&p, &std_abbr)) { + if (!PyErr_Occurred()) { + PyErr_Format(PyExc_ValueError, "Invalid STD format in %R", tz_str_obj); + } goto error; } - p += num_chars; - // Now read the STD offset, which is required - num_chars = parse_tz_delta(p, &std_offset); - if (num_chars < 0) { + if (parse_tz_delta(&p, &std_offset)) { PyErr_Format(PyExc_ValueError, "Invalid STD offset in %R", tz_str_obj); goto error; } - p += num_chars; // If the string ends here, there is no DST, otherwise we must parse the // DST abbreviation and start and end dates and times. @@ -1506,12 +1510,12 @@ parse_tz_str(PyObject *tz_str_obj, _tzrule *out) goto complete; } - num_chars = parse_abbr(p, &dst_abbr); - if (num_chars < 1) { - PyErr_Format(PyExc_ValueError, "Invalid DST format in %R", tz_str_obj); + if (parse_abbr(&p, &dst_abbr)) { + if (!PyErr_Occurred()) { + PyErr_Format(PyExc_ValueError, "Invalid DST format in %R", tz_str_obj); + } goto error; } - p += num_chars; if (*p == ',') { // From the POSIX standard: @@ -1521,14 +1525,11 @@ parse_tz_str(PyObject *tz_str_obj, _tzrule *out) dst_offset = std_offset + 3600; } else { - num_chars = parse_tz_delta(p, &dst_offset); - if (num_chars < 0) { + if (parse_tz_delta(&p, &dst_offset)) { PyErr_Format(PyExc_ValueError, "Invalid DST offset in %R", tz_str_obj); goto error; } - - p += num_chars; } TransitionRuleType **transitions[2] = {&start, &end}; @@ -1541,14 +1542,12 @@ parse_tz_str(PyObject *tz_str_obj, _tzrule *out) } p++; - num_chars = parse_transition_rule(p, transitions[i]); - if (num_chars < 0) { + if (parse_transition_rule(&p, transitions[i])) { PyErr_Format(PyExc_ValueError, "Malformed transition rule in TZ string: %R", tz_str_obj); goto error; } - p += num_chars; } if (*p != '\0') { @@ -1582,26 +1581,30 @@ parse_tz_str(PyObject *tz_str_obj, _tzrule *out) } static int -parse_uint(const char *const p, uint8_t *value) +parse_digits(const char **p, int min, int max, int *value) { - if (!isdigit(*p)) { - return -1; + assert(max <= 3); + *value = 0; + for (int i = 0; i < max; i++, (*p)++) { + if (!Py_ISDIGIT(**p)) { + return (i < min) ? -1 : 0; + } + *value *= 10; + *value += (**p) - '0'; } - - *value = (*p) - '0'; return 0; } /* Parse the STD and DST abbreviations from a TZ string. */ -static Py_ssize_t -parse_abbr(const char *const p, PyObject **abbr) +static int +parse_abbr(const char **p, PyObject **abbr) { - const char *ptr = p; - char buff = *ptr; + const char *ptr = *p; const char *str_start; const char *str_end; if (*ptr == '<') { + char buff; ptr++; str_start = ptr; while ((buff = *ptr) != '>') { @@ -1625,7 +1628,7 @@ parse_abbr(const char *const p, PyObject **abbr) ptr++; } else { - str_start = p; + str_start = ptr; // From the POSIX standard: // // In the unquoted form, all characters in these fields shall be @@ -1635,6 +1638,9 @@ parse_abbr(const char *const p, PyObject **abbr) ptr++; } str_end = ptr; + if (str_end == str_start) { + return -1; + } } *abbr = PyUnicode_FromStringAndSize(str_start, str_end - str_start); @@ -1642,12 +1648,13 @@ parse_abbr(const char *const p, PyObject **abbr) return -1; } - return ptr - p; + *p = ptr; + return 0; } /* Parse a UTC offset from a TZ str. */ -static Py_ssize_t -parse_tz_delta(const char *const p, long *total_seconds) +static int +parse_tz_delta(const char **p, long *total_seconds) { // From the POSIX spec: // @@ -1662,75 +1669,30 @@ parse_tz_delta(const char *const p, long *total_seconds) // The POSIX spec says that the values for `hour` must be between 0 and 24 // hours, but RFC 8536 §3.3.1 specifies that the hours part of the // transition times may be signed and range from -167 to 167. - long sign = -1; - long hours = 0; - long minutes = 0; - long seconds = 0; - - const char *ptr = p; - char buff = *ptr; - if (buff == '-' || buff == '+') { - // Negative numbers correspond to *positive* offsets, from the spec: - // - // If preceded by a '-', the timezone shall be east of the Prime - // Meridian; otherwise, it shall be west (which may be indicated by - // an optional preceding '+' ). - if (buff == '-') { - sign = 1; - } - - ptr++; - } - - // The hour can be 1 or 2 numeric characters - for (size_t i = 0; i < 2; ++i) { - buff = *ptr; - if (!isdigit(buff)) { - if (i == 0) { - return -1; - } - else { - break; - } - } + int hours = 0; + int minutes = 0; + int seconds = 0; - hours *= 10; - hours += buff - '0'; - ptr++; - } - - if (hours > 24 || hours < 0) { + if (parse_transition_time(p, &hours, &minutes, &seconds)) { return -1; } - // Minutes and seconds always of the format ":dd" - long *outputs[2] = {&minutes, &seconds}; - for (size_t i = 0; i < 2; ++i) { - if (*ptr != ':') { - goto complete; - } - ptr++; - - for (size_t j = 0; j < 2; ++j) { - buff = *ptr; - if (!isdigit(buff)) { - return -1; - } - *(outputs[i]) *= 10; - *(outputs[i]) += buff - '0'; - ptr++; - } + if (hours > 24 || hours < -24) { + return -1; } -complete: - *total_seconds = sign * ((hours * 3600) + (minutes * 60) + seconds); - - return ptr - p; + // Negative numbers correspond to *positive* offsets, from the spec: + // + // If preceded by a '-', the timezone shall be east of the Prime + // Meridian; otherwise, it shall be west (which may be indicated by + // an optional preceding '+' ). + *total_seconds = -((hours * 3600L) + (minutes * 60) + seconds); + return 0; } /* Parse the date portion of a transition rule. */ -static Py_ssize_t -parse_transition_rule(const char *const p, TransitionRuleType **out) +static int +parse_transition_rule(const char **p, TransitionRuleType **out) { // The full transition rule indicates when to change back and forth between // STD and DST, and has the form: @@ -1742,10 +1704,10 @@ parse_transition_rule(const char *const p, TransitionRuleType **out) // does not include the ',' at the end of the first rule. // // The POSIX spec states that if *time* is not given, the default is 02:00. - const char *ptr = p; - int8_t hour = 2; - int8_t minute = 0; - int8_t second = 0; + const char *ptr = *p; + int hour = 2; + int minute = 0; + int second = 0; // Rules come in one of three flavors: // @@ -1754,44 +1716,30 @@ parse_transition_rule(const char *const p, TransitionRuleType **out) // 3. Mm.n.d: Specifying by month, week and day-of-week. if (*ptr == 'M') { - uint8_t month, week, day; + int month, week, day; ptr++; - if (parse_uint(ptr, &month)) { + + if (parse_digits(&ptr, 1, 2, &month)) { return -1; } - ptr++; - if (*ptr != '.') { - uint8_t tmp; - if (parse_uint(ptr, &tmp)) { - return -1; - } - - month *= 10; - month += tmp; - ptr++; + if (*ptr++ != '.') { + return -1; } - - uint8_t *values[2] = {&week, &day}; - for (size_t i = 0; i < 2; ++i) { - if (*ptr != '.') { - return -1; - } - ptr++; - - if (parse_uint(ptr, values[i])) { - return -1; - } - ptr++; + if (parse_digits(&ptr, 1, 1, &week)) { + return -1; + } + if (*ptr++ != '.') { + return -1; + } + if (parse_digits(&ptr, 1, 1, &day)) { + return -1; } if (*ptr == '/') { ptr++; - Py_ssize_t num_chars = - parse_transition_time(ptr, &hour, &minute, &second); - if (num_chars < 0) { + if (parse_transition_time(&ptr, &hour, &minute, &second)) { return -1; } - ptr += num_chars; } CalendarRule *rv = PyMem_Calloc(1, sizeof(CalendarRule)); @@ -1807,33 +1755,22 @@ parse_transition_rule(const char *const p, TransitionRuleType **out) *out = (TransitionRuleType *)rv; } else { - uint8_t julian = 0; - unsigned int day = 0; + int julian = 0; + int day = 0; if (*ptr == 'J') { julian = 1; ptr++; } - for (size_t i = 0; i < 3; ++i) { - if (!isdigit(*ptr)) { - if (i == 0) { - return -1; - } - break; - } - day *= 10; - day += (*ptr) - '0'; - ptr++; + if (parse_digits(&ptr, 1, 3, &day)) { + return -1; } if (*ptr == '/') { ptr++; - Py_ssize_t num_chars = - parse_transition_time(ptr, &hour, &minute, &second); - if (num_chars < 0) { + if (parse_transition_time(&ptr, &hour, &minute, &second)) { return -1; } - ptr += num_chars; } DayRule *rv = PyMem_Calloc(1, sizeof(DayRule)); @@ -1848,13 +1785,13 @@ parse_transition_rule(const char *const p, TransitionRuleType **out) *out = (TransitionRuleType *)rv; } - return ptr - p; + *p = ptr; + return 0; } /* Parse the time portion of a transition rule (e.g. following an /) */ -static Py_ssize_t -parse_transition_time(const char *const p, int8_t *hour, int8_t *minute, - int8_t *second) +static int +parse_transition_time(const char **p, int *hour, int *minute, int *second) { // From the spec: // @@ -1866,12 +1803,9 @@ parse_transition_time(const char *const p, int8_t *hour, int8_t *minute, // h[h][:mm[:ss]] // // RFC 8536 also allows transition times to be signed and to range from - // -167 to +167, but the current version only supports [0, 99]. - // - // TODO: Support the full range of transition hours. - int8_t *components[3] = {hour, minute, second}; - const char *ptr = p; - int8_t sign = 1; + // -167 to +167. + const char *ptr = *p; + int sign = 1; if (*ptr == '-' || *ptr == '+') { if (*ptr == '-') { @@ -1880,32 +1814,31 @@ parse_transition_time(const char *const p, int8_t *hour, int8_t *minute, ptr++; } - for (size_t i = 0; i < 3; ++i) { - if (i > 0) { - if (*ptr != ':') { - break; - } - ptr++; + // The hour can be 1 to 3 numeric characters + if (parse_digits(&ptr, 1, 3, hour)) { + return -1; + } + *hour *= sign; + + // Minutes and seconds always of the format ":dd" + if (*ptr == ':') { + ptr++; + if (parse_digits(&ptr, 2, 2, minute)) { + return -1; } + *minute *= sign; - uint8_t buff = 0; - for (size_t j = 0; j < 2; j++) { - if (!isdigit(*ptr)) { - if (i == 0 && j > 0) { - break; - } + if (*ptr == ':') { + ptr++; + if (parse_digits(&ptr, 2, 2, second)) { return -1; } - - buff *= 10; - buff += (*ptr) - '0'; - ptr++; + *second *= sign; } - - *(components[i]) = sign * buff; } - return ptr - p; + *p = ptr; + return 0; } /* Constructor for a _tzrule. @@ -2260,8 +2193,8 @@ get_local_timestamp(PyObject *dt, int64_t *local_ts) } } - *local_ts = (int64_t)(ord - EPOCHORDINAL) * 86400 + - (int64_t)(hour * 3600 + minute * 60 + second); + *local_ts = (int64_t)(ord - EPOCHORDINAL) * 86400L + + (int64_t)(hour * 3600L + minute * 60 + second); return 0; } From fd1b314cd3fc58e9155306e2d907492ed272c567 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Sun, 15 Oct 2023 07:51:13 -0700 Subject: [PATCH 479/632] [3.11] remove redundant call to attach_loop in watcher (GH-110847) (#110870) (cherry picked from commit 596589104fe5a4d90cb145b2cc69b71cc9aa9f07) --- Lib/asyncio/unix_events.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Lib/asyncio/unix_events.py b/Lib/asyncio/unix_events.py index ac4519acc4307b..0d4ba72603e675 100644 --- a/Lib/asyncio/unix_events.py +++ b/Lib/asyncio/unix_events.py @@ -1443,8 +1443,6 @@ def _init_watcher(self): with events._lock: if self._watcher is None: # pragma: no branch self._watcher = ThreadedChildWatcher() - if threading.current_thread() is threading.main_thread(): - self._watcher.attach_loop(self._local._loop) def set_event_loop(self, loop): """Set the event loop. From e60abeea30c004cfe0dd619554e013ad76cf1dc2 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 15 Oct 2023 19:00:57 +0200 Subject: [PATCH 480/632] [3.11] gh-110886 Doc: add a link to BNF Wikipedia article (GH-110887) (#110901) Co-authored-by: partev Co-authored-by: Hugo van Kemenade --- Doc/reference/introduction.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/reference/introduction.rst b/Doc/reference/introduction.rst index 81f0a5c5d43883..cf186705e6e987 100644 --- a/Doc/reference/introduction.rst +++ b/Doc/reference/introduction.rst @@ -90,7 +90,8 @@ Notation .. index:: BNF, grammar, syntax, notation -The descriptions of lexical analysis and syntax use a modified BNF grammar +The descriptions of lexical analysis and syntax use a modified +`Backus–Naur form (BNF) `_ grammar notation. This uses the following style of definition: .. productionlist:: notation From 616862d58e41da4493619d8dac8b3dc372a1d789 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 16 Oct 2023 15:15:07 +0200 Subject: [PATCH 481/632] [3.11] gh-110527: Improve `PySet_Clear` docs (GH-110528) (#110927) gh-110527: Improve `PySet_Clear` docs (GH-110528) (cherry picked from commit bfc1cd8145db00df23fbbd2ed95324bb96c0b25b) Co-authored-by: Nikita Sobolev --- Doc/c-api/set.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Doc/c-api/set.rst b/Doc/c-api/set.rst index 1e8a09509032f5..09c0fb6b9c5f23 100644 --- a/Doc/c-api/set.rst +++ b/Doc/c-api/set.rst @@ -163,4 +163,6 @@ subtypes but not for instances of :class:`frozenset` or its subtypes. .. c:function:: int PySet_Clear(PyObject *set) - Empty an existing set of all elements. + Empty an existing set of all elements. Return ``0`` on + success. Return ``-1`` and raise :exc:`SystemError` if *set* is not an instance of + :class:`set` or its subtype. From 6502a130e46a24267b9655e69e2300243c510dec Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 16 Oct 2023 16:55:52 +0200 Subject: [PATCH 482/632] [3.11] regrtest: Prepend 'use' options in --{fast,slow}-ci (GH-110363) (#110924) regrtest: Prepend 'use' options in --{fast,slow}-ci (GH-110363) This allows individual resources to be disabled without having to explicitly re-enable all others. (cherry picked from commit b75186f69edcf54615910a5cd707996144163ef7) Co-authored-by: Zachary Ware --- Lib/test/libregrtest/cmdline.py | 10 ++++++---- Lib/test/test_regrtest.py | 8 +++++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Lib/test/libregrtest/cmdline.py b/Lib/test/libregrtest/cmdline.py index dd4cd335bef7e3..b1b00893402d8e 100644 --- a/Lib/test/libregrtest/cmdline.py +++ b/Lib/test/libregrtest/cmdline.py @@ -418,14 +418,16 @@ def _parse_args(args, **kwargs): # --slow-ci has the priority if ns.slow_ci: # Similar to: -u "all" --timeout=1200 - if not ns.use: - ns.use = [['all']] + if ns.use is None: + ns.use = [] + ns.use.insert(0, ['all']) if ns.timeout is None: ns.timeout = 1200 # 20 minutes elif ns.fast_ci: # Similar to: -u "all,-cpu" --timeout=600 - if not ns.use: - ns.use = [['all', '-cpu']] + if ns.use is None: + ns.use = [] + ns.use.insert(0, ['all', '-cpu']) if ns.timeout is None: ns.timeout = 600 # 10 minutes diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py index 45573a2719c543..c8e182397c835e 100644 --- a/Lib/test/test_regrtest.py +++ b/Lib/test/test_regrtest.py @@ -416,9 +416,11 @@ def test_fast_ci_python_cmd(self): self.assertEqual(regrtest.python_cmd, ('python', '-X', 'dev')) def test_fast_ci_resource(self): - # it should be possible to override resources - args = ['--fast-ci', '-u', 'network'] - use_resources = ['network'] + # it should be possible to override resources individually + args = ['--fast-ci', '-u-network'] + use_resources = sorted(cmdline.ALL_RESOURCES) + use_resources.remove('cpu') + use_resources.remove('network') self.check_ci_mode(args, use_resources) def test_slow_ci(self): From e8eb2bf7884292907878dca05082f3db9c31791e Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 16 Oct 2023 21:06:34 +0200 Subject: [PATCH 483/632] [3.11] C-API docs: Clarify the size of arenas (GH-110895) (#110947) C-API docs: Clarify the size of arenas (GH-110895) Clarify the size of arenas From 3.10.0 alpha 7, the pymalloc allocator uses arenas with a fixed size of 1 MiB on 64-bit platforms instead of 256 KiB on 32-bit platforms. (cherry picked from commit f07ca27709855d4637b43bba23384cc795143ee3) Co-authored-by: Mienxiu <82512658+mienxiu@users.noreply.github.com> --- Doc/c-api/memory.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/c-api/memory.rst b/Doc/c-api/memory.rst index 87b8603c4c0b10..27005f92dd43e5 100644 --- a/Doc/c-api/memory.rst +++ b/Doc/c-api/memory.rst @@ -620,7 +620,8 @@ The pymalloc allocator Python has a *pymalloc* allocator optimized for small objects (smaller or equal to 512 bytes) with a short lifetime. It uses memory mappings called "arenas" -with a fixed size of 256 KiB. It falls back to :c:func:`PyMem_RawMalloc` and +with a fixed size of either 256 KiB on 32-bit platforms or 1 MiB on 64-bit +platforms. It falls back to :c:func:`PyMem_RawMalloc` and :c:func:`PyMem_RawRealloc` for allocations larger than 512 bytes. *pymalloc* is the :ref:`default allocator ` of the From 5f7aba938cf5007b6f95472a3a54732dd99a2df8 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 16 Oct 2023 21:17:59 +0100 Subject: [PATCH 484/632] [3.11] Enable ruff on several more files in `Lib/test` (#110929) (#110935) (cherry-picked from commit 02d26c4bef3ad0f9c97e47993a7fa67898842e5c) --- Lib/ctypes/test/test_arrays.py | 10 +++++----- Lib/ctypes/test/test_functions.py | 6 +++--- Lib/test/.ruff.toml | 3 --- Lib/test/test_genericclass.py | 4 ++-- Lib/test/test_keywordonlyarg.py | 2 +- Lib/test/test_subclassinit.py | 10 +++++----- 6 files changed, 16 insertions(+), 19 deletions(-) diff --git a/Lib/ctypes/test/test_arrays.py b/Lib/ctypes/test/test_arrays.py index 14603b7049c92c..a6538779611d77 100644 --- a/Lib/ctypes/test/test_arrays.py +++ b/Lib/ctypes/test/test_arrays.py @@ -178,10 +178,10 @@ def test_bad_subclass(self): class T(Array): pass with self.assertRaises(AttributeError): - class T(Array): + class T2(Array): _type_ = c_int with self.assertRaises(AttributeError): - class T(Array): + class T3(Array): _length_ = 13 def test_bad_length(self): @@ -190,15 +190,15 @@ class T(Array): _type_ = c_int _length_ = - sys.maxsize * 2 with self.assertRaises(ValueError): - class T(Array): + class T2(Array): _type_ = c_int _length_ = -1 with self.assertRaises(TypeError): - class T(Array): + class T3(Array): _type_ = c_int _length_ = 1.87 with self.assertRaises(OverflowError): - class T(Array): + class T4(Array): _type_ = c_int _length_ = sys.maxsize * 2 diff --git a/Lib/ctypes/test/test_functions.py b/Lib/ctypes/test/test_functions.py index fc571700ce3be3..d1fbc32a41ea2c 100644 --- a/Lib/ctypes/test/test_functions.py +++ b/Lib/ctypes/test/test_functions.py @@ -42,16 +42,16 @@ class X(object, Array): from _ctypes import _Pointer with self.assertRaises(TypeError): - class X(object, _Pointer): + class X2(object, _Pointer): pass from _ctypes import _SimpleCData with self.assertRaises(TypeError): - class X(object, _SimpleCData): + class X3(object, _SimpleCData): _type_ = "i" with self.assertRaises(TypeError): - class X(object, Structure): + class X4(object, Structure): _fields_ = [] @need_symbol('c_wchar') diff --git a/Lib/test/.ruff.toml b/Lib/test/.ruff.toml index 7ef54160ec6a81..89e33b2d63c8f6 100644 --- a/Lib/test/.ruff.toml +++ b/Lib/test/.ruff.toml @@ -15,12 +15,9 @@ extend-exclude = [ "test_descr.py", "test_enum.py", "test_functools.py", - "test_genericclass.py", "test_grammar.py", "test_import/__init__.py", - "test_keywordonlyarg.py", "test_pkg.py", - "test_subclassinit.py", "test_tokenize.py", "test_yield_from.py", "time_hashlib.py", diff --git a/Lib/test/test_genericclass.py b/Lib/test/test_genericclass.py index d8bb37f69e18a1..aece757fc1933e 100644 --- a/Lib/test/test_genericclass.py +++ b/Lib/test/test_genericclass.py @@ -98,7 +98,7 @@ def __mro_entries__(self): return () d = C_too_few() with self.assertRaises(TypeError): - class D(d): ... + class E(d): ... def test_mro_entry_errors_2(self): class C_not_callable: @@ -111,7 +111,7 @@ def __mro_entries__(self): return object c = C_not_tuple() with self.assertRaises(TypeError): - class D(c): ... + class E(c): ... def test_mro_entry_metaclass(self): meta_args = [] diff --git a/Lib/test/test_keywordonlyarg.py b/Lib/test/test_keywordonlyarg.py index df82f677a00a48..918f953cae5702 100644 --- a/Lib/test/test_keywordonlyarg.py +++ b/Lib/test/test_keywordonlyarg.py @@ -170,7 +170,7 @@ def f(v=a, x=b, *, y=c, z=d): pass self.assertEqual(str(err.exception), "name 'b' is not defined") with self.assertRaises(NameError) as err: - f = lambda v=a, x=b, *, y=c, z=d: None + g = lambda v=a, x=b, *, y=c, z=d: None self.assertEqual(str(err.exception), "name 'b' is not defined") diff --git a/Lib/test/test_subclassinit.py b/Lib/test/test_subclassinit.py index 0ad7d17fbd4ddd..de3e4bb958a570 100644 --- a/Lib/test/test_subclassinit.py +++ b/Lib/test/test_subclassinit.py @@ -232,7 +232,7 @@ def __init__(self, name, bases, namespace, otherarg): super().__init__(name, bases, namespace) with self.assertRaises(TypeError): - class MyClass(metaclass=MyMeta, otherarg=1): + class MyClass2(metaclass=MyMeta, otherarg=1): pass class MyMeta(type): @@ -243,10 +243,10 @@ def __init__(self, name, bases, namespace, otherarg): super().__init__(name, bases, namespace) self.otherarg = otherarg - class MyClass(metaclass=MyMeta, otherarg=1): + class MyClass3(metaclass=MyMeta, otherarg=1): pass - self.assertEqual(MyClass.otherarg, 1) + self.assertEqual(MyClass3.otherarg, 1) def test_errors_changed_pep487(self): # These tests failed before Python 3.6, PEP 487 @@ -265,10 +265,10 @@ def __new__(cls, name, bases, namespace, otherarg): self.otherarg = otherarg return self - class MyClass(metaclass=MyMeta, otherarg=1): + class MyClass2(metaclass=MyMeta, otherarg=1): pass - self.assertEqual(MyClass.otherarg, 1) + self.assertEqual(MyClass2.otherarg, 1) def test_type(self): t = type('NewClass', (object,), {}) From 57ae64f76693b905f5fa72eaf320ad28c3a30440 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 17 Oct 2023 07:29:46 +0200 Subject: [PATCH 485/632] [3.11] Bump sphinx-lint to v0.8.1 (GH-110933) (#110958) Co-authored-by: Alex Waygood --- .pre-commit-config.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5d9deaa7ca8547..d69365f85c0507 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,12 +17,11 @@ repos: types_or: [c, inc, python, rst] - repo: https://github.com/sphinx-contrib/sphinx-lint - rev: v0.7.0 + rev: v0.8.1 hooks: - id: sphinx-lint - args: [--enable=default-role, -j1] + args: [--enable=default-role] files: ^Doc/|^Misc/NEWS.d/next/ - types: [rst] - repo: meta hooks: From ae495de4e65b2c8fbde16a246537b4484efed255 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 17 Oct 2023 11:59:10 +0200 Subject: [PATCH 486/632] [3.11] gh-110695: test_asyncio uses 50 ms for clock resolution (GH-110952) (#110971) gh-110695: test_asyncio uses 50 ms for clock resolution (GH-110952) Before utils.CLOCK_RES constant was added (20 ms), test_asyncio already used 50 ms. (cherry picked from commit 9a9fba825f8aaee4ea9b3429875c6c6324d0dee0) Co-authored-by: Victor Stinner --- Lib/test/test_asyncio/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_asyncio/utils.py b/Lib/test/test_asyncio/utils.py index f49db4e37b3592..d6f60db10f9b3f 100644 --- a/Lib/test/test_asyncio/utils.py +++ b/Lib/test/test_asyncio/utils.py @@ -38,9 +38,9 @@ # Use the maximum known clock resolution (gh-75191, gh-110088): Windows -# GetTickCount64() has a resolution of 15.6 ms. Use 20 ms to tolerate rounding +# GetTickCount64() has a resolution of 15.6 ms. Use 50 ms to tolerate rounding # issues. -CLOCK_RES = 0.020 +CLOCK_RES = 0.050 def data_file(*filename): From f58bcee53a9ef542d44c2898d743362f3f96dc53 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Tue, 17 Oct 2023 15:35:00 +0300 Subject: [PATCH 487/632] [3.11] Bump test deps: `ruff` and `pre-commit-hooks` (GH-110972) (#110981) (cherry picked from commit b75b1f389f083db8568bff573c33ab4ecf29655a) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d69365f85c0507..fd2692e490bec3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.292 + rev: v0.1.0 hooks: - id: ruff name: Run Ruff on Lib/test/ @@ -8,7 +8,7 @@ repos: files: ^Lib/test/ - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: check-toml exclude: ^Lib/test/test_tomllib/ From 73ebe2f881ac2ffb4d3d23b8693213fea53357e9 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 17 Oct 2023 20:43:46 +0200 Subject: [PATCH 488/632] [3.11] gh-110995: Fix test_gdb check_usable_gdb() (GH-110998) (#111004) gh-110995: Fix test_gdb check_usable_gdb() (GH-110998) Fix detection of gdb built without Python scripting support. * check_usable_gdb() doesn't check gdb exit code when calling run_gdb(). * Use shutil.which() to get the path to the gdb program. (cherry picked from commit 920b3dfacad615c7bb9bd9a35774469f8809b453) Co-authored-by: Victor Stinner --- Lib/test/test_gdb/util.py | 17 +++++++++++------ ...23-10-17-17-54-36.gh-issue-110995.Fx8KRD.rst | 2 ++ 2 files changed, 13 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2023-10-17-17-54-36.gh-issue-110995.Fx8KRD.rst diff --git a/Lib/test/test_gdb/util.py b/Lib/test/test_gdb/util.py index 7f4e3cba3534bd..8fe9cfc543395e 100644 --- a/Lib/test/test_gdb/util.py +++ b/Lib/test/test_gdb/util.py @@ -1,6 +1,7 @@ import os import re import shlex +import shutil import subprocess import sys import sysconfig @@ -8,6 +9,8 @@ from test import support +GDB_PROGRAM = shutil.which('gdb') or 'gdb' + # Location of custom hooks file in a repository checkout. CHECKOUT_HOOK_PATH = os.path.join(os.path.dirname(sys.executable), 'python-gdb.py') @@ -27,7 +30,7 @@ def clean_environment(): # Temporary value until it's initialized by get_gdb_version() below GDB_VERSION = (0, 0) -def run_gdb(*args, exitcode=0, **env_vars): +def run_gdb(*args, exitcode=0, check=True, **env_vars): """Runs gdb in --batch mode with the additional arguments given by *args. Returns its (stdout, stderr) decoded from utf-8 using the replace handler. @@ -36,7 +39,7 @@ def run_gdb(*args, exitcode=0, **env_vars): if env_vars: env.update(env_vars) - cmd = ['gdb', + cmd = [GDB_PROGRAM, # Batch mode: Exit after processing all the command files # specified with -x/--command '--batch', @@ -59,7 +62,7 @@ def run_gdb(*args, exitcode=0, **env_vars): stdout = proc.stdout stderr = proc.stderr - if proc.returncode != exitcode: + if check and proc.returncode != exitcode: cmd_text = shlex.join(cmd) raise Exception(f"{cmd_text} failed with exit code {proc.returncode}, " f"expected exit code {exitcode}:\n" @@ -72,10 +75,10 @@ def run_gdb(*args, exitcode=0, **env_vars): def get_gdb_version(): try: stdout, stderr = run_gdb('--version') - except OSError: + except OSError as exc: # This is what "no gdb" looks like. There may, however, be other # errors that manifest this way too. - raise unittest.SkipTest("Couldn't find gdb program on the path") + raise unittest.SkipTest(f"Couldn't find gdb program on the path: {exc}") # Regex to parse: # 'GNU gdb (GDB; SUSE Linux Enterprise 12) 7.7\n' -> 7.7 @@ -106,7 +109,8 @@ def check_usable_gdb(): # disallow this without a customized .gdbinit. stdout, stderr = run_gdb( '--eval-command=python import sys; print(sys.version_info)', - '--args', sys.executable) + '--args', sys.executable, + check=False) if "auto-loading has been declined" in stderr: raise unittest.SkipTest( @@ -144,6 +148,7 @@ def setup_module(): print(f"gdb version {GDB_VERSION[0]}.{GDB_VERSION[1]}:") for line in GDB_VERSION_TEXT.splitlines(): print(" " * 4 + line) + print(f" path: {GDB_PROGRAM}") print() 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 new file mode 100644 index 00000000000000..db29eaf234b731 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-10-17-17-54-36.gh-issue-110995.Fx8KRD.rst @@ -0,0 +1,2 @@ +test_gdb: Fix detection of gdb built without Python scripting support. Patch +by Victor Stinner. From 1af7b7db0d1cd6756ecb6081364fdd1378b1605c Mon Sep 17 00:00:00 2001 From: Lysandros Nikolaou Date: Wed, 18 Oct 2023 00:34:56 +0200 Subject: [PATCH 489/632] [3.11] gh-107450: Check for overflow in the tokenizer and fix overflow test (GH-110832) (#110939) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit a1ac5590e0f8fe008e5562d22edab65d0c1c5507) Co-authored-by: Filipe Laíns Co-authored-by: Serhiy Storchaka --- Include/errcode.h | 39 +++++++++++++++++++------------------ Lib/test/test_exceptions.py | 16 +++++++++++---- Parser/pegen_errors.c | 10 ++++------ Parser/tokenizer.c | 4 ++++ 4 files changed, 40 insertions(+), 29 deletions(-) diff --git a/Include/errcode.h b/Include/errcode.h index 54ae929bf25870..19ee83ec73d8cd 100644 --- a/Include/errcode.h +++ b/Include/errcode.h @@ -4,7 +4,6 @@ extern "C" { #endif - /* Error codes passed around between file input, tokenizer, parser and interpreter. This is necessary so we can turn them into Python exceptions at a higher level. Note that some errors have a @@ -13,24 +12,26 @@ extern "C" { the parser only returns E_EOF when it hits EOF immediately, and it never returns E_OK. */ -#define E_OK 10 /* No error */ -#define E_EOF 11 /* End Of File */ -#define E_INTR 12 /* Interrupted */ -#define E_TOKEN 13 /* Bad token */ -#define E_SYNTAX 14 /* Syntax error */ -#define E_NOMEM 15 /* Ran out of memory */ -#define E_DONE 16 /* Parsing complete */ -#define E_ERROR 17 /* Execution error */ -#define E_TABSPACE 18 /* Inconsistent mixing of tabs and spaces */ -#define E_OVERFLOW 19 /* Node had too many children */ -#define E_TOODEEP 20 /* Too many indentation levels */ -#define E_DEDENT 21 /* No matching outer block for dedent */ -#define E_DECODE 22 /* Error in decoding into Unicode */ -#define E_EOFS 23 /* EOF in triple-quoted string */ -#define E_EOLS 24 /* EOL in single-quoted string */ -#define E_LINECONT 25 /* Unexpected characters after a line continuation */ -#define E_BADSINGLE 27 /* Ill-formed single statement input */ -#define E_INTERACT_STOP 28 /* Interactive mode stopped tokenization */ +#define E_OK 10 /* No error */ +#define E_EOF 11 /* End Of File */ +#define E_INTR 12 /* Interrupted */ +#define E_TOKEN 13 /* Bad token */ +#define E_SYNTAX 14 /* Syntax error */ +#define E_NOMEM 15 /* Ran out of memory */ +#define E_DONE 16 /* Parsing complete */ +#define E_ERROR 17 /* Execution error */ +#define E_TABSPACE 18 /* Inconsistent mixing of tabs and spaces */ +#define E_OVERFLOW 19 /* Node had too many children */ +#define E_TOODEEP 20 /* Too many indentation levels */ +#define E_DEDENT 21 /* No matching outer block for dedent */ +#define E_DECODE 22 /* Error in decoding into Unicode */ +#define E_EOFS 23 /* EOF in triple-quoted string */ +#define E_EOLS 24 /* EOL in single-quoted string */ +#define E_LINECONT 25 /* Unexpected characters after a line continuation */ +#define E_BADSINGLE 27 /* Ill-formed single statement input */ +#define E_INTERACT_STOP 28 /* Interactive mode stopped tokenization */ +#define E_COLUMNOVERFLOW 29 /* Column offset overflow */ + #ifdef __cplusplus } diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index c7c63e5e4ae29d..cd064219c671e0 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -19,6 +19,12 @@ from test.support.warnings_helper import check_warnings from test import support +try: + from _testcapi import INT_MAX +except ImportError: + INT_MAX = 2**31 - 1 + + class NaiveException(Exception): def __init__(self, x): @@ -318,11 +324,13 @@ def baz(): check('(yield i) = 2', 1, 2) check('def f(*):\n pass', 1, 7) + @unittest.skipIf(INT_MAX >= sys.maxsize, "Downcasting to int is safe for col_offset") @support.requires_resource('cpu') - @support.bigmemtest(support._2G, memuse=1.5) - def testMemoryErrorBigSource(self, _size): - with self.assertRaises(OverflowError): - exec(f"if True:\n {' ' * 2**31}print('hello world')") + @support.bigmemtest(INT_MAX, memuse=2, dry_run=False) + def testMemoryErrorBigSource(self, size): + src = b"if True:\n%*s" % (size, b"pass") + with self.assertRaisesRegex(OverflowError, "Parser column offset overflow"): + compile(src, '', 'exec') @cpython_only def testSettingException(self): diff --git a/Parser/pegen_errors.c b/Parser/pegen_errors.c index ea5c4e227ff763..005356110a4340 100644 --- a/Parser/pegen_errors.c +++ b/Parser/pegen_errors.c @@ -101,6 +101,10 @@ _Pypegen_tokenizer_error(Parser *p) msg = "unexpected character after line continuation character"; break; } + case E_COLUMNOVERFLOW: + PyErr_SetString(PyExc_OverflowError, + "Parser column offset overflow - source line is too big"); + return -1; default: msg = "unknown parsing error"; } @@ -224,12 +228,6 @@ _PyPegen_raise_error(Parser *p, PyObject *errtype, const char *errmsg, ...) col_offset = 0; } else { const char* start = p->tok->buf ? p->tok->line_start : p->tok->buf; - if (p->tok->cur - start > INT_MAX) { - PyErr_SetString(PyExc_OverflowError, - "Parser column offset overflow - source line is too big"); - p->error_indicator = 1; - return NULL; - } col_offset = Py_SAFE_DOWNCAST(p->tok->cur - start, intptr_t, int); } } else { diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c index 7fc8a585621d3a..566ad8dfa00e0c 100644 --- a/Parser/tokenizer.c +++ b/Parser/tokenizer.c @@ -1057,6 +1057,10 @@ tok_nextc(struct tok_state *tok) int rc; for (;;) { if (tok->cur != tok->inp) { + if (tok->cur - tok->buf >= INT_MAX) { + tok->done = E_COLUMNOVERFLOW; + return EOF; + } return Py_CHARMASK(*tok->cur++); /* Fast path */ } if (tok->done != E_OK) { From 4ccd418a024d15d986a01588f935f43248c2f05d Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 18 Oct 2023 01:20:36 +0200 Subject: [PATCH 490/632] [3.11] gh-110756: Fix libregrtest clear_caches() for distutils (#111011) gh-110756: Fix libregrtest clear_caches() for distutils Restore code removed by recent sync with the main branch which no longer has distutils: commit 26748ed4f61520c59af15547792d1e73144a4314. --- Lib/test/libregrtest/utils.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Lib/test/libregrtest/utils.py b/Lib/test/libregrtest/utils.py index b5bbe0ef6596a7..f62184ad4fec6c 100644 --- a/Lib/test/libregrtest/utils.py +++ b/Lib/test/libregrtest/utils.py @@ -184,6 +184,15 @@ def clear_caches(): if stream is not None: stream.flush() + # Clear assorted module caches. + # Don't worry about resetting the cache if the module is not loaded + try: + distutils_dir_util = sys.modules['distutils.dir_util'] + except KeyError: + pass + else: + distutils_dir_util._path_created.clear() + try: re = sys.modules['re'] except KeyError: From 72131d0610baf5d486b83f79bdb63a75aa22b8f1 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 18 Oct 2023 06:30:39 +0200 Subject: [PATCH 491/632] [3.11] Regen Doc/requirements-oldest-sphinx.txt (GH-111012) (#111021) Regen Doc/requirements-oldest-sphinx.txt (GH-111012) Fix https://github.com/python/cpython/security/dependabot/4: use urllib3 version 2.0.7. (cherry picked from commit e7ae43ad7dde74e731a9d258e372d17f3b2eb893) Co-authored-by: Victor Stinner --- Doc/requirements-oldest-sphinx.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Doc/requirements-oldest-sphinx.txt b/Doc/requirements-oldest-sphinx.txt index 5de739fc10b085..597341d99ffeaa 100644 --- a/Doc/requirements-oldest-sphinx.txt +++ b/Doc/requirements-oldest-sphinx.txt @@ -16,7 +16,6 @@ alabaster==0.7.13 Babel==2.13.0 certifi==2023.7.22 charset-normalizer==3.3.0 -colorama==0.4.6 docutils==0.17.1 idna==3.4 imagesize==1.4.1 @@ -33,4 +32,4 @@ sphinxcontrib-htmlhelp==2.0.1 sphinxcontrib-jsmath==1.0.1 sphinxcontrib-qthelp==1.0.3 sphinxcontrib-serializinghtml==1.1.5 -urllib3==2.0.6 +urllib3==2.0.7 From 50d936a125b17ff31b527347c0f4c37aa85efb83 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 18 Oct 2023 10:04:39 +0200 Subject: [PATCH 492/632] [3.11] gh-111019: Align expected and actual titles in test output (GH-111020) (#111025) gh-111019: Align expected and actual titles in test output (GH-111020) Align expected and actual titles in output from assert_has_calls/assert_called_with for greater readability (cherry picked from commit 77dbd956090aac66e264d9d640f6adb6b0930b87) Co-authored-by: James --- Lib/unittest/mock.py | 6 +++--- Lib/unittest/test/testmock/testmock.py | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index e78eb350b8c0d5..3c96f1e864932a 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -822,7 +822,7 @@ def _format_mock_call_signature(self, args, kwargs): def _format_mock_failure_message(self, args, kwargs, action='call'): - message = 'expected %s not found.\nExpected: %s\nActual: %s' + message = 'expected %s not found.\nExpected: %s\n Actual: %s' expected_string = self._format_mock_call_signature(args, kwargs) call_args = self.call_args actual_string = self._format_mock_call_signature(*call_args) @@ -925,7 +925,7 @@ def assert_called_with(self, /, *args, **kwargs): if self.call_args is None: expected = self._format_mock_call_signature(args, kwargs) actual = 'not called.' - error_message = ('expected call not found.\nExpected: %s\nActual: %s' + error_message = ('expected call not found.\nExpected: %s\n Actual: %s' % (expected, actual)) raise AssertionError(error_message) @@ -976,7 +976,7 @@ def assert_has_calls(self, calls, any_order=False): raise AssertionError( f'{problem}\n' f'Expected: {_CallList(calls)}' - f'{self._calls_repr(prefix="Actual").rstrip(".")}' + f'{self._calls_repr(prefix=" Actual").rstrip(".")}' ) from cause return diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py index eaae22e854e949..b2b299c14023b9 100644 --- a/Lib/unittest/test/testmock/testmock.py +++ b/Lib/unittest/test/testmock/testmock.py @@ -1039,7 +1039,7 @@ def test_assert_called_with_failure_message(self): actual = 'not called.' expected = "mock(1, '2', 3, bar='foo')" - message = 'expected call not found.\nExpected: %s\nActual: %s' + message = 'expected call not found.\nExpected: %s\n Actual: %s' self.assertRaisesWithMsg( AssertionError, message % (expected, actual), mock.assert_called_with, 1, '2', 3, bar='foo' @@ -1054,7 +1054,7 @@ def test_assert_called_with_failure_message(self): for meth in asserters: actual = "foo(1, '2', 3, foo='foo')" expected = "foo(1, '2', 3, bar='foo')" - message = 'expected call not found.\nExpected: %s\nActual: %s' + message = 'expected call not found.\nExpected: %s\n Actual: %s' self.assertRaisesWithMsg( AssertionError, message % (expected, actual), meth, 1, '2', 3, bar='foo' @@ -1064,7 +1064,7 @@ def test_assert_called_with_failure_message(self): for meth in asserters: actual = "foo(1, '2', 3, foo='foo')" expected = "foo(bar='foo')" - message = 'expected call not found.\nExpected: %s\nActual: %s' + message = 'expected call not found.\nExpected: %s\n Actual: %s' self.assertRaisesWithMsg( AssertionError, message % (expected, actual), meth, bar='foo' @@ -1074,7 +1074,7 @@ def test_assert_called_with_failure_message(self): for meth in asserters: actual = "foo(1, '2', 3, foo='foo')" expected = "foo(1, 2, 3)" - message = 'expected call not found.\nExpected: %s\nActual: %s' + message = 'expected call not found.\nExpected: %s\n Actual: %s' self.assertRaisesWithMsg( AssertionError, message % (expected, actual), meth, 1, 2, 3 @@ -1084,7 +1084,7 @@ def test_assert_called_with_failure_message(self): for meth in asserters: actual = "foo(1, '2', 3, foo='foo')" expected = "foo()" - message = 'expected call not found.\nExpected: %s\nActual: %s' + message = 'expected call not found.\nExpected: %s\n Actual: %s' self.assertRaisesWithMsg( AssertionError, message % (expected, actual), meth ) @@ -1533,7 +1533,7 @@ def f(x=None): pass '^{}$'.format( re.escape('Calls not found.\n' 'Expected: [call()]\n' - 'Actual: [call(1)]'))) as cm: + ' Actual: [call(1)]'))) as cm: mock.assert_has_calls([call()]) self.assertIsNone(cm.exception.__cause__) @@ -1545,7 +1545,7 @@ def f(x=None): pass 'Error processing expected calls.\n' "Errors: [None, TypeError('too many positional arguments')]\n" "Expected: [call(), call(1, 2)]\n" - 'Actual: [call(1)]'))) as cm: + ' Actual: [call(1)]'))) as cm: mock.assert_has_calls([call(), call(1, 2)]) self.assertIsInstance(cm.exception.__cause__, TypeError) From 7c308f4c64dca4093b70b7a5e49f81c4f3679359 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 18 Oct 2023 10:40:39 +0200 Subject: [PATCH 493/632] [3.11] gh-103737: IDLE - Remove unneeded .keys() for dict iteration (GH-110960) (#111027) gh-103737: IDLE - Remove unneeded .keys() for dict iteration (GH-110960) Add comments where .keys() is needed. Leave debugger usages along because situation is unclear as indicated in expanded comment. Most testing is manual. (cherry picked from commit baefbb21d91db2d950706737a6ebee9b2eff5c2d) Co-authored-by: Terry Jan Reedy --- Lib/idlelib/config.py | 10 +++++++--- Lib/idlelib/configdialog.py | 22 +++++++++------------- Lib/idlelib/debugger.py | 2 +- Lib/idlelib/debugobj.py | 3 ++- Lib/idlelib/idle_test/test_config.py | 4 ++-- Lib/idlelib/idle_test/test_debugobj.py | 4 ++-- Lib/idlelib/pyshell.py | 7 ++++--- Lib/idlelib/stackviewer.py | 2 +- 8 files changed, 28 insertions(+), 26 deletions(-) diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index 2b09d79470b47c..898efeb4dd1550 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -597,7 +597,9 @@ def GetCoreKeys(self, keySetName=None): problem getting any core binding there will be an 'ultimate last resort fallback' to the CUA-ish bindings defined here. """ + # TODO: = dict(sorted([(v-event, keys), ...]))? keyBindings={ + # vitual-event: list of key events. '<>': ['', ''], '<>': ['', ''], '<>': ['', ''], @@ -880,7 +882,7 @@ def _dump(): # htest # (not really, but ignore in coverage) line, crc = 0, 0 def sprint(obj): - global line, crc + nonlocal line, crc txt = str(obj) line += 1 crc = crc32(txt.encode(encoding='utf-8'), crc) @@ -889,7 +891,7 @@ def sprint(obj): def dumpCfg(cfg): print('\n', cfg, '\n') # Cfg has variable '0xnnnnnnnn' address. - for key in sorted(cfg.keys()): + for key in sorted(cfg): sections = cfg[key].sections() sprint(key) sprint(sections) @@ -908,4 +910,6 @@ def dumpCfg(cfg): from unittest import main main('idlelib.idle_test.test_config', verbosity=2, exit=False) - # Run revised _dump() as htest? + _dump() + # Run revised _dump() (700+ lines) as htest? More sorting. + # Perhaps as window with tabs for textviews, making it config viewer. diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index cda7966d558a51..d56afe8a807fea 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -211,14 +211,8 @@ def help(self): contents=help_common+help_pages.get(page, '')) def deactivate_current_config(self): - """Remove current key bindings. - Iterate over window instances defined in parent and remove - the keybindings. - """ - # Before a config is saved, some cleanup of current - # config must be done - remove the previous keybindings. - win_instances = self.parent.instance_dict.keys() - for instance in win_instances: + """Remove current key bindings in current windows.""" + for instance in self.parent.instance_dict: instance.RemoveKeybindings() def activate_config_changes(self): @@ -227,8 +221,7 @@ def activate_config_changes(self): Dynamically update the current parent window instances with some of the configuration changes. """ - win_instances = self.parent.instance_dict.keys() - for instance in win_instances: + for instance in self.parent.instance_dict: instance.ResetColorizer() instance.ResetFont() instance.set_notabs_indentwidth() @@ -583,6 +576,8 @@ def create_page_highlight(self): (*)theme_message: Label """ self.theme_elements = { + # Display_name: ('internal_name, sort_number'). + # TODO: remove sort_number unneeded with dict ordering. 'Normal Code or Text': ('normal', '00'), 'Code Context': ('context', '01'), 'Python Keywords': ('keyword', '02'), @@ -765,7 +760,7 @@ def load_theme_cfg(self): self.builtinlist.SetMenu(item_list, item_list[0]) self.set_theme_type() # Load theme element option menu. - theme_names = list(self.theme_elements.keys()) + theme_names = list(self.theme_elements) theme_names.sort(key=lambda x: self.theme_elements[x][1]) self.targetlist.SetMenu(theme_names, theme_names[0]) self.paint_theme_sample() @@ -1477,12 +1472,13 @@ def load_keys_list(self, keyset_name): reselect = True list_index = self.bindingslist.index(ANCHOR) keyset = idleConf.GetKeySet(keyset_name) - bind_names = list(keyset.keys()) + # 'set' is dict mapping virtual event to list of key events. + bind_names = list(keyset) bind_names.sort() self.bindingslist.delete(0, END) for bind_name in bind_names: key = ' '.join(keyset[bind_name]) - bind_name = bind_name[2:-2] # Trim off the angle brackets. + bind_name = bind_name[2:-2] # Trim double angle brackets. if keyset_name in changes['keys']: # Handle any unsaved changes to this key set. if bind_name in changes['keys'][keyset_name]: diff --git a/Lib/idlelib/debugger.py b/Lib/idlelib/debugger.py index 452c62b42655b3..a92bb98d908d46 100644 --- a/Lib/idlelib/debugger.py +++ b/Lib/idlelib/debugger.py @@ -509,7 +509,7 @@ def load_dict(self, dict, force=0, rpc_client=None): # There is also an obscure bug in sorted(dict) where the # interpreter gets into a loop requesting non-existing dict[0], # dict[1], dict[2], etc from the debugger_r.DictProxy. - ### + # TODO recheck above; see debugger_r 159ff, debugobj 60. keys_list = dict.keys() names = sorted(keys_list) ### diff --git a/Lib/idlelib/debugobj.py b/Lib/idlelib/debugobj.py index 71d01c7070df54..032b686f379378 100644 --- a/Lib/idlelib/debugobj.py +++ b/Lib/idlelib/debugobj.py @@ -93,7 +93,8 @@ def setfunction(value, key=key, object=self.object): class DictTreeItem(SequenceTreeItem): def keys(self): - keys = list(self.object.keys()) + # TODO return sorted(self.object) + keys = list(self.object) try: keys.sort() except: diff --git a/Lib/idlelib/idle_test/test_config.py b/Lib/idlelib/idle_test/test_config.py index 08ed76fe288294..a746f1538a62b0 100644 --- a/Lib/idlelib/idle_test/test_config.py +++ b/Lib/idlelib/idle_test/test_config.py @@ -274,8 +274,8 @@ def test_create_config_handlers(self): conf.CreateConfigHandlers() # Check keys are equal - self.assertCountEqual(conf.defaultCfg.keys(), conf.config_types) - self.assertCountEqual(conf.userCfg.keys(), conf.config_types) + self.assertCountEqual(conf.defaultCfg, conf.config_types) + self.assertCountEqual(conf.userCfg, conf.config_types) # Check conf parser are correct type for default_parser in conf.defaultCfg.values(): diff --git a/Lib/idlelib/idle_test/test_debugobj.py b/Lib/idlelib/idle_test/test_debugobj.py index 131ce22b8bb69b..90ace4e1bc4f9e 100644 --- a/Lib/idlelib/idle_test/test_debugobj.py +++ b/Lib/idlelib/idle_test/test_debugobj.py @@ -37,7 +37,7 @@ def test_isexpandable(self): def test_keys(self): ti = debugobj.SequenceTreeItem('label', 'abc') - self.assertEqual(list(ti.keys()), [0, 1, 2]) + self.assertEqual(list(ti.keys()), [0, 1, 2]) # keys() is a range. class DictTreeItemTest(unittest.TestCase): @@ -50,7 +50,7 @@ def test_isexpandable(self): def test_keys(self): ti = debugobj.DictTreeItem('label', {1:1, 0:0, 2:2}) - self.assertEqual(ti.keys(), [0, 1, 2]) + self.assertEqual(ti.keys(), [0, 1, 2]) # keys() is a sorted list. if __name__ == '__main__': diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py index 3141b477eff181..0a7ee59f730a56 100755 --- a/Lib/idlelib/pyshell.py +++ b/Lib/idlelib/pyshell.py @@ -747,10 +747,11 @@ def showtraceback(self): self.tkconsole.open_stack_viewer() def checklinecache(self): - c = linecache.cache - for key in list(c.keys()): + "Remove keys other than ''." + cache = linecache.cache + for key in list(cache): # Iterate list because mutate cache. if key[:1] + key[-1:] != "<>": - del c[key] + del cache[key] def runcommand(self, code): "Run the code without invoking the debugger" diff --git a/Lib/idlelib/stackviewer.py b/Lib/idlelib/stackviewer.py index 7b00c4cdb7d033..4858cc682a4f45 100644 --- a/Lib/idlelib/stackviewer.py +++ b/Lib/idlelib/stackviewer.py @@ -99,7 +99,7 @@ def IsExpandable(self): def GetSubList(self): sublist = [] - for key in self.object.keys(): + for key in self.object.keys(): # self.object not necessarily dict. try: value = self.object[key] except KeyError: From 4e4a3e161f52d17e0bab9d1757dc1610962b215b Mon Sep 17 00:00:00 2001 From: Pablo Galindo Salgado Date: Wed, 18 Oct 2023 13:59:17 +0100 Subject: [PATCH 494/632] [3.11] gh-110696: Fix incorrect syntax error message for incorrect argument unpacking (GH-110706) (#110766) --- Grammar/python.gram | 3 +- Lib/test/test_syntax.py | 11 + ...-10-11-13-46-14.gh-issue-110696.J9kSzr.rst | 2 + Parser/parser.c | 2334 ++++++++++------- 4 files changed, 1361 insertions(+), 989 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-11-13-46-14.gh-issue-110696.J9kSzr.rst diff --git a/Grammar/python.gram b/Grammar/python.gram index bae8bc32242f9a..7b462fd0cd4157 100644 --- a/Grammar/python.gram +++ b/Grammar/python.gram @@ -1075,7 +1075,8 @@ func_type_comment[Token*]: # From here on, there are rules for invalid syntax with specialised error messages invalid_arguments: - | a=args ',' '*' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "iterable argument unpacking follows keyword argument unpacking") } + | ((','.(starred_expression | ( assignment_expression | expression !':=') !'=')+ ',' kwargs) | kwargs) ',' b='*' { + RAISE_SYNTAX_ERROR_KNOWN_LOCATION(b, "iterable argument unpacking follows keyword argument unpacking") } | a=expression b=for_if_clauses ',' [args | expression for_if_clauses] { RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, _PyPegen_get_last_comprehension_item(PyPegen_last_item(b, comprehension_ty)), "Generator expression must be parenthesized") } | a=NAME b='=' expression for_if_clauses { diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index 81456d62a37c49..07049087cdacc0 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -1827,6 +1827,17 @@ def f(x: *b) ^^^^^^^^^^^ SyntaxError: bytes can only contain ASCII literal characters + >>> f(**x, *y) + Traceback (most recent call last): + SyntaxError: iterable argument unpacking follows keyword argument unpacking + + >>> f(**x, *) + Traceback (most recent call last): + SyntaxError: iterable argument unpacking follows keyword argument unpacking + + >>> f(x, *:) + Traceback (most recent call last): + SyntaxError: invalid syntax """ import re diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-11-13-46-14.gh-issue-110696.J9kSzr.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-11-13-46-14.gh-issue-110696.J9kSzr.rst new file mode 100644 index 00000000000000..c845289d714f4c --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-10-11-13-46-14.gh-issue-110696.J9kSzr.rst @@ -0,0 +1,2 @@ +Fix incorrect error message for invalid argument unpacking. Patch by Pablo +Galindo diff --git a/Parser/parser.c b/Parser/parser.c index b2c0cfe3c2b9f7..af4c8933b181be 100644 --- a/Parser/parser.c +++ b/Parser/parser.c @@ -457,63 +457,63 @@ static char *soft_keywords[] = { #define _tmp_149_type 1377 #define _tmp_150_type 1378 #define _tmp_151_type 1379 -#define _loop0_152_type 1380 +#define _tmp_152_type 1380 #define _loop0_153_type 1381 #define _loop0_154_type 1382 -#define _tmp_155_type 1383 +#define _loop0_155_type 1383 #define _tmp_156_type 1384 #define _tmp_157_type 1385 #define _tmp_158_type 1386 -#define _loop0_159_type 1387 +#define _tmp_159_type 1387 #define _loop0_160_type 1388 -#define _loop1_161_type 1389 -#define _tmp_162_type 1390 -#define _loop0_163_type 1391 -#define _tmp_164_type 1392 -#define _loop0_165_type 1393 -#define _tmp_166_type 1394 -#define _loop0_167_type 1395 -#define _loop1_168_type 1396 -#define _tmp_169_type 1397 +#define _loop0_161_type 1389 +#define _loop1_162_type 1390 +#define _tmp_163_type 1391 +#define _loop0_164_type 1392 +#define _tmp_165_type 1393 +#define _loop0_166_type 1394 +#define _tmp_167_type 1395 +#define _loop0_168_type 1396 +#define _loop1_169_type 1397 #define _tmp_170_type 1398 #define _tmp_171_type 1399 -#define _loop0_172_type 1400 -#define _tmp_173_type 1401 +#define _tmp_172_type 1400 +#define _loop0_173_type 1401 #define _tmp_174_type 1402 -#define _loop1_175_type 1403 -#define _loop0_176_type 1404 +#define _tmp_175_type 1403 +#define _loop1_176_type 1404 #define _loop0_177_type 1405 -#define _loop0_179_type 1406 -#define _gather_178_type 1407 -#define _tmp_180_type 1408 -#define _loop0_181_type 1409 -#define _tmp_182_type 1410 -#define _loop0_183_type 1411 -#define _tmp_184_type 1412 -#define _loop0_185_type 1413 -#define _loop1_186_type 1414 +#define _loop0_178_type 1406 +#define _loop0_180_type 1407 +#define _gather_179_type 1408 +#define _tmp_181_type 1409 +#define _loop0_182_type 1410 +#define _tmp_183_type 1411 +#define _loop0_184_type 1412 +#define _tmp_185_type 1413 +#define _loop0_186_type 1414 #define _loop1_187_type 1415 -#define _tmp_188_type 1416 +#define _loop1_188_type 1416 #define _tmp_189_type 1417 -#define _loop0_190_type 1418 -#define _tmp_191_type 1419 +#define _tmp_190_type 1418 +#define _loop0_191_type 1419 #define _tmp_192_type 1420 #define _tmp_193_type 1421 -#define _loop0_195_type 1422 -#define _gather_194_type 1423 -#define _loop0_197_type 1424 -#define _gather_196_type 1425 -#define _loop0_199_type 1426 -#define _gather_198_type 1427 -#define _loop0_201_type 1428 -#define _gather_200_type 1429 -#define _tmp_202_type 1430 -#define _loop0_203_type 1431 -#define _loop1_204_type 1432 -#define _tmp_205_type 1433 -#define _loop0_206_type 1434 -#define _loop1_207_type 1435 -#define _tmp_208_type 1436 +#define _tmp_194_type 1422 +#define _loop0_196_type 1423 +#define _gather_195_type 1424 +#define _loop0_198_type 1425 +#define _gather_197_type 1426 +#define _loop0_200_type 1427 +#define _gather_199_type 1428 +#define _loop0_202_type 1429 +#define _gather_201_type 1430 +#define _tmp_203_type 1431 +#define _loop0_204_type 1432 +#define _loop1_205_type 1433 +#define _tmp_206_type 1434 +#define _loop0_207_type 1435 +#define _loop1_208_type 1436 #define _tmp_209_type 1437 #define _tmp_210_type 1438 #define _tmp_211_type 1439 @@ -523,9 +523,9 @@ static char *soft_keywords[] = { #define _tmp_215_type 1443 #define _tmp_216_type 1444 #define _tmp_217_type 1445 -#define _loop0_219_type 1446 -#define _gather_218_type 1447 -#define _tmp_220_type 1448 +#define _tmp_218_type 1446 +#define _loop0_220_type 1447 +#define _gather_219_type 1448 #define _tmp_221_type 1449 #define _tmp_222_type 1450 #define _tmp_223_type 1451 @@ -553,8 +553,14 @@ static char *soft_keywords[] = { #define _tmp_245_type 1473 #define _tmp_246_type 1474 #define _tmp_247_type 1475 -#define _tmp_248_type 1476 -#define _tmp_249_type 1477 +#define _loop0_249_type 1476 +#define _gather_248_type 1477 +#define _tmp_250_type 1478 +#define _tmp_251_type 1479 +#define _tmp_252_type 1480 +#define _tmp_253_type 1481 +#define _tmp_254_type 1482 +#define _tmp_255_type 1483 static mod_ty file_rule(Parser *p); static mod_ty interactive_rule(Parser *p); @@ -936,63 +942,63 @@ static void *_tmp_148_rule(Parser *p); static void *_tmp_149_rule(Parser *p); static void *_tmp_150_rule(Parser *p); static void *_tmp_151_rule(Parser *p); -static asdl_seq *_loop0_152_rule(Parser *p); +static void *_tmp_152_rule(Parser *p); static asdl_seq *_loop0_153_rule(Parser *p); static asdl_seq *_loop0_154_rule(Parser *p); -static void *_tmp_155_rule(Parser *p); +static asdl_seq *_loop0_155_rule(Parser *p); static void *_tmp_156_rule(Parser *p); static void *_tmp_157_rule(Parser *p); static void *_tmp_158_rule(Parser *p); -static asdl_seq *_loop0_159_rule(Parser *p); +static void *_tmp_159_rule(Parser *p); static asdl_seq *_loop0_160_rule(Parser *p); -static asdl_seq *_loop1_161_rule(Parser *p); -static void *_tmp_162_rule(Parser *p); -static asdl_seq *_loop0_163_rule(Parser *p); -static void *_tmp_164_rule(Parser *p); -static asdl_seq *_loop0_165_rule(Parser *p); -static void *_tmp_166_rule(Parser *p); -static asdl_seq *_loop0_167_rule(Parser *p); -static asdl_seq *_loop1_168_rule(Parser *p); -static void *_tmp_169_rule(Parser *p); +static asdl_seq *_loop0_161_rule(Parser *p); +static asdl_seq *_loop1_162_rule(Parser *p); +static void *_tmp_163_rule(Parser *p); +static asdl_seq *_loop0_164_rule(Parser *p); +static void *_tmp_165_rule(Parser *p); +static asdl_seq *_loop0_166_rule(Parser *p); +static void *_tmp_167_rule(Parser *p); +static asdl_seq *_loop0_168_rule(Parser *p); +static asdl_seq *_loop1_169_rule(Parser *p); static void *_tmp_170_rule(Parser *p); static void *_tmp_171_rule(Parser *p); -static asdl_seq *_loop0_172_rule(Parser *p); -static void *_tmp_173_rule(Parser *p); +static void *_tmp_172_rule(Parser *p); +static asdl_seq *_loop0_173_rule(Parser *p); static void *_tmp_174_rule(Parser *p); -static asdl_seq *_loop1_175_rule(Parser *p); -static asdl_seq *_loop0_176_rule(Parser *p); +static void *_tmp_175_rule(Parser *p); +static asdl_seq *_loop1_176_rule(Parser *p); static asdl_seq *_loop0_177_rule(Parser *p); -static asdl_seq *_loop0_179_rule(Parser *p); -static asdl_seq *_gather_178_rule(Parser *p); -static void *_tmp_180_rule(Parser *p); -static asdl_seq *_loop0_181_rule(Parser *p); -static void *_tmp_182_rule(Parser *p); -static asdl_seq *_loop0_183_rule(Parser *p); -static void *_tmp_184_rule(Parser *p); -static asdl_seq *_loop0_185_rule(Parser *p); -static asdl_seq *_loop1_186_rule(Parser *p); +static asdl_seq *_loop0_178_rule(Parser *p); +static asdl_seq *_loop0_180_rule(Parser *p); +static asdl_seq *_gather_179_rule(Parser *p); +static void *_tmp_181_rule(Parser *p); +static asdl_seq *_loop0_182_rule(Parser *p); +static void *_tmp_183_rule(Parser *p); +static asdl_seq *_loop0_184_rule(Parser *p); +static void *_tmp_185_rule(Parser *p); +static asdl_seq *_loop0_186_rule(Parser *p); static asdl_seq *_loop1_187_rule(Parser *p); -static void *_tmp_188_rule(Parser *p); +static asdl_seq *_loop1_188_rule(Parser *p); static void *_tmp_189_rule(Parser *p); -static asdl_seq *_loop0_190_rule(Parser *p); -static void *_tmp_191_rule(Parser *p); +static void *_tmp_190_rule(Parser *p); +static asdl_seq *_loop0_191_rule(Parser *p); static void *_tmp_192_rule(Parser *p); static void *_tmp_193_rule(Parser *p); -static asdl_seq *_loop0_195_rule(Parser *p); -static asdl_seq *_gather_194_rule(Parser *p); -static asdl_seq *_loop0_197_rule(Parser *p); -static asdl_seq *_gather_196_rule(Parser *p); -static asdl_seq *_loop0_199_rule(Parser *p); -static asdl_seq *_gather_198_rule(Parser *p); -static asdl_seq *_loop0_201_rule(Parser *p); -static asdl_seq *_gather_200_rule(Parser *p); -static void *_tmp_202_rule(Parser *p); -static asdl_seq *_loop0_203_rule(Parser *p); -static asdl_seq *_loop1_204_rule(Parser *p); -static void *_tmp_205_rule(Parser *p); -static asdl_seq *_loop0_206_rule(Parser *p); -static asdl_seq *_loop1_207_rule(Parser *p); -static void *_tmp_208_rule(Parser *p); +static void *_tmp_194_rule(Parser *p); +static asdl_seq *_loop0_196_rule(Parser *p); +static asdl_seq *_gather_195_rule(Parser *p); +static asdl_seq *_loop0_198_rule(Parser *p); +static asdl_seq *_gather_197_rule(Parser *p); +static asdl_seq *_loop0_200_rule(Parser *p); +static asdl_seq *_gather_199_rule(Parser *p); +static asdl_seq *_loop0_202_rule(Parser *p); +static asdl_seq *_gather_201_rule(Parser *p); +static void *_tmp_203_rule(Parser *p); +static asdl_seq *_loop0_204_rule(Parser *p); +static asdl_seq *_loop1_205_rule(Parser *p); +static void *_tmp_206_rule(Parser *p); +static asdl_seq *_loop0_207_rule(Parser *p); +static asdl_seq *_loop1_208_rule(Parser *p); static void *_tmp_209_rule(Parser *p); static void *_tmp_210_rule(Parser *p); static void *_tmp_211_rule(Parser *p); @@ -1002,9 +1008,9 @@ static void *_tmp_214_rule(Parser *p); static void *_tmp_215_rule(Parser *p); static void *_tmp_216_rule(Parser *p); static void *_tmp_217_rule(Parser *p); -static asdl_seq *_loop0_219_rule(Parser *p); -static asdl_seq *_gather_218_rule(Parser *p); -static void *_tmp_220_rule(Parser *p); +static void *_tmp_218_rule(Parser *p); +static asdl_seq *_loop0_220_rule(Parser *p); +static asdl_seq *_gather_219_rule(Parser *p); static void *_tmp_221_rule(Parser *p); static void *_tmp_222_rule(Parser *p); static void *_tmp_223_rule(Parser *p); @@ -1032,8 +1038,14 @@ static void *_tmp_244_rule(Parser *p); static void *_tmp_245_rule(Parser *p); static void *_tmp_246_rule(Parser *p); static void *_tmp_247_rule(Parser *p); -static void *_tmp_248_rule(Parser *p); -static void *_tmp_249_rule(Parser *p); +static asdl_seq *_loop0_249_rule(Parser *p); +static asdl_seq *_gather_248_rule(Parser *p); +static void *_tmp_250_rule(Parser *p); +static void *_tmp_251_rule(Parser *p); +static void *_tmp_252_rule(Parser *p); +static void *_tmp_253_rule(Parser *p); +static void *_tmp_254_rule(Parser *p); +static void *_tmp_255_rule(Parser *p); // file: statements? $ @@ -18915,7 +18927,7 @@ func_type_comment_rule(Parser *p) } // invalid_arguments: -// | args ',' '*' +// | ((','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs) | kwargs) ',' '*' // | expression for_if_clauses ',' [args | expression for_if_clauses] // | NAME '=' expression for_if_clauses // | args for_if_clauses @@ -18934,25 +18946,25 @@ invalid_arguments_rule(Parser *p) } void * _res = NULL; int _mark = p->mark; - { // args ',' '*' + { // ((','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs) | kwargs) ',' '*' if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> invalid_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args ',' '*'")); + D(fprintf(stderr, "%*c> invalid_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "((','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs) | kwargs) ',' '*'")); Token * _literal; - Token * _literal_1; - expr_ty a; + void *_tmp_144_var; + Token * b; if ( - (a = args_rule(p)) // args + (_tmp_144_var = _tmp_144_rule(p)) // (','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs) | kwargs && (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (_literal_1 = _PyPegen_expect_token(p, 16)) // token='*' + (b = _PyPegen_expect_token(p, 16)) // token='*' ) { - D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args ',' '*'")); - _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "iterable argument unpacking follows keyword argument unpacking" ); + D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "((','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs) | kwargs) ',' '*'")); + _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( b , "iterable argument unpacking follows keyword argument unpacking" ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; p->level--; @@ -18962,7 +18974,7 @@ invalid_arguments_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s invalid_arguments[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "args ',' '*'")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "((','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs) | kwargs) ',' '*'")); } { // expression for_if_clauses ',' [args | expression for_if_clauses] if (p->error_indicator) { @@ -18982,7 +18994,7 @@ invalid_arguments_rule(Parser *p) && (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (_opt_var = _tmp_144_rule(p), !p->error_indicator) // [args | expression for_if_clauses] + (_opt_var = _tmp_145_rule(p), !p->error_indicator) // [args | expression for_if_clauses] ) { D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses ',' [args | expression for_if_clauses]")); @@ -19153,7 +19165,7 @@ invalid_kwarg_rule(Parser *p) Token* a; Token * b; if ( - (a = (Token*)_tmp_145_rule(p)) // 'True' | 'False' | 'None' + (a = (Token*)_tmp_146_rule(p)) // 'True' | 'False' | 'None' && (b = _PyPegen_expect_token(p, 22)) // token='=' ) @@ -19213,7 +19225,7 @@ invalid_kwarg_rule(Parser *p) expr_ty a; Token * b; if ( - _PyPegen_lookahead(0, _tmp_146_rule, p) + _PyPegen_lookahead(0, _tmp_147_rule, p) && (a = expression_rule(p)) // expression && @@ -19438,7 +19450,7 @@ invalid_expression_rule(Parser *p) expr_ty a; expr_ty b; if ( - _PyPegen_lookahead(0, _tmp_147_rule, p) + _PyPegen_lookahead(0, _tmp_148_rule, p) && (a = disjunction_rule(p)) // disjunction && @@ -19474,7 +19486,7 @@ invalid_expression_rule(Parser *p) && (b = disjunction_rule(p)) // disjunction && - _PyPegen_lookahead(0, _tmp_148_rule, p) + _PyPegen_lookahead(0, _tmp_149_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_expression[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "disjunction 'if' disjunction !('else' | ':')")); @@ -19563,7 +19575,7 @@ invalid_named_expression_rule(Parser *p) && (b = bitwise_or_rule(p)) // bitwise_or && - _PyPegen_lookahead(0, _tmp_149_rule, p) + _PyPegen_lookahead(0, _tmp_150_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_named_expression[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME '=' bitwise_or !('=' | ':=')")); @@ -19589,7 +19601,7 @@ invalid_named_expression_rule(Parser *p) Token * b; expr_ty bitwise_or_var; if ( - _PyPegen_lookahead(0, _tmp_150_rule, p) + _PyPegen_lookahead(0, _tmp_151_rule, p) && (a = bitwise_or_rule(p)) // bitwise_or && @@ -19597,7 +19609,7 @@ invalid_named_expression_rule(Parser *p) && (bitwise_or_var = bitwise_or_rule(p)) // bitwise_or && - _PyPegen_lookahead(0, _tmp_151_rule, p) + _PyPegen_lookahead(0, _tmp_152_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_named_expression[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "!(list | tuple | genexp | 'True' | 'None' | 'False') bitwise_or '=' bitwise_or !('=' | ':=')")); @@ -19678,7 +19690,7 @@ invalid_assignment_rule(Parser *p) D(fprintf(stderr, "%*c> invalid_assignment[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_named_expression ',' star_named_expressions* ':' expression")); Token * _literal; Token * _literal_1; - asdl_seq * _loop0_152_var; + asdl_seq * _loop0_153_var; expr_ty a; expr_ty expression_var; if ( @@ -19686,7 +19698,7 @@ invalid_assignment_rule(Parser *p) && (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (_loop0_152_var = _loop0_152_rule(p)) // star_named_expressions* + (_loop0_153_var = _loop0_153_rule(p)) // star_named_expressions* && (_literal_1 = _PyPegen_expect_token(p, 11)) // token=':' && @@ -19743,10 +19755,10 @@ invalid_assignment_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_assignment[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "((star_targets '='))* star_expressions '='")); Token * _literal; - asdl_seq * _loop0_153_var; + asdl_seq * _loop0_154_var; expr_ty a; if ( - (_loop0_153_var = _loop0_153_rule(p)) // ((star_targets '='))* + (_loop0_154_var = _loop0_154_rule(p)) // ((star_targets '='))* && (a = star_expressions_rule(p)) // star_expressions && @@ -19773,10 +19785,10 @@ invalid_assignment_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_assignment[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "((star_targets '='))* yield_expr '='")); Token * _literal; - asdl_seq * _loop0_154_var; + asdl_seq * _loop0_155_var; expr_ty a; if ( - (_loop0_154_var = _loop0_154_rule(p)) // ((star_targets '='))* + (_loop0_155_var = _loop0_155_rule(p)) // ((star_targets '='))* && (a = yield_expr_rule(p)) // yield_expr && @@ -19802,7 +19814,7 @@ invalid_assignment_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_assignment[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_expressions augassign (yield_expr | star_expressions)")); - void *_tmp_155_var; + void *_tmp_156_var; expr_ty a; AugOperator* augassign_var; if ( @@ -19810,7 +19822,7 @@ invalid_assignment_rule(Parser *p) && (augassign_var = augassign_rule(p)) // augassign && - (_tmp_155_var = _tmp_155_rule(p)) // yield_expr | star_expressions + (_tmp_156_var = _tmp_156_rule(p)) // yield_expr | star_expressions ) { D(fprintf(stderr, "%*c+ invalid_assignment[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_expressions augassign (yield_expr | star_expressions)")); @@ -20036,11 +20048,11 @@ invalid_comprehension_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_comprehension[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('[' | '(' | '{') starred_expression for_if_clauses")); - void *_tmp_156_var; + void *_tmp_157_var; expr_ty a; asdl_comprehension_seq* for_if_clauses_var; if ( - (_tmp_156_var = _tmp_156_rule(p)) // '[' | '(' | '{' + (_tmp_157_var = _tmp_157_rule(p)) // '[' | '(' | '{' && (a = starred_expression_rule(p)) // starred_expression && @@ -20067,12 +20079,12 @@ invalid_comprehension_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_comprehension[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('[' | '{') star_named_expression ',' star_named_expressions for_if_clauses")); Token * _literal; - void *_tmp_157_var; + void *_tmp_158_var; expr_ty a; asdl_expr_seq* b; asdl_comprehension_seq* for_if_clauses_var; if ( - (_tmp_157_var = _tmp_157_rule(p)) // '[' | '{' + (_tmp_158_var = _tmp_158_rule(p)) // '[' | '{' && (a = star_named_expression_rule(p)) // star_named_expression && @@ -20102,12 +20114,12 @@ invalid_comprehension_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_comprehension[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('[' | '{') star_named_expression ',' for_if_clauses")); - void *_tmp_158_var; + void *_tmp_159_var; expr_ty a; Token * b; asdl_comprehension_seq* for_if_clauses_var; if ( - (_tmp_158_var = _tmp_158_rule(p)) // '[' | '{' + (_tmp_159_var = _tmp_159_rule(p)) // '[' | '{' && (a = star_named_expression_rule(p)) // star_named_expression && @@ -20217,11 +20229,11 @@ invalid_parameters_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default* invalid_parameters_helper param_no_default")); - asdl_seq * _loop0_159_var; + asdl_seq * _loop0_160_var; arg_ty a; void *invalid_parameters_helper_var; if ( - (_loop0_159_var = _loop0_159_rule(p)) // param_no_default* + (_loop0_160_var = _loop0_160_rule(p)) // param_no_default* && (invalid_parameters_helper_var = invalid_parameters_helper_rule(p)) // invalid_parameters_helper && @@ -20247,18 +20259,18 @@ invalid_parameters_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default* '(' param_no_default+ ','? ')'")); - asdl_seq * _loop0_160_var; - asdl_seq * _loop1_161_var; + asdl_seq * _loop0_161_var; + asdl_seq * _loop1_162_var; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings Token * a; Token * b; if ( - (_loop0_160_var = _loop0_160_rule(p)) // param_no_default* + (_loop0_161_var = _loop0_161_rule(p)) // param_no_default* && (a = _PyPegen_expect_token(p, 7)) // token='(' && - (_loop1_161_var = _loop1_161_rule(p)) // param_no_default+ + (_loop1_162_var = _loop1_162_rule(p)) // param_no_default+ && (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator) // ','? && @@ -20311,13 +20323,13 @@ invalid_parameters_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(slash_no_default | slash_with_default) param_maybe_default* '/'")); - asdl_seq * _loop0_163_var; - void *_tmp_162_var; + asdl_seq * _loop0_164_var; + void *_tmp_163_var; Token * a; if ( - (_tmp_162_var = _tmp_162_rule(p)) // slash_no_default | slash_with_default + (_tmp_163_var = _tmp_163_rule(p)) // slash_no_default | slash_with_default && - (_loop0_163_var = _loop0_163_rule(p)) // param_maybe_default* + (_loop0_164_var = _loop0_164_rule(p)) // param_maybe_default* && (a = _PyPegen_expect_token(p, 17)) // token='/' ) @@ -20342,22 +20354,22 @@ invalid_parameters_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "[(slash_no_default | slash_with_default)] param_maybe_default* '*' (',' | param_no_default) param_maybe_default* '/'")); Token * _literal; - asdl_seq * _loop0_165_var; - asdl_seq * _loop0_167_var; + asdl_seq * _loop0_166_var; + asdl_seq * _loop0_168_var; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings - void *_tmp_166_var; + void *_tmp_167_var; Token * a; if ( - (_opt_var = _tmp_164_rule(p), !p->error_indicator) // [(slash_no_default | slash_with_default)] + (_opt_var = _tmp_165_rule(p), !p->error_indicator) // [(slash_no_default | slash_with_default)] && - (_loop0_165_var = _loop0_165_rule(p)) // param_maybe_default* + (_loop0_166_var = _loop0_166_rule(p)) // param_maybe_default* && (_literal = _PyPegen_expect_token(p, 16)) // token='*' && - (_tmp_166_var = _tmp_166_rule(p)) // ',' | param_no_default + (_tmp_167_var = _tmp_167_rule(p)) // ',' | param_no_default && - (_loop0_167_var = _loop0_167_rule(p)) // param_maybe_default* + (_loop0_168_var = _loop0_168_rule(p)) // param_maybe_default* && (a = _PyPegen_expect_token(p, 17)) // token='/' ) @@ -20382,10 +20394,10 @@ invalid_parameters_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_maybe_default+ '/' '*'")); Token * _literal; - asdl_seq * _loop1_168_var; + asdl_seq * _loop1_169_var; Token * a; if ( - (_loop1_168_var = _loop1_168_rule(p)) // param_maybe_default+ + (_loop1_169_var = _loop1_169_rule(p)) // param_maybe_default+ && (_literal = _PyPegen_expect_token(p, 17)) // token='/' && @@ -20435,7 +20447,7 @@ invalid_default_rule(Parser *p) if ( (a = _PyPegen_expect_token(p, 22)) // token='=' && - _PyPegen_lookahead(1, _tmp_169_rule, p) + _PyPegen_lookahead(1, _tmp_170_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_default[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'=' &(')' | ',')")); @@ -20481,12 +20493,12 @@ invalid_star_etc_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_star_etc[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*' (')' | ',' (')' | '**'))")); - void *_tmp_170_var; + void *_tmp_171_var; Token * a; if ( (a = _PyPegen_expect_token(p, 16)) // token='*' && - (_tmp_170_var = _tmp_170_rule(p)) // ')' | ',' (')' | '**') + (_tmp_171_var = _tmp_171_rule(p)) // ')' | ',' (')' | '**') ) { D(fprintf(stderr, "%*c+ invalid_star_etc[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' (')' | ',' (')' | '**'))")); @@ -20569,20 +20581,20 @@ invalid_star_etc_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_star_etc[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*' (param_no_default | ',') param_maybe_default* '*' (param_no_default | ',')")); Token * _literal; - asdl_seq * _loop0_172_var; - void *_tmp_171_var; - void *_tmp_173_var; + asdl_seq * _loop0_173_var; + void *_tmp_172_var; + void *_tmp_174_var; Token * a; if ( (_literal = _PyPegen_expect_token(p, 16)) // token='*' && - (_tmp_171_var = _tmp_171_rule(p)) // param_no_default | ',' + (_tmp_172_var = _tmp_172_rule(p)) // param_no_default | ',' && - (_loop0_172_var = _loop0_172_rule(p)) // param_maybe_default* + (_loop0_173_var = _loop0_173_rule(p)) // param_maybe_default* && (a = _PyPegen_expect_token(p, 16)) // token='*' && - (_tmp_173_var = _tmp_173_rule(p)) // param_no_default | ',' + (_tmp_174_var = _tmp_174_rule(p)) // param_no_default | ',' ) { D(fprintf(stderr, "%*c+ invalid_star_etc[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' (param_no_default | ',') param_maybe_default* '*' (param_no_default | ',')")); @@ -20698,7 +20710,7 @@ invalid_kwds_rule(Parser *p) && (_literal_1 = _PyPegen_expect_token(p, 12)) // token=',' && - (a = (Token*)_tmp_174_rule(p)) // '*' | '**' | '/' + (a = (Token*)_tmp_175_rule(p)) // '*' | '**' | '/' ) { D(fprintf(stderr, "%*c+ invalid_kwds[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**' param ',' ('*' | '**' | '/')")); @@ -20764,13 +20776,13 @@ invalid_parameters_helper_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_parameters_helper[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_with_default+")); - asdl_seq * _loop1_175_var; + asdl_seq * _loop1_176_var; if ( - (_loop1_175_var = _loop1_175_rule(p)) // param_with_default+ + (_loop1_176_var = _loop1_176_rule(p)) // param_with_default+ ) { D(fprintf(stderr, "%*c+ invalid_parameters_helper[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_with_default+")); - _res = _loop1_175_var; + _res = _loop1_176_var; goto done; } p->mark = _mark; @@ -20809,11 +20821,11 @@ invalid_lambda_parameters_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_lambda_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default* invalid_lambda_parameters_helper lambda_param_no_default")); - asdl_seq * _loop0_176_var; + asdl_seq * _loop0_177_var; arg_ty a; void *invalid_lambda_parameters_helper_var; if ( - (_loop0_176_var = _loop0_176_rule(p)) // lambda_param_no_default* + (_loop0_177_var = _loop0_177_rule(p)) // lambda_param_no_default* && (invalid_lambda_parameters_helper_var = invalid_lambda_parameters_helper_rule(p)) // invalid_lambda_parameters_helper && @@ -20839,18 +20851,18 @@ invalid_lambda_parameters_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_lambda_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default* '(' ','.lambda_param+ ','? ')'")); - asdl_seq * _gather_178_var; - asdl_seq * _loop0_177_var; + asdl_seq * _gather_179_var; + asdl_seq * _loop0_178_var; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings Token * a; Token * b; if ( - (_loop0_177_var = _loop0_177_rule(p)) // lambda_param_no_default* + (_loop0_178_var = _loop0_178_rule(p)) // lambda_param_no_default* && (a = _PyPegen_expect_token(p, 7)) // token='(' && - (_gather_178_var = _gather_178_rule(p)) // ','.lambda_param+ + (_gather_179_var = _gather_179_rule(p)) // ','.lambda_param+ && (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator) // ','? && @@ -20903,13 +20915,13 @@ invalid_lambda_parameters_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_lambda_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(lambda_slash_no_default | lambda_slash_with_default) lambda_param_maybe_default* '/'")); - asdl_seq * _loop0_181_var; - void *_tmp_180_var; + asdl_seq * _loop0_182_var; + void *_tmp_181_var; Token * a; if ( - (_tmp_180_var = _tmp_180_rule(p)) // lambda_slash_no_default | lambda_slash_with_default + (_tmp_181_var = _tmp_181_rule(p)) // lambda_slash_no_default | lambda_slash_with_default && - (_loop0_181_var = _loop0_181_rule(p)) // lambda_param_maybe_default* + (_loop0_182_var = _loop0_182_rule(p)) // lambda_param_maybe_default* && (a = _PyPegen_expect_token(p, 17)) // token='/' ) @@ -20934,22 +20946,22 @@ invalid_lambda_parameters_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_lambda_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "[(lambda_slash_no_default | lambda_slash_with_default)] lambda_param_maybe_default* '*' (',' | lambda_param_no_default) lambda_param_maybe_default* '/'")); Token * _literal; - asdl_seq * _loop0_183_var; - asdl_seq * _loop0_185_var; + asdl_seq * _loop0_184_var; + asdl_seq * _loop0_186_var; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings - void *_tmp_184_var; + void *_tmp_185_var; Token * a; if ( - (_opt_var = _tmp_182_rule(p), !p->error_indicator) // [(lambda_slash_no_default | lambda_slash_with_default)] + (_opt_var = _tmp_183_rule(p), !p->error_indicator) // [(lambda_slash_no_default | lambda_slash_with_default)] && - (_loop0_183_var = _loop0_183_rule(p)) // lambda_param_maybe_default* + (_loop0_184_var = _loop0_184_rule(p)) // lambda_param_maybe_default* && (_literal = _PyPegen_expect_token(p, 16)) // token='*' && - (_tmp_184_var = _tmp_184_rule(p)) // ',' | lambda_param_no_default + (_tmp_185_var = _tmp_185_rule(p)) // ',' | lambda_param_no_default && - (_loop0_185_var = _loop0_185_rule(p)) // lambda_param_maybe_default* + (_loop0_186_var = _loop0_186_rule(p)) // lambda_param_maybe_default* && (a = _PyPegen_expect_token(p, 17)) // token='/' ) @@ -20974,10 +20986,10 @@ invalid_lambda_parameters_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_lambda_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_maybe_default+ '/' '*'")); Token * _literal; - asdl_seq * _loop1_186_var; + asdl_seq * _loop1_187_var; Token * a; if ( - (_loop1_186_var = _loop1_186_rule(p)) // lambda_param_maybe_default+ + (_loop1_187_var = _loop1_187_rule(p)) // lambda_param_maybe_default+ && (_literal = _PyPegen_expect_token(p, 17)) // token='/' && @@ -21049,13 +21061,13 @@ invalid_lambda_parameters_helper_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_lambda_parameters_helper[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default+")); - asdl_seq * _loop1_187_var; + asdl_seq * _loop1_188_var; if ( - (_loop1_187_var = _loop1_187_rule(p)) // lambda_param_with_default+ + (_loop1_188_var = _loop1_188_rule(p)) // lambda_param_with_default+ ) { D(fprintf(stderr, "%*c+ invalid_lambda_parameters_helper[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default+")); - _res = _loop1_187_var; + _res = _loop1_188_var; goto done; } p->mark = _mark; @@ -21092,11 +21104,11 @@ invalid_lambda_star_etc_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_lambda_star_etc[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*' (':' | ',' (':' | '**'))")); Token * _literal; - void *_tmp_188_var; + void *_tmp_189_var; if ( (_literal = _PyPegen_expect_token(p, 16)) // token='*' && - (_tmp_188_var = _tmp_188_rule(p)) // ':' | ',' (':' | '**') + (_tmp_189_var = _tmp_189_rule(p)) // ':' | ',' (':' | '**') ) { D(fprintf(stderr, "%*c+ invalid_lambda_star_etc[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' (':' | ',' (':' | '**'))")); @@ -21149,20 +21161,20 @@ invalid_lambda_star_etc_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_lambda_star_etc[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*' (lambda_param_no_default | ',') lambda_param_maybe_default* '*' (lambda_param_no_default | ',')")); Token * _literal; - asdl_seq * _loop0_190_var; - void *_tmp_189_var; - void *_tmp_191_var; + asdl_seq * _loop0_191_var; + void *_tmp_190_var; + void *_tmp_192_var; Token * a; if ( (_literal = _PyPegen_expect_token(p, 16)) // token='*' && - (_tmp_189_var = _tmp_189_rule(p)) // lambda_param_no_default | ',' + (_tmp_190_var = _tmp_190_rule(p)) // lambda_param_no_default | ',' && - (_loop0_190_var = _loop0_190_rule(p)) // lambda_param_maybe_default* + (_loop0_191_var = _loop0_191_rule(p)) // lambda_param_maybe_default* && (a = _PyPegen_expect_token(p, 16)) // token='*' && - (_tmp_191_var = _tmp_191_rule(p)) // lambda_param_no_default | ',' + (_tmp_192_var = _tmp_192_rule(p)) // lambda_param_no_default | ',' ) { D(fprintf(stderr, "%*c+ invalid_lambda_star_etc[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' (lambda_param_no_default | ',') lambda_param_maybe_default* '*' (lambda_param_no_default | ',')")); @@ -21281,7 +21293,7 @@ invalid_lambda_kwds_rule(Parser *p) && (_literal_1 = _PyPegen_expect_token(p, 12)) // token=',' && - (a = (Token*)_tmp_192_rule(p)) // '*' | '**' | '/' + (a = (Token*)_tmp_193_rule(p)) // '*' | '**' | '/' ) { D(fprintf(stderr, "%*c+ invalid_lambda_kwds[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**' lambda_param ',' ('*' | '**' | '/')")); @@ -21389,7 +21401,7 @@ invalid_with_item_rule(Parser *p) && (a = expression_rule(p)) // expression && - _PyPegen_lookahead(1, _tmp_193_rule, p) + _PyPegen_lookahead(1, _tmp_194_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_with_item[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression 'as' expression &(',' | ')' | ':')")); @@ -21617,7 +21629,7 @@ invalid_with_stmt_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_with_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "ASYNC? 'with' ','.(expression ['as' star_target])+ NEWLINE")); - asdl_seq * _gather_194_var; + asdl_seq * _gather_195_var; Token * _keyword; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings @@ -21627,7 +21639,7 @@ invalid_with_stmt_rule(Parser *p) && (_keyword = _PyPegen_expect_token(p, 612)) // token='with' && - (_gather_194_var = _gather_194_rule(p)) // ','.(expression ['as' star_target])+ + (_gather_195_var = _gather_195_rule(p)) // ','.(expression ['as' star_target])+ && (newline_var = _PyPegen_expect_token(p, NEWLINE)) // token='NEWLINE' ) @@ -21651,7 +21663,7 @@ invalid_with_stmt_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_with_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "ASYNC? 'with' '(' ','.(expressions ['as' star_target])+ ','? ')' NEWLINE")); - asdl_seq * _gather_196_var; + asdl_seq * _gather_197_var; Token * _keyword; Token * _literal; Token * _literal_1; @@ -21667,7 +21679,7 @@ invalid_with_stmt_rule(Parser *p) && (_literal = _PyPegen_expect_token(p, 7)) // token='(' && - (_gather_196_var = _gather_196_rule(p)) // ','.(expressions ['as' star_target])+ + (_gather_197_var = _gather_197_rule(p)) // ','.(expressions ['as' star_target])+ && (_opt_var_1 = _PyPegen_expect_token(p, 12), !p->error_indicator) // ','? && @@ -21717,7 +21729,7 @@ invalid_with_stmt_indent_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_with_stmt_indent[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "ASYNC? 'with' ','.(expression ['as' star_target])+ ':' NEWLINE !INDENT")); - asdl_seq * _gather_198_var; + asdl_seq * _gather_199_var; Token * _literal; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings @@ -21728,7 +21740,7 @@ invalid_with_stmt_indent_rule(Parser *p) && (a = _PyPegen_expect_token(p, 612)) // token='with' && - (_gather_198_var = _gather_198_rule(p)) // ','.(expression ['as' star_target])+ + (_gather_199_var = _gather_199_rule(p)) // ','.(expression ['as' star_target])+ && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -21756,7 +21768,7 @@ invalid_with_stmt_indent_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_with_stmt_indent[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "ASYNC? 'with' '(' ','.(expressions ['as' star_target])+ ','? ')' ':' NEWLINE !INDENT")); - asdl_seq * _gather_200_var; + asdl_seq * _gather_201_var; Token * _literal; Token * _literal_1; Token * _literal_2; @@ -21773,7 +21785,7 @@ invalid_with_stmt_indent_rule(Parser *p) && (_literal = _PyPegen_expect_token(p, 7)) // token='(' && - (_gather_200_var = _gather_200_rule(p)) // ','.(expressions ['as' star_target])+ + (_gather_201_var = _gather_201_rule(p)) // ','.(expressions ['as' star_target])+ && (_opt_var_1 = _PyPegen_expect_token(p, 12), !p->error_indicator) // ','? && @@ -21871,7 +21883,7 @@ invalid_try_stmt_rule(Parser *p) && (block_var = block_rule(p)) // block && - _PyPegen_lookahead(0, _tmp_202_rule, p) + _PyPegen_lookahead(0, _tmp_203_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_try_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'try' ':' block !('except' | 'finally')")); @@ -21896,8 +21908,8 @@ invalid_try_stmt_rule(Parser *p) Token * _keyword; Token * _literal; Token * _literal_1; - asdl_seq * _loop0_203_var; - asdl_seq * _loop1_204_var; + asdl_seq * _loop0_204_var; + asdl_seq * _loop1_205_var; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings Token * a; @@ -21908,9 +21920,9 @@ invalid_try_stmt_rule(Parser *p) && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && - (_loop0_203_var = _loop0_203_rule(p)) // block* + (_loop0_204_var = _loop0_204_rule(p)) // block* && - (_loop1_204_var = _loop1_204_rule(p)) // except_block+ + (_loop1_205_var = _loop1_205_rule(p)) // except_block+ && (a = _PyPegen_expect_token(p, 634)) // token='except' && @@ -21918,7 +21930,7 @@ invalid_try_stmt_rule(Parser *p) && (expression_var = expression_rule(p)) // expression && - (_opt_var = _tmp_205_rule(p), !p->error_indicator) // ['as' NAME] + (_opt_var = _tmp_206_rule(p), !p->error_indicator) // ['as' NAME] && (_literal_1 = _PyPegen_expect_token(p, 11)) // token=':' ) @@ -21945,8 +21957,8 @@ invalid_try_stmt_rule(Parser *p) Token * _keyword; Token * _literal; Token * _literal_1; - asdl_seq * _loop0_206_var; - asdl_seq * _loop1_207_var; + asdl_seq * _loop0_207_var; + asdl_seq * _loop1_208_var; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings Token * a; @@ -21955,13 +21967,13 @@ invalid_try_stmt_rule(Parser *p) && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && - (_loop0_206_var = _loop0_206_rule(p)) // block* + (_loop0_207_var = _loop0_207_rule(p)) // block* && - (_loop1_207_var = _loop1_207_rule(p)) // except_star_block+ + (_loop1_208_var = _loop1_208_rule(p)) // except_star_block+ && (a = _PyPegen_expect_token(p, 634)) // token='except' && - (_opt_var = _tmp_208_rule(p), !p->error_indicator) // [expression ['as' NAME]] + (_opt_var = _tmp_209_rule(p), !p->error_indicator) // [expression ['as' NAME]] && (_literal_1 = _PyPegen_expect_token(p, 11)) // token=':' ) @@ -22029,7 +22041,7 @@ invalid_except_stmt_rule(Parser *p) && (expressions_var = expressions_rule(p)) // expressions && - (_opt_var_1 = _tmp_209_rule(p), !p->error_indicator) // ['as' NAME] + (_opt_var_1 = _tmp_210_rule(p), !p->error_indicator) // ['as' NAME] && (_literal_1 = _PyPegen_expect_token(p, 11)) // token=':' ) @@ -22067,7 +22079,7 @@ invalid_except_stmt_rule(Parser *p) && (expression_var = expression_rule(p)) // expression && - (_opt_var_1 = _tmp_210_rule(p), !p->error_indicator) // ['as' NAME] + (_opt_var_1 = _tmp_211_rule(p), !p->error_indicator) // ['as' NAME] && (newline_var = _PyPegen_expect_token(p, NEWLINE)) // token='NEWLINE' ) @@ -22119,14 +22131,14 @@ invalid_except_stmt_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_except_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'except' '*' (NEWLINE | ':')")); Token * _literal; - void *_tmp_211_var; + void *_tmp_212_var; Token * a; if ( (a = _PyPegen_expect_token(p, 634)) // token='except' && (_literal = _PyPegen_expect_token(p, 16)) // token='*' && - (_tmp_211_var = _tmp_211_rule(p)) // NEWLINE | ':' + (_tmp_212_var = _tmp_212_rule(p)) // NEWLINE | ':' ) { D(fprintf(stderr, "%*c+ invalid_except_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'except' '*' (NEWLINE | ':')")); @@ -22233,7 +22245,7 @@ invalid_except_stmt_indent_rule(Parser *p) && (expression_var = expression_rule(p)) // expression && - (_opt_var = _tmp_212_rule(p), !p->error_indicator) // ['as' NAME] + (_opt_var = _tmp_213_rule(p), !p->error_indicator) // ['as' NAME] && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -22328,7 +22340,7 @@ invalid_except_star_stmt_indent_rule(Parser *p) && (expression_var = expression_rule(p)) // expression && - (_opt_var = _tmp_213_rule(p), !p->error_indicator) // ['as' NAME] + (_opt_var = _tmp_214_rule(p), !p->error_indicator) // ['as' NAME] && (_literal_1 = _PyPegen_expect_token(p, 11)) // token=':' && @@ -22697,7 +22709,7 @@ invalid_class_argument_pattern_rule(Parser *p) asdl_pattern_seq* a; asdl_seq* keyword_patterns_var; if ( - (_opt_var = _tmp_214_rule(p), !p->error_indicator) // [positional_patterns ','] + (_opt_var = _tmp_215_rule(p), !p->error_indicator) // [positional_patterns ','] && (keyword_patterns_var = keyword_patterns_rule(p)) // keyword_patterns && @@ -23191,7 +23203,7 @@ invalid_def_raw_rule(Parser *p) && (_literal_1 = _PyPegen_expect_token(p, 8)) // token=')' && - (_opt_var_2 = _tmp_215_rule(p), !p->error_indicator) // ['->' expression] + (_opt_var_2 = _tmp_216_rule(p), !p->error_indicator) // ['->' expression] && (_literal_2 = _PyPegen_expect_token(p, 11)) // token=':' && @@ -23251,7 +23263,7 @@ invalid_class_def_raw_rule(Parser *p) && (name_var = _PyPegen_name_token(p)) // NAME && - (_opt_var = _tmp_216_rule(p), !p->error_indicator) // ['(' arguments? ')'] + (_opt_var = _tmp_217_rule(p), !p->error_indicator) // ['(' arguments? ')'] && (newline_var = _PyPegen_expect_token(p, NEWLINE)) // token='NEWLINE' ) @@ -23286,7 +23298,7 @@ invalid_class_def_raw_rule(Parser *p) && (name_var = _PyPegen_name_token(p)) // NAME && - (_opt_var = _tmp_217_rule(p), !p->error_indicator) // ['(' arguments? ')'] + (_opt_var = _tmp_218_rule(p), !p->error_indicator) // ['(' arguments? ')'] && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -23337,11 +23349,11 @@ invalid_double_starred_kvpairs_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_double_starred_kvpairs[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.double_starred_kvpair+ ',' invalid_kvpair")); - asdl_seq * _gather_218_var; + asdl_seq * _gather_219_var; Token * _literal; void *invalid_kvpair_var; if ( - (_gather_218_var = _gather_218_rule(p)) // ','.double_starred_kvpair+ + (_gather_219_var = _gather_219_rule(p)) // ','.double_starred_kvpair+ && (_literal = _PyPegen_expect_token(p, 12)) // token=',' && @@ -23349,7 +23361,7 @@ invalid_double_starred_kvpairs_rule(Parser *p) ) { D(fprintf(stderr, "%*c+ invalid_double_starred_kvpairs[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.double_starred_kvpair+ ',' invalid_kvpair")); - _res = _PyPegen_dummy_name(p, _gather_218_var, _literal, invalid_kvpair_var); + _res = _PyPegen_dummy_name(p, _gather_219_var, _literal, invalid_kvpair_var); goto done; } p->mark = _mark; @@ -23402,7 +23414,7 @@ invalid_double_starred_kvpairs_rule(Parser *p) && (a = _PyPegen_expect_token(p, 11)) // token=':' && - _PyPegen_lookahead(1, _tmp_220_rule, p) + _PyPegen_lookahead(1, _tmp_221_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_double_starred_kvpairs[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ':' &('}' | ',')")); @@ -23513,7 +23525,7 @@ invalid_kvpair_rule(Parser *p) && (a = _PyPegen_expect_token(p, 11)) // token=':' && - _PyPegen_lookahead(1, _tmp_221_rule, p) + _PyPegen_lookahead(1, _tmp_222_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_kvpair[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ':' &('}' | ',')")); @@ -24364,12 +24376,12 @@ _loop1_14_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_14[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(star_targets '=')")); - void *_tmp_222_var; + void *_tmp_223_var; while ( - (_tmp_222_var = _tmp_222_rule(p)) // star_targets '=' + (_tmp_223_var = _tmp_223_rule(p)) // star_targets '=' ) { - _res = _tmp_222_var; + _res = _tmp_223_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -24943,12 +24955,12 @@ _loop0_24_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop0_24[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('.' | '...')")); - void *_tmp_223_var; + void *_tmp_224_var; while ( - (_tmp_223_var = _tmp_223_rule(p)) // '.' | '...' + (_tmp_224_var = _tmp_224_rule(p)) // '.' | '...' ) { - _res = _tmp_223_var; + _res = _tmp_224_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -25011,12 +25023,12 @@ _loop1_25_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_25[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('.' | '...')")); - void *_tmp_224_var; + void *_tmp_225_var; while ( - (_tmp_224_var = _tmp_224_rule(p)) // '.' | '...' + (_tmp_225_var = _tmp_225_rule(p)) // '.' | '...' ) { - _res = _tmp_224_var; + _res = _tmp_225_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -25416,12 +25428,12 @@ _loop1_32_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_32[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('@' named_expression NEWLINE)")); - void *_tmp_225_var; + void *_tmp_226_var; while ( - (_tmp_225_var = _tmp_225_rule(p)) // '@' named_expression NEWLINE + (_tmp_226_var = _tmp_226_rule(p)) // '@' named_expression NEWLINE ) { - _res = _tmp_225_var; + _res = _tmp_226_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -28477,12 +28489,12 @@ _loop1_80_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_80[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' expression)")); - void *_tmp_226_var; + void *_tmp_227_var; while ( - (_tmp_226_var = _tmp_226_rule(p)) // ',' expression + (_tmp_227_var = _tmp_227_rule(p)) // ',' expression ) { - _res = _tmp_226_var; + _res = _tmp_227_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -28550,12 +28562,12 @@ _loop1_81_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_81[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' star_expression)")); - void *_tmp_227_var; + void *_tmp_228_var; while ( - (_tmp_227_var = _tmp_227_rule(p)) // ',' star_expression + (_tmp_228_var = _tmp_228_rule(p)) // ',' star_expression ) { - _res = _tmp_227_var; + _res = _tmp_228_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -28742,12 +28754,12 @@ _loop1_84_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_84[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('or' conjunction)")); - void *_tmp_228_var; + void *_tmp_229_var; while ( - (_tmp_228_var = _tmp_228_rule(p)) // 'or' conjunction + (_tmp_229_var = _tmp_229_rule(p)) // 'or' conjunction ) { - _res = _tmp_228_var; + _res = _tmp_229_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -28815,12 +28827,12 @@ _loop1_85_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_85[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('and' inversion)")); - void *_tmp_229_var; + void *_tmp_230_var; while ( - (_tmp_229_var = _tmp_229_rule(p)) // 'and' inversion + (_tmp_230_var = _tmp_230_rule(p)) // 'and' inversion ) { - _res = _tmp_229_var; + _res = _tmp_230_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -29010,7 +29022,7 @@ _loop0_89_rule(Parser *p) while ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (elem = _tmp_230_rule(p)) // slice | starred_expression + (elem = _tmp_231_rule(p)) // slice | starred_expression ) { _res = elem; @@ -29076,7 +29088,7 @@ _gather_88_rule(Parser *p) void *elem; asdl_seq * seq; if ( - (elem = _tmp_230_rule(p)) // slice | starred_expression + (elem = _tmp_231_rule(p)) // slice | starred_expression && (seq = _loop0_89_rule(p)) // _loop0_89 ) @@ -30762,12 +30774,12 @@ _loop0_114_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop0_114[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('if' disjunction)")); - void *_tmp_231_var; + void *_tmp_232_var; while ( - (_tmp_231_var = _tmp_231_rule(p)) // 'if' disjunction + (_tmp_232_var = _tmp_232_rule(p)) // 'if' disjunction ) { - _res = _tmp_231_var; + _res = _tmp_232_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -30830,12 +30842,12 @@ _loop0_115_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop0_115[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('if' disjunction)")); - void *_tmp_232_var; + void *_tmp_233_var; while ( - (_tmp_232_var = _tmp_232_rule(p)) // 'if' disjunction + (_tmp_233_var = _tmp_233_rule(p)) // 'if' disjunction ) { - _res = _tmp_232_var; + _res = _tmp_233_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -30963,7 +30975,7 @@ _loop0_118_rule(Parser *p) while ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (elem = _tmp_233_rule(p)) // starred_expression | (assignment_expression | expression !':=') !'=' + (elem = _tmp_234_rule(p)) // starred_expression | (assignment_expression | expression !':=') !'=' ) { _res = elem; @@ -31030,7 +31042,7 @@ _gather_117_rule(Parser *p) void *elem; asdl_seq * seq; if ( - (elem = _tmp_233_rule(p)) // starred_expression | (assignment_expression | expression !':=') !'=' + (elem = _tmp_234_rule(p)) // starred_expression | (assignment_expression | expression !':=') !'=' && (seq = _loop0_118_rule(p)) // _loop0_118 ) @@ -31601,12 +31613,12 @@ _loop0_128_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop0_128[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' star_target)")); - void *_tmp_234_var; + void *_tmp_235_var; while ( - (_tmp_234_var = _tmp_234_rule(p)) // ',' star_target + (_tmp_235_var = _tmp_235_rule(p)) // ',' star_target ) { - _res = _tmp_234_var; + _res = _tmp_235_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -31788,12 +31800,12 @@ _loop1_131_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_131[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' star_target)")); - void *_tmp_235_var; + void *_tmp_236_var; while ( - (_tmp_235_var = _tmp_235_rule(p)) // ',' star_target + (_tmp_236_var = _tmp_236_rule(p)) // ',' star_target ) { - _res = _tmp_235_var; + _res = _tmp_236_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -32510,9 +32522,69 @@ _tmp_143_rule(Parser *p) return _res; } -// _tmp_144: args | expression for_if_clauses +// _tmp_144: +// | (','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs) +// | kwargs static void * _tmp_144_rule(Parser *p) +{ + if (p->level++ == MAXSTACK) { + p->error_indicator = 1; + PyErr_NoMemory(); + } + if (p->error_indicator) { + p->level--; + return NULL; + } + void * _res = NULL; + int _mark = p->mark; + { // (','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs) + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _tmp_144[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs)")); + void *_tmp_237_var; + if ( + (_tmp_237_var = _tmp_237_rule(p)) // ','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs + ) + { + D(fprintf(stderr, "%*c+ _tmp_144[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs)")); + _res = _tmp_237_var; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _tmp_144[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs)")); + } + { // kwargs + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _tmp_144[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "kwargs")); + asdl_seq* kwargs_var; + if ( + (kwargs_var = kwargs_rule(p)) // kwargs + ) + { + D(fprintf(stderr, "%*c+ _tmp_144[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "kwargs")); + _res = kwargs_var; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _tmp_144[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "kwargs")); + } + _res = NULL; + done: + p->level--; + return _res; +} + +// _tmp_145: args | expression for_if_clauses +static void * +_tmp_145_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -32529,18 +32601,18 @@ _tmp_144_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_144[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args")); + D(fprintf(stderr, "%*c> _tmp_145[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args")); expr_ty args_var; if ( (args_var = args_rule(p)) // args ) { - D(fprintf(stderr, "%*c+ _tmp_144[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args")); + D(fprintf(stderr, "%*c+ _tmp_145[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args")); _res = args_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_144[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_145[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "args")); } { // expression for_if_clauses @@ -32548,7 +32620,7 @@ _tmp_144_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_144[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses")); + D(fprintf(stderr, "%*c> _tmp_145[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses")); expr_ty expression_var; asdl_comprehension_seq* for_if_clauses_var; if ( @@ -32557,12 +32629,12 @@ _tmp_144_rule(Parser *p) (for_if_clauses_var = for_if_clauses_rule(p)) // for_if_clauses ) { - D(fprintf(stderr, "%*c+ _tmp_144[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses")); + D(fprintf(stderr, "%*c+ _tmp_145[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses")); _res = _PyPegen_dummy_name(p, expression_var, for_if_clauses_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_144[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_145[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression for_if_clauses")); } _res = NULL; @@ -32571,9 +32643,9 @@ _tmp_144_rule(Parser *p) return _res; } -// _tmp_145: 'True' | 'False' | 'None' +// _tmp_146: 'True' | 'False' | 'None' static void * -_tmp_145_rule(Parser *p) +_tmp_146_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -32590,18 +32662,18 @@ _tmp_145_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_145[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'True'")); + D(fprintf(stderr, "%*c> _tmp_146[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'True'")); Token * _keyword; if ( (_keyword = _PyPegen_expect_token(p, 600)) // token='True' ) { - D(fprintf(stderr, "%*c+ _tmp_145[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'True'")); + D(fprintf(stderr, "%*c+ _tmp_146[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'True'")); _res = _keyword; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_145[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_146[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'True'")); } { // 'False' @@ -32609,18 +32681,18 @@ _tmp_145_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_145[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'False'")); + D(fprintf(stderr, "%*c> _tmp_146[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'False'")); Token * _keyword; if ( (_keyword = _PyPegen_expect_token(p, 602)) // token='False' ) { - D(fprintf(stderr, "%*c+ _tmp_145[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'False'")); + D(fprintf(stderr, "%*c+ _tmp_146[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'False'")); _res = _keyword; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_145[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_146[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'False'")); } { // 'None' @@ -32628,18 +32700,18 @@ _tmp_145_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_145[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'None'")); + D(fprintf(stderr, "%*c> _tmp_146[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'None'")); Token * _keyword; if ( (_keyword = _PyPegen_expect_token(p, 601)) // token='None' ) { - D(fprintf(stderr, "%*c+ _tmp_145[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'None'")); + D(fprintf(stderr, "%*c+ _tmp_146[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'None'")); _res = _keyword; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_145[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_146[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'None'")); } _res = NULL; @@ -32648,9 +32720,9 @@ _tmp_145_rule(Parser *p) return _res; } -// _tmp_146: NAME '=' +// _tmp_147: NAME '=' static void * -_tmp_146_rule(Parser *p) +_tmp_147_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -32667,7 +32739,7 @@ _tmp_146_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_146[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NAME '='")); + D(fprintf(stderr, "%*c> _tmp_147[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NAME '='")); Token * _literal; expr_ty name_var; if ( @@ -32676,12 +32748,12 @@ _tmp_146_rule(Parser *p) (_literal = _PyPegen_expect_token(p, 22)) // token='=' ) { - D(fprintf(stderr, "%*c+ _tmp_146[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME '='")); + D(fprintf(stderr, "%*c+ _tmp_147[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME '='")); _res = _PyPegen_dummy_name(p, name_var, _literal); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_146[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_147[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NAME '='")); } _res = NULL; @@ -32690,9 +32762,9 @@ _tmp_146_rule(Parser *p) return _res; } -// _tmp_147: NAME STRING | SOFT_KEYWORD +// _tmp_148: NAME STRING | SOFT_KEYWORD static void * -_tmp_147_rule(Parser *p) +_tmp_148_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -32709,7 +32781,7 @@ _tmp_147_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_147[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NAME STRING")); + D(fprintf(stderr, "%*c> _tmp_148[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NAME STRING")); expr_ty name_var; expr_ty string_var; if ( @@ -32718,12 +32790,12 @@ _tmp_147_rule(Parser *p) (string_var = _PyPegen_string_token(p)) // STRING ) { - D(fprintf(stderr, "%*c+ _tmp_147[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME STRING")); + D(fprintf(stderr, "%*c+ _tmp_148[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME STRING")); _res = _PyPegen_dummy_name(p, name_var, string_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_147[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_148[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NAME STRING")); } { // SOFT_KEYWORD @@ -32731,18 +32803,18 @@ _tmp_147_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_147[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "SOFT_KEYWORD")); + D(fprintf(stderr, "%*c> _tmp_148[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "SOFT_KEYWORD")); expr_ty soft_keyword_var; if ( (soft_keyword_var = _PyPegen_soft_keyword_token(p)) // SOFT_KEYWORD ) { - D(fprintf(stderr, "%*c+ _tmp_147[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "SOFT_KEYWORD")); + D(fprintf(stderr, "%*c+ _tmp_148[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "SOFT_KEYWORD")); _res = soft_keyword_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_147[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_148[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "SOFT_KEYWORD")); } _res = NULL; @@ -32751,9 +32823,9 @@ _tmp_147_rule(Parser *p) return _res; } -// _tmp_148: 'else' | ':' +// _tmp_149: 'else' | ':' static void * -_tmp_148_rule(Parser *p) +_tmp_149_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -32770,18 +32842,18 @@ _tmp_148_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_148[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'else'")); + D(fprintf(stderr, "%*c> _tmp_149[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'else'")); Token * _keyword; if ( (_keyword = _PyPegen_expect_token(p, 642)) // token='else' ) { - D(fprintf(stderr, "%*c+ _tmp_148[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'else'")); + D(fprintf(stderr, "%*c+ _tmp_149[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'else'")); _res = _keyword; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_148[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_149[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'else'")); } { // ':' @@ -32789,18 +32861,18 @@ _tmp_148_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_148[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c> _tmp_149[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 11)) // token=':' ) { - D(fprintf(stderr, "%*c+ _tmp_148[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c+ _tmp_149[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_148[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_149[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'")); } _res = NULL; @@ -32809,9 +32881,9 @@ _tmp_148_rule(Parser *p) return _res; } -// _tmp_149: '=' | ':=' +// _tmp_150: '=' | ':=' static void * -_tmp_149_rule(Parser *p) +_tmp_150_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -32828,18 +32900,18 @@ _tmp_149_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_149[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'='")); + D(fprintf(stderr, "%*c> _tmp_150[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'='")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 22)) // token='=' ) { - D(fprintf(stderr, "%*c+ _tmp_149[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'='")); + D(fprintf(stderr, "%*c+ _tmp_150[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'='")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_149[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_150[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'='")); } { // ':=' @@ -32847,18 +32919,18 @@ _tmp_149_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_149[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':='")); + D(fprintf(stderr, "%*c> _tmp_150[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':='")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 53)) // token=':=' ) { - D(fprintf(stderr, "%*c+ _tmp_149[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':='")); + D(fprintf(stderr, "%*c+ _tmp_150[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':='")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_149[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_150[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':='")); } _res = NULL; @@ -32867,9 +32939,9 @@ _tmp_149_rule(Parser *p) return _res; } -// _tmp_150: list | tuple | genexp | 'True' | 'None' | 'False' +// _tmp_151: list | tuple | genexp | 'True' | 'None' | 'False' static void * -_tmp_150_rule(Parser *p) +_tmp_151_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -32886,18 +32958,18 @@ _tmp_150_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_150[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "list")); + D(fprintf(stderr, "%*c> _tmp_151[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "list")); expr_ty list_var; if ( (list_var = list_rule(p)) // list ) { - D(fprintf(stderr, "%*c+ _tmp_150[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "list")); + D(fprintf(stderr, "%*c+ _tmp_151[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "list")); _res = list_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_150[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_151[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "list")); } { // tuple @@ -32905,18 +32977,18 @@ _tmp_150_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_150[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "tuple")); + D(fprintf(stderr, "%*c> _tmp_151[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "tuple")); expr_ty tuple_var; if ( (tuple_var = tuple_rule(p)) // tuple ) { - D(fprintf(stderr, "%*c+ _tmp_150[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "tuple")); + D(fprintf(stderr, "%*c+ _tmp_151[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "tuple")); _res = tuple_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_150[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_151[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "tuple")); } { // genexp @@ -32924,18 +32996,18 @@ _tmp_150_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_150[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "genexp")); + D(fprintf(stderr, "%*c> _tmp_151[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "genexp")); expr_ty genexp_var; if ( (genexp_var = genexp_rule(p)) // genexp ) { - D(fprintf(stderr, "%*c+ _tmp_150[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "genexp")); + D(fprintf(stderr, "%*c+ _tmp_151[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "genexp")); _res = genexp_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_150[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_151[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "genexp")); } { // 'True' @@ -32943,18 +33015,18 @@ _tmp_150_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_150[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'True'")); + D(fprintf(stderr, "%*c> _tmp_151[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'True'")); Token * _keyword; if ( (_keyword = _PyPegen_expect_token(p, 600)) // token='True' ) { - D(fprintf(stderr, "%*c+ _tmp_150[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'True'")); + D(fprintf(stderr, "%*c+ _tmp_151[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'True'")); _res = _keyword; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_150[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_151[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'True'")); } { // 'None' @@ -32962,18 +33034,18 @@ _tmp_150_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_150[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'None'")); + D(fprintf(stderr, "%*c> _tmp_151[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'None'")); Token * _keyword; if ( (_keyword = _PyPegen_expect_token(p, 601)) // token='None' ) { - D(fprintf(stderr, "%*c+ _tmp_150[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'None'")); + D(fprintf(stderr, "%*c+ _tmp_151[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'None'")); _res = _keyword; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_150[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_151[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'None'")); } { // 'False' @@ -32981,18 +33053,18 @@ _tmp_150_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_150[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'False'")); + D(fprintf(stderr, "%*c> _tmp_151[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'False'")); Token * _keyword; if ( (_keyword = _PyPegen_expect_token(p, 602)) // token='False' ) { - D(fprintf(stderr, "%*c+ _tmp_150[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'False'")); + D(fprintf(stderr, "%*c+ _tmp_151[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'False'")); _res = _keyword; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_150[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_151[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'False'")); } _res = NULL; @@ -33001,9 +33073,9 @@ _tmp_150_rule(Parser *p) return _res; } -// _tmp_151: '=' | ':=' +// _tmp_152: '=' | ':=' static void * -_tmp_151_rule(Parser *p) +_tmp_152_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -33020,18 +33092,18 @@ _tmp_151_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_151[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'='")); + D(fprintf(stderr, "%*c> _tmp_152[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'='")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 22)) // token='=' ) { - D(fprintf(stderr, "%*c+ _tmp_151[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'='")); + D(fprintf(stderr, "%*c+ _tmp_152[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'='")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_151[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_152[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'='")); } { // ':=' @@ -33039,18 +33111,18 @@ _tmp_151_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_151[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':='")); + D(fprintf(stderr, "%*c> _tmp_152[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':='")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 53)) // token=':=' ) { - D(fprintf(stderr, "%*c+ _tmp_151[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':='")); + D(fprintf(stderr, "%*c+ _tmp_152[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':='")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_151[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_152[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':='")); } _res = NULL; @@ -33059,9 +33131,9 @@ _tmp_151_rule(Parser *p) return _res; } -// _loop0_152: star_named_expressions +// _loop0_153: star_named_expressions static asdl_seq * -_loop0_152_rule(Parser *p) +_loop0_153_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -33087,7 +33159,7 @@ _loop0_152_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_152[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_named_expressions")); + D(fprintf(stderr, "%*c> _loop0_153[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_named_expressions")); asdl_expr_seq* star_named_expressions_var; while ( (star_named_expressions_var = star_named_expressions_rule(p)) // star_named_expressions @@ -33110,7 +33182,7 @@ _loop0_152_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_152[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_153[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_named_expressions")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -33127,9 +33199,9 @@ _loop0_152_rule(Parser *p) return _seq; } -// _loop0_153: (star_targets '=') +// _loop0_154: (star_targets '=') static asdl_seq * -_loop0_153_rule(Parser *p) +_loop0_154_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -33155,13 +33227,13 @@ _loop0_153_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_153[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(star_targets '=')")); - void *_tmp_236_var; + D(fprintf(stderr, "%*c> _loop0_154[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(star_targets '=')")); + void *_tmp_238_var; while ( - (_tmp_236_var = _tmp_236_rule(p)) // star_targets '=' + (_tmp_238_var = _tmp_238_rule(p)) // star_targets '=' ) { - _res = _tmp_236_var; + _res = _tmp_238_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -33178,7 +33250,7 @@ _loop0_153_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_153[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_154[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(star_targets '=')")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -33195,9 +33267,9 @@ _loop0_153_rule(Parser *p) return _seq; } -// _loop0_154: (star_targets '=') +// _loop0_155: (star_targets '=') static asdl_seq * -_loop0_154_rule(Parser *p) +_loop0_155_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -33223,13 +33295,13 @@ _loop0_154_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_154[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(star_targets '=')")); - void *_tmp_237_var; + D(fprintf(stderr, "%*c> _loop0_155[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(star_targets '=')")); + void *_tmp_239_var; while ( - (_tmp_237_var = _tmp_237_rule(p)) // star_targets '=' + (_tmp_239_var = _tmp_239_rule(p)) // star_targets '=' ) { - _res = _tmp_237_var; + _res = _tmp_239_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -33246,7 +33318,7 @@ _loop0_154_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_154[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_155[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(star_targets '=')")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -33263,9 +33335,9 @@ _loop0_154_rule(Parser *p) return _seq; } -// _tmp_155: yield_expr | star_expressions +// _tmp_156: yield_expr | star_expressions static void * -_tmp_155_rule(Parser *p) +_tmp_156_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -33282,18 +33354,18 @@ _tmp_155_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_155[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "yield_expr")); + D(fprintf(stderr, "%*c> _tmp_156[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "yield_expr")); expr_ty yield_expr_var; if ( (yield_expr_var = yield_expr_rule(p)) // yield_expr ) { - D(fprintf(stderr, "%*c+ _tmp_155[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "yield_expr")); + D(fprintf(stderr, "%*c+ _tmp_156[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "yield_expr")); _res = yield_expr_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_155[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_156[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "yield_expr")); } { // star_expressions @@ -33301,18 +33373,18 @@ _tmp_155_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_155[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_expressions")); + D(fprintf(stderr, "%*c> _tmp_156[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_expressions")); expr_ty star_expressions_var; if ( (star_expressions_var = star_expressions_rule(p)) // star_expressions ) { - D(fprintf(stderr, "%*c+ _tmp_155[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_expressions")); + D(fprintf(stderr, "%*c+ _tmp_156[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_expressions")); _res = star_expressions_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_155[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_156[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_expressions")); } _res = NULL; @@ -33321,9 +33393,9 @@ _tmp_155_rule(Parser *p) return _res; } -// _tmp_156: '[' | '(' | '{' +// _tmp_157: '[' | '(' | '{' static void * -_tmp_156_rule(Parser *p) +_tmp_157_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -33340,18 +33412,18 @@ _tmp_156_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_156[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'['")); + D(fprintf(stderr, "%*c> _tmp_157[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'['")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 9)) // token='[' ) { - D(fprintf(stderr, "%*c+ _tmp_156[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'['")); + D(fprintf(stderr, "%*c+ _tmp_157[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'['")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_156[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_157[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'['")); } { // '(' @@ -33359,18 +33431,18 @@ _tmp_156_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_156[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'('")); + D(fprintf(stderr, "%*c> _tmp_157[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'('")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 7)) // token='(' ) { - D(fprintf(stderr, "%*c+ _tmp_156[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'('")); + D(fprintf(stderr, "%*c+ _tmp_157[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'('")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_156[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_157[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'('")); } { // '{' @@ -33378,18 +33450,18 @@ _tmp_156_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_156[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{'")); + D(fprintf(stderr, "%*c> _tmp_157[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 25)) // token='{' ) { - D(fprintf(stderr, "%*c+ _tmp_156[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{'")); + D(fprintf(stderr, "%*c+ _tmp_157[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_156[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_157[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{'")); } _res = NULL; @@ -33398,9 +33470,9 @@ _tmp_156_rule(Parser *p) return _res; } -// _tmp_157: '[' | '{' +// _tmp_158: '[' | '{' static void * -_tmp_157_rule(Parser *p) +_tmp_158_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -33417,18 +33489,18 @@ _tmp_157_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_157[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'['")); + D(fprintf(stderr, "%*c> _tmp_158[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'['")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 9)) // token='[' ) { - D(fprintf(stderr, "%*c+ _tmp_157[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'['")); + D(fprintf(stderr, "%*c+ _tmp_158[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'['")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_157[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_158[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'['")); } { // '{' @@ -33436,18 +33508,18 @@ _tmp_157_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_157[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{'")); + D(fprintf(stderr, "%*c> _tmp_158[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 25)) // token='{' ) { - D(fprintf(stderr, "%*c+ _tmp_157[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{'")); + D(fprintf(stderr, "%*c+ _tmp_158[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_157[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_158[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{'")); } _res = NULL; @@ -33456,9 +33528,9 @@ _tmp_157_rule(Parser *p) return _res; } -// _tmp_158: '[' | '{' +// _tmp_159: '[' | '{' static void * -_tmp_158_rule(Parser *p) +_tmp_159_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -33475,18 +33547,18 @@ _tmp_158_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_158[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'['")); + D(fprintf(stderr, "%*c> _tmp_159[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'['")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 9)) // token='[' ) { - D(fprintf(stderr, "%*c+ _tmp_158[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'['")); + D(fprintf(stderr, "%*c+ _tmp_159[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'['")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_158[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_159[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'['")); } { // '{' @@ -33494,18 +33566,18 @@ _tmp_158_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_158[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{'")); + D(fprintf(stderr, "%*c> _tmp_159[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 25)) // token='{' ) { - D(fprintf(stderr, "%*c+ _tmp_158[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{'")); + D(fprintf(stderr, "%*c+ _tmp_159[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_158[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_159[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{'")); } _res = NULL; @@ -33514,9 +33586,9 @@ _tmp_158_rule(Parser *p) return _res; } -// _loop0_159: param_no_default +// _loop0_160: param_no_default static asdl_seq * -_loop0_159_rule(Parser *p) +_loop0_160_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -33542,7 +33614,7 @@ _loop0_159_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_159[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default")); + D(fprintf(stderr, "%*c> _loop0_160[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default")); arg_ty param_no_default_var; while ( (param_no_default_var = param_no_default_rule(p)) // param_no_default @@ -33565,7 +33637,7 @@ _loop0_159_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_159[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_160[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_no_default")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -33582,9 +33654,9 @@ _loop0_159_rule(Parser *p) return _seq; } -// _loop0_160: param_no_default +// _loop0_161: param_no_default static asdl_seq * -_loop0_160_rule(Parser *p) +_loop0_161_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -33610,7 +33682,7 @@ _loop0_160_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_160[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default")); + D(fprintf(stderr, "%*c> _loop0_161[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default")); arg_ty param_no_default_var; while ( (param_no_default_var = param_no_default_rule(p)) // param_no_default @@ -33633,7 +33705,7 @@ _loop0_160_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_160[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_161[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_no_default")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -33650,9 +33722,9 @@ _loop0_160_rule(Parser *p) return _seq; } -// _loop1_161: param_no_default +// _loop1_162: param_no_default static asdl_seq * -_loop1_161_rule(Parser *p) +_loop1_162_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -33678,7 +33750,7 @@ _loop1_161_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop1_161[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default")); + D(fprintf(stderr, "%*c> _loop1_162[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default")); arg_ty param_no_default_var; while ( (param_no_default_var = param_no_default_rule(p)) // param_no_default @@ -33701,7 +33773,7 @@ _loop1_161_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop1_161[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop1_162[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_no_default")); } if (_n == 0 || p->error_indicator) { @@ -33723,9 +33795,9 @@ _loop1_161_rule(Parser *p) return _seq; } -// _tmp_162: slash_no_default | slash_with_default +// _tmp_163: slash_no_default | slash_with_default static void * -_tmp_162_rule(Parser *p) +_tmp_163_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -33742,18 +33814,18 @@ _tmp_162_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_162[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slash_no_default")); + D(fprintf(stderr, "%*c> _tmp_163[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slash_no_default")); asdl_arg_seq* slash_no_default_var; if ( (slash_no_default_var = slash_no_default_rule(p)) // slash_no_default ) { - D(fprintf(stderr, "%*c+ _tmp_162[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_no_default")); + D(fprintf(stderr, "%*c+ _tmp_163[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_no_default")); _res = slash_no_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_162[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_163[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "slash_no_default")); } { // slash_with_default @@ -33761,18 +33833,18 @@ _tmp_162_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_162[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slash_with_default")); + D(fprintf(stderr, "%*c> _tmp_163[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slash_with_default")); SlashWithDefault* slash_with_default_var; if ( (slash_with_default_var = slash_with_default_rule(p)) // slash_with_default ) { - D(fprintf(stderr, "%*c+ _tmp_162[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_with_default")); + D(fprintf(stderr, "%*c+ _tmp_163[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_with_default")); _res = slash_with_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_162[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_163[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "slash_with_default")); } _res = NULL; @@ -33781,9 +33853,9 @@ _tmp_162_rule(Parser *p) return _res; } -// _loop0_163: param_maybe_default +// _loop0_164: param_maybe_default static asdl_seq * -_loop0_163_rule(Parser *p) +_loop0_164_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -33809,7 +33881,7 @@ _loop0_163_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_163[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_maybe_default")); + D(fprintf(stderr, "%*c> _loop0_164[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_maybe_default")); NameDefaultPair* param_maybe_default_var; while ( (param_maybe_default_var = param_maybe_default_rule(p)) // param_maybe_default @@ -33832,7 +33904,7 @@ _loop0_163_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_163[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_164[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_maybe_default")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -33849,9 +33921,9 @@ _loop0_163_rule(Parser *p) return _seq; } -// _tmp_164: slash_no_default | slash_with_default +// _tmp_165: slash_no_default | slash_with_default static void * -_tmp_164_rule(Parser *p) +_tmp_165_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -33868,18 +33940,18 @@ _tmp_164_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_164[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slash_no_default")); + D(fprintf(stderr, "%*c> _tmp_165[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slash_no_default")); asdl_arg_seq* slash_no_default_var; if ( (slash_no_default_var = slash_no_default_rule(p)) // slash_no_default ) { - D(fprintf(stderr, "%*c+ _tmp_164[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_no_default")); + D(fprintf(stderr, "%*c+ _tmp_165[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_no_default")); _res = slash_no_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_164[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_165[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "slash_no_default")); } { // slash_with_default @@ -33887,18 +33959,18 @@ _tmp_164_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_164[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slash_with_default")); + D(fprintf(stderr, "%*c> _tmp_165[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slash_with_default")); SlashWithDefault* slash_with_default_var; if ( (slash_with_default_var = slash_with_default_rule(p)) // slash_with_default ) { - D(fprintf(stderr, "%*c+ _tmp_164[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_with_default")); + D(fprintf(stderr, "%*c+ _tmp_165[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_with_default")); _res = slash_with_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_164[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_165[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "slash_with_default")); } _res = NULL; @@ -33907,9 +33979,9 @@ _tmp_164_rule(Parser *p) return _res; } -// _loop0_165: param_maybe_default +// _loop0_166: param_maybe_default static asdl_seq * -_loop0_165_rule(Parser *p) +_loop0_166_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -33935,7 +34007,7 @@ _loop0_165_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_165[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_maybe_default")); + D(fprintf(stderr, "%*c> _loop0_166[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_maybe_default")); NameDefaultPair* param_maybe_default_var; while ( (param_maybe_default_var = param_maybe_default_rule(p)) // param_maybe_default @@ -33958,7 +34030,7 @@ _loop0_165_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_165[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_166[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_maybe_default")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -33975,9 +34047,9 @@ _loop0_165_rule(Parser *p) return _seq; } -// _tmp_166: ',' | param_no_default +// _tmp_167: ',' | param_no_default static void * -_tmp_166_rule(Parser *p) +_tmp_167_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -33994,18 +34066,18 @@ _tmp_166_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_166[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c> _tmp_167[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_166[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c+ _tmp_167[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_166[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_167[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); } { // param_no_default @@ -34013,18 +34085,18 @@ _tmp_166_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_166[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default")); + D(fprintf(stderr, "%*c> _tmp_167[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default")); arg_ty param_no_default_var; if ( (param_no_default_var = param_no_default_rule(p)) // param_no_default ) { - D(fprintf(stderr, "%*c+ _tmp_166[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_no_default")); + D(fprintf(stderr, "%*c+ _tmp_167[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_no_default")); _res = param_no_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_166[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_167[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_no_default")); } _res = NULL; @@ -34033,9 +34105,9 @@ _tmp_166_rule(Parser *p) return _res; } -// _loop0_167: param_maybe_default +// _loop0_168: param_maybe_default static asdl_seq * -_loop0_167_rule(Parser *p) +_loop0_168_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -34061,7 +34133,7 @@ _loop0_167_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_167[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_maybe_default")); + D(fprintf(stderr, "%*c> _loop0_168[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_maybe_default")); NameDefaultPair* param_maybe_default_var; while ( (param_maybe_default_var = param_maybe_default_rule(p)) // param_maybe_default @@ -34084,7 +34156,7 @@ _loop0_167_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_167[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_168[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_maybe_default")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -34101,9 +34173,9 @@ _loop0_167_rule(Parser *p) return _seq; } -// _loop1_168: param_maybe_default +// _loop1_169: param_maybe_default static asdl_seq * -_loop1_168_rule(Parser *p) +_loop1_169_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -34129,7 +34201,7 @@ _loop1_168_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop1_168[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_maybe_default")); + D(fprintf(stderr, "%*c> _loop1_169[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_maybe_default")); NameDefaultPair* param_maybe_default_var; while ( (param_maybe_default_var = param_maybe_default_rule(p)) // param_maybe_default @@ -34152,7 +34224,7 @@ _loop1_168_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop1_168[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop1_169[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_maybe_default")); } if (_n == 0 || p->error_indicator) { @@ -34174,9 +34246,9 @@ _loop1_168_rule(Parser *p) return _seq; } -// _tmp_169: ')' | ',' +// _tmp_170: ')' | ',' static void * -_tmp_169_rule(Parser *p) +_tmp_170_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -34193,18 +34265,18 @@ _tmp_169_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_169[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c> _tmp_170[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 8)) // token=')' ) { - D(fprintf(stderr, "%*c+ _tmp_169[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c+ _tmp_170[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_169[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_170[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "')'")); } { // ',' @@ -34212,18 +34284,18 @@ _tmp_169_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_169[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c> _tmp_170[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_169[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c+ _tmp_170[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_169[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_170[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); } _res = NULL; @@ -34232,9 +34304,9 @@ _tmp_169_rule(Parser *p) return _res; } -// _tmp_170: ')' | ',' (')' | '**') +// _tmp_171: ')' | ',' (')' | '**') static void * -_tmp_170_rule(Parser *p) +_tmp_171_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -34251,18 +34323,18 @@ _tmp_170_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_170[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c> _tmp_171[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 8)) // token=')' ) { - D(fprintf(stderr, "%*c+ _tmp_170[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c+ _tmp_171[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_170[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_171[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "')'")); } { // ',' (')' | '**') @@ -34270,21 +34342,21 @@ _tmp_170_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_170[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (')' | '**')")); + D(fprintf(stderr, "%*c> _tmp_171[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (')' | '**')")); Token * _literal; - void *_tmp_238_var; + void *_tmp_240_var; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (_tmp_238_var = _tmp_238_rule(p)) // ')' | '**' + (_tmp_240_var = _tmp_240_rule(p)) // ')' | '**' ) { - D(fprintf(stderr, "%*c+ _tmp_170[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' (')' | '**')")); - _res = _PyPegen_dummy_name(p, _literal, _tmp_238_var); + D(fprintf(stderr, "%*c+ _tmp_171[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' (')' | '**')")); + _res = _PyPegen_dummy_name(p, _literal, _tmp_240_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_170[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_171[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (')' | '**')")); } _res = NULL; @@ -34293,9 +34365,9 @@ _tmp_170_rule(Parser *p) return _res; } -// _tmp_171: param_no_default | ',' +// _tmp_172: param_no_default | ',' static void * -_tmp_171_rule(Parser *p) +_tmp_172_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -34312,18 +34384,18 @@ _tmp_171_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_171[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default")); + D(fprintf(stderr, "%*c> _tmp_172[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default")); arg_ty param_no_default_var; if ( (param_no_default_var = param_no_default_rule(p)) // param_no_default ) { - D(fprintf(stderr, "%*c+ _tmp_171[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_no_default")); + D(fprintf(stderr, "%*c+ _tmp_172[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_no_default")); _res = param_no_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_171[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_172[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_no_default")); } { // ',' @@ -34331,18 +34403,18 @@ _tmp_171_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_171[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c> _tmp_172[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_171[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c+ _tmp_172[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_171[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_172[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); } _res = NULL; @@ -34351,9 +34423,9 @@ _tmp_171_rule(Parser *p) return _res; } -// _loop0_172: param_maybe_default +// _loop0_173: param_maybe_default static asdl_seq * -_loop0_172_rule(Parser *p) +_loop0_173_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -34379,7 +34451,7 @@ _loop0_172_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_172[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_maybe_default")); + D(fprintf(stderr, "%*c> _loop0_173[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_maybe_default")); NameDefaultPair* param_maybe_default_var; while ( (param_maybe_default_var = param_maybe_default_rule(p)) // param_maybe_default @@ -34402,7 +34474,7 @@ _loop0_172_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_172[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_173[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_maybe_default")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -34419,9 +34491,9 @@ _loop0_172_rule(Parser *p) return _seq; } -// _tmp_173: param_no_default | ',' +// _tmp_174: param_no_default | ',' static void * -_tmp_173_rule(Parser *p) +_tmp_174_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -34438,18 +34510,18 @@ _tmp_173_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_173[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default")); + D(fprintf(stderr, "%*c> _tmp_174[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default")); arg_ty param_no_default_var; if ( (param_no_default_var = param_no_default_rule(p)) // param_no_default ) { - D(fprintf(stderr, "%*c+ _tmp_173[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_no_default")); + D(fprintf(stderr, "%*c+ _tmp_174[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_no_default")); _res = param_no_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_173[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_174[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_no_default")); } { // ',' @@ -34457,18 +34529,18 @@ _tmp_173_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_173[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c> _tmp_174[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_173[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c+ _tmp_174[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_173[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_174[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); } _res = NULL; @@ -34477,9 +34549,9 @@ _tmp_173_rule(Parser *p) return _res; } -// _tmp_174: '*' | '**' | '/' +// _tmp_175: '*' | '**' | '/' static void * -_tmp_174_rule(Parser *p) +_tmp_175_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -34496,18 +34568,18 @@ _tmp_174_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_174[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*'")); + D(fprintf(stderr, "%*c> _tmp_175[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 16)) // token='*' ) { - D(fprintf(stderr, "%*c+ _tmp_174[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*'")); + D(fprintf(stderr, "%*c+ _tmp_175[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_174[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_175[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'*'")); } { // '**' @@ -34515,18 +34587,18 @@ _tmp_174_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_174[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'")); + D(fprintf(stderr, "%*c> _tmp_175[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 35)) // token='**' ) { - D(fprintf(stderr, "%*c+ _tmp_174[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'")); + D(fprintf(stderr, "%*c+ _tmp_175[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_174[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_175[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'**'")); } { // '/' @@ -34534,18 +34606,18 @@ _tmp_174_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_174[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'/'")); + D(fprintf(stderr, "%*c> _tmp_175[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'/'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 17)) // token='/' ) { - D(fprintf(stderr, "%*c+ _tmp_174[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'/'")); + D(fprintf(stderr, "%*c+ _tmp_175[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'/'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_174[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_175[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'/'")); } _res = NULL; @@ -34554,9 +34626,9 @@ _tmp_174_rule(Parser *p) return _res; } -// _loop1_175: param_with_default +// _loop1_176: param_with_default static asdl_seq * -_loop1_175_rule(Parser *p) +_loop1_176_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -34582,7 +34654,7 @@ _loop1_175_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop1_175[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_with_default")); + D(fprintf(stderr, "%*c> _loop1_176[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_with_default")); NameDefaultPair* param_with_default_var; while ( (param_with_default_var = param_with_default_rule(p)) // param_with_default @@ -34605,7 +34677,7 @@ _loop1_175_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop1_175[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop1_176[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_with_default")); } if (_n == 0 || p->error_indicator) { @@ -34627,9 +34699,9 @@ _loop1_175_rule(Parser *p) return _seq; } -// _loop0_176: lambda_param_no_default +// _loop0_177: lambda_param_no_default static asdl_seq * -_loop0_176_rule(Parser *p) +_loop0_177_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -34655,7 +34727,7 @@ _loop0_176_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_176[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); + D(fprintf(stderr, "%*c> _loop0_177[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); arg_ty lambda_param_no_default_var; while ( (lambda_param_no_default_var = lambda_param_no_default_rule(p)) // lambda_param_no_default @@ -34678,7 +34750,7 @@ _loop0_176_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_176[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_177[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_no_default")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -34695,9 +34767,9 @@ _loop0_176_rule(Parser *p) return _seq; } -// _loop0_177: lambda_param_no_default +// _loop0_178: lambda_param_no_default static asdl_seq * -_loop0_177_rule(Parser *p) +_loop0_178_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -34723,7 +34795,7 @@ _loop0_177_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_177[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); + D(fprintf(stderr, "%*c> _loop0_178[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); arg_ty lambda_param_no_default_var; while ( (lambda_param_no_default_var = lambda_param_no_default_rule(p)) // lambda_param_no_default @@ -34746,7 +34818,7 @@ _loop0_177_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_177[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_178[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_no_default")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -34763,9 +34835,9 @@ _loop0_177_rule(Parser *p) return _seq; } -// _loop0_179: ',' lambda_param +// _loop0_180: ',' lambda_param static asdl_seq * -_loop0_179_rule(Parser *p) +_loop0_180_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -34791,7 +34863,7 @@ _loop0_179_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_179[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' lambda_param")); + D(fprintf(stderr, "%*c> _loop0_180[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' lambda_param")); Token * _literal; arg_ty elem; while ( @@ -34823,7 +34895,7 @@ _loop0_179_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_179[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_180[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' lambda_param")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -34840,9 +34912,9 @@ _loop0_179_rule(Parser *p) return _seq; } -// _gather_178: lambda_param _loop0_179 +// _gather_179: lambda_param _loop0_180 static asdl_seq * -_gather_178_rule(Parser *p) +_gather_179_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -34854,27 +34926,27 @@ _gather_178_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // lambda_param _loop0_179 + { // lambda_param _loop0_180 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_178[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param _loop0_179")); + D(fprintf(stderr, "%*c> _gather_179[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param _loop0_180")); arg_ty elem; asdl_seq * seq; if ( (elem = lambda_param_rule(p)) // lambda_param && - (seq = _loop0_179_rule(p)) // _loop0_179 + (seq = _loop0_180_rule(p)) // _loop0_180 ) { - D(fprintf(stderr, "%*c+ _gather_178[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param _loop0_179")); + D(fprintf(stderr, "%*c+ _gather_179[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param _loop0_180")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_178[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param _loop0_179")); + D(fprintf(stderr, "%*c%s _gather_179[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param _loop0_180")); } _res = NULL; done: @@ -34882,9 +34954,9 @@ _gather_178_rule(Parser *p) return _res; } -// _tmp_180: lambda_slash_no_default | lambda_slash_with_default +// _tmp_181: lambda_slash_no_default | lambda_slash_with_default static void * -_tmp_180_rule(Parser *p) +_tmp_181_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -34901,18 +34973,18 @@ _tmp_180_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_180[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_slash_no_default")); + D(fprintf(stderr, "%*c> _tmp_181[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_slash_no_default")); asdl_arg_seq* lambda_slash_no_default_var; if ( (lambda_slash_no_default_var = lambda_slash_no_default_rule(p)) // lambda_slash_no_default ) { - D(fprintf(stderr, "%*c+ _tmp_180[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_no_default")); + D(fprintf(stderr, "%*c+ _tmp_181[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_no_default")); _res = lambda_slash_no_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_180[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_181[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_slash_no_default")); } { // lambda_slash_with_default @@ -34920,18 +34992,18 @@ _tmp_180_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_180[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default")); + D(fprintf(stderr, "%*c> _tmp_181[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default")); SlashWithDefault* lambda_slash_with_default_var; if ( (lambda_slash_with_default_var = lambda_slash_with_default_rule(p)) // lambda_slash_with_default ) { - D(fprintf(stderr, "%*c+ _tmp_180[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default")); + D(fprintf(stderr, "%*c+ _tmp_181[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default")); _res = lambda_slash_with_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_180[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_181[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_slash_with_default")); } _res = NULL; @@ -34940,9 +35012,9 @@ _tmp_180_rule(Parser *p) return _res; } -// _loop0_181: lambda_param_maybe_default +// _loop0_182: lambda_param_maybe_default static asdl_seq * -_loop0_181_rule(Parser *p) +_loop0_182_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -34968,7 +35040,7 @@ _loop0_181_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_181[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_maybe_default")); + D(fprintf(stderr, "%*c> _loop0_182[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_maybe_default")); NameDefaultPair* lambda_param_maybe_default_var; while ( (lambda_param_maybe_default_var = lambda_param_maybe_default_rule(p)) // lambda_param_maybe_default @@ -34991,7 +35063,7 @@ _loop0_181_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_181[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_182[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_maybe_default")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -35008,9 +35080,9 @@ _loop0_181_rule(Parser *p) return _seq; } -// _tmp_182: lambda_slash_no_default | lambda_slash_with_default +// _tmp_183: lambda_slash_no_default | lambda_slash_with_default static void * -_tmp_182_rule(Parser *p) +_tmp_183_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -35027,18 +35099,18 @@ _tmp_182_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_182[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_slash_no_default")); + D(fprintf(stderr, "%*c> _tmp_183[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_slash_no_default")); asdl_arg_seq* lambda_slash_no_default_var; if ( (lambda_slash_no_default_var = lambda_slash_no_default_rule(p)) // lambda_slash_no_default ) { - D(fprintf(stderr, "%*c+ _tmp_182[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_no_default")); + D(fprintf(stderr, "%*c+ _tmp_183[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_no_default")); _res = lambda_slash_no_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_182[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_183[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_slash_no_default")); } { // lambda_slash_with_default @@ -35046,18 +35118,18 @@ _tmp_182_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_182[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default")); + D(fprintf(stderr, "%*c> _tmp_183[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default")); SlashWithDefault* lambda_slash_with_default_var; if ( (lambda_slash_with_default_var = lambda_slash_with_default_rule(p)) // lambda_slash_with_default ) { - D(fprintf(stderr, "%*c+ _tmp_182[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default")); + D(fprintf(stderr, "%*c+ _tmp_183[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default")); _res = lambda_slash_with_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_182[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_183[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_slash_with_default")); } _res = NULL; @@ -35066,9 +35138,9 @@ _tmp_182_rule(Parser *p) return _res; } -// _loop0_183: lambda_param_maybe_default +// _loop0_184: lambda_param_maybe_default static asdl_seq * -_loop0_183_rule(Parser *p) +_loop0_184_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -35094,7 +35166,7 @@ _loop0_183_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_183[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_maybe_default")); + D(fprintf(stderr, "%*c> _loop0_184[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_maybe_default")); NameDefaultPair* lambda_param_maybe_default_var; while ( (lambda_param_maybe_default_var = lambda_param_maybe_default_rule(p)) // lambda_param_maybe_default @@ -35117,7 +35189,7 @@ _loop0_183_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_183[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_184[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_maybe_default")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -35134,9 +35206,9 @@ _loop0_183_rule(Parser *p) return _seq; } -// _tmp_184: ',' | lambda_param_no_default +// _tmp_185: ',' | lambda_param_no_default static void * -_tmp_184_rule(Parser *p) +_tmp_185_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -35153,18 +35225,18 @@ _tmp_184_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_184[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c> _tmp_185[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_184[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c+ _tmp_185[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_184[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_185[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); } { // lambda_param_no_default @@ -35172,18 +35244,18 @@ _tmp_184_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_184[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); + D(fprintf(stderr, "%*c> _tmp_185[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); arg_ty lambda_param_no_default_var; if ( (lambda_param_no_default_var = lambda_param_no_default_rule(p)) // lambda_param_no_default ) { - D(fprintf(stderr, "%*c+ _tmp_184[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); + D(fprintf(stderr, "%*c+ _tmp_185[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); _res = lambda_param_no_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_184[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_185[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_no_default")); } _res = NULL; @@ -35192,9 +35264,9 @@ _tmp_184_rule(Parser *p) return _res; } -// _loop0_185: lambda_param_maybe_default +// _loop0_186: lambda_param_maybe_default static asdl_seq * -_loop0_185_rule(Parser *p) +_loop0_186_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -35220,7 +35292,7 @@ _loop0_185_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_185[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_maybe_default")); + D(fprintf(stderr, "%*c> _loop0_186[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_maybe_default")); NameDefaultPair* lambda_param_maybe_default_var; while ( (lambda_param_maybe_default_var = lambda_param_maybe_default_rule(p)) // lambda_param_maybe_default @@ -35243,7 +35315,7 @@ _loop0_185_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_185[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_186[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_maybe_default")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -35260,9 +35332,9 @@ _loop0_185_rule(Parser *p) return _seq; } -// _loop1_186: lambda_param_maybe_default +// _loop1_187: lambda_param_maybe_default static asdl_seq * -_loop1_186_rule(Parser *p) +_loop1_187_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -35288,7 +35360,7 @@ _loop1_186_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop1_186[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_maybe_default")); + D(fprintf(stderr, "%*c> _loop1_187[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_maybe_default")); NameDefaultPair* lambda_param_maybe_default_var; while ( (lambda_param_maybe_default_var = lambda_param_maybe_default_rule(p)) // lambda_param_maybe_default @@ -35311,7 +35383,7 @@ _loop1_186_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop1_186[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop1_187[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_maybe_default")); } if (_n == 0 || p->error_indicator) { @@ -35333,9 +35405,9 @@ _loop1_186_rule(Parser *p) return _seq; } -// _loop1_187: lambda_param_with_default +// _loop1_188: lambda_param_with_default static asdl_seq * -_loop1_187_rule(Parser *p) +_loop1_188_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -35361,7 +35433,7 @@ _loop1_187_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop1_187[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default")); + D(fprintf(stderr, "%*c> _loop1_188[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default")); NameDefaultPair* lambda_param_with_default_var; while ( (lambda_param_with_default_var = lambda_param_with_default_rule(p)) // lambda_param_with_default @@ -35384,7 +35456,7 @@ _loop1_187_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop1_187[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop1_188[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_with_default")); } if (_n == 0 || p->error_indicator) { @@ -35406,9 +35478,9 @@ _loop1_187_rule(Parser *p) return _seq; } -// _tmp_188: ':' | ',' (':' | '**') +// _tmp_189: ':' | ',' (':' | '**') static void * -_tmp_188_rule(Parser *p) +_tmp_189_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -35425,18 +35497,18 @@ _tmp_188_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_188[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c> _tmp_189[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 11)) // token=':' ) { - D(fprintf(stderr, "%*c+ _tmp_188[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c+ _tmp_189[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_188[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_189[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'")); } { // ',' (':' | '**') @@ -35444,21 +35516,21 @@ _tmp_188_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_188[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (':' | '**')")); + D(fprintf(stderr, "%*c> _tmp_189[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (':' | '**')")); Token * _literal; - void *_tmp_239_var; + void *_tmp_241_var; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (_tmp_239_var = _tmp_239_rule(p)) // ':' | '**' + (_tmp_241_var = _tmp_241_rule(p)) // ':' | '**' ) { - D(fprintf(stderr, "%*c+ _tmp_188[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' (':' | '**')")); - _res = _PyPegen_dummy_name(p, _literal, _tmp_239_var); + D(fprintf(stderr, "%*c+ _tmp_189[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' (':' | '**')")); + _res = _PyPegen_dummy_name(p, _literal, _tmp_241_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_188[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_189[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (':' | '**')")); } _res = NULL; @@ -35467,9 +35539,9 @@ _tmp_188_rule(Parser *p) return _res; } -// _tmp_189: lambda_param_no_default | ',' +// _tmp_190: lambda_param_no_default | ',' static void * -_tmp_189_rule(Parser *p) +_tmp_190_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -35486,18 +35558,18 @@ _tmp_189_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_189[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); + D(fprintf(stderr, "%*c> _tmp_190[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); arg_ty lambda_param_no_default_var; if ( (lambda_param_no_default_var = lambda_param_no_default_rule(p)) // lambda_param_no_default ) { - D(fprintf(stderr, "%*c+ _tmp_189[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); + D(fprintf(stderr, "%*c+ _tmp_190[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); _res = lambda_param_no_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_189[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_190[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_no_default")); } { // ',' @@ -35505,18 +35577,18 @@ _tmp_189_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_189[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c> _tmp_190[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_189[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c+ _tmp_190[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_189[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_190[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); } _res = NULL; @@ -35525,9 +35597,9 @@ _tmp_189_rule(Parser *p) return _res; } -// _loop0_190: lambda_param_maybe_default +// _loop0_191: lambda_param_maybe_default static asdl_seq * -_loop0_190_rule(Parser *p) +_loop0_191_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -35553,7 +35625,7 @@ _loop0_190_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_190[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_maybe_default")); + D(fprintf(stderr, "%*c> _loop0_191[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_maybe_default")); NameDefaultPair* lambda_param_maybe_default_var; while ( (lambda_param_maybe_default_var = lambda_param_maybe_default_rule(p)) // lambda_param_maybe_default @@ -35576,7 +35648,7 @@ _loop0_190_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_190[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_191[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_maybe_default")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -35593,9 +35665,9 @@ _loop0_190_rule(Parser *p) return _seq; } -// _tmp_191: lambda_param_no_default | ',' +// _tmp_192: lambda_param_no_default | ',' static void * -_tmp_191_rule(Parser *p) +_tmp_192_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -35612,18 +35684,18 @@ _tmp_191_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_191[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); + D(fprintf(stderr, "%*c> _tmp_192[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); arg_ty lambda_param_no_default_var; if ( (lambda_param_no_default_var = lambda_param_no_default_rule(p)) // lambda_param_no_default ) { - D(fprintf(stderr, "%*c+ _tmp_191[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); + D(fprintf(stderr, "%*c+ _tmp_192[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); _res = lambda_param_no_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_191[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_192[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_no_default")); } { // ',' @@ -35631,18 +35703,18 @@ _tmp_191_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_191[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c> _tmp_192[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_191[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c+ _tmp_192[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_191[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_192[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); } _res = NULL; @@ -35651,9 +35723,9 @@ _tmp_191_rule(Parser *p) return _res; } -// _tmp_192: '*' | '**' | '/' +// _tmp_193: '*' | '**' | '/' static void * -_tmp_192_rule(Parser *p) +_tmp_193_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -35670,18 +35742,18 @@ _tmp_192_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_192[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*'")); + D(fprintf(stderr, "%*c> _tmp_193[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 16)) // token='*' ) { - D(fprintf(stderr, "%*c+ _tmp_192[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*'")); + D(fprintf(stderr, "%*c+ _tmp_193[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_192[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_193[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'*'")); } { // '**' @@ -35689,18 +35761,18 @@ _tmp_192_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_192[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'")); + D(fprintf(stderr, "%*c> _tmp_193[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 35)) // token='**' ) { - D(fprintf(stderr, "%*c+ _tmp_192[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'")); + D(fprintf(stderr, "%*c+ _tmp_193[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_192[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_193[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'**'")); } { // '/' @@ -35708,18 +35780,18 @@ _tmp_192_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_192[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'/'")); + D(fprintf(stderr, "%*c> _tmp_193[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'/'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 17)) // token='/' ) { - D(fprintf(stderr, "%*c+ _tmp_192[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'/'")); + D(fprintf(stderr, "%*c+ _tmp_193[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'/'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_192[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_193[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'/'")); } _res = NULL; @@ -35728,9 +35800,9 @@ _tmp_192_rule(Parser *p) return _res; } -// _tmp_193: ',' | ')' | ':' +// _tmp_194: ',' | ')' | ':' static void * -_tmp_193_rule(Parser *p) +_tmp_194_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -35747,18 +35819,18 @@ _tmp_193_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_193[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c> _tmp_194[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_193[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c+ _tmp_194[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_193[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_194[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); } { // ')' @@ -35766,18 +35838,18 @@ _tmp_193_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_193[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c> _tmp_194[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 8)) // token=')' ) { - D(fprintf(stderr, "%*c+ _tmp_193[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c+ _tmp_194[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_193[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_194[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "')'")); } { // ':' @@ -35785,18 +35857,18 @@ _tmp_193_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_193[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c> _tmp_194[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 11)) // token=':' ) { - D(fprintf(stderr, "%*c+ _tmp_193[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c+ _tmp_194[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_193[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_194[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'")); } _res = NULL; @@ -35805,9 +35877,9 @@ _tmp_193_rule(Parser *p) return _res; } -// _loop0_195: ',' (expression ['as' star_target]) +// _loop0_196: ',' (expression ['as' star_target]) static asdl_seq * -_loop0_195_rule(Parser *p) +_loop0_196_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -35833,13 +35905,13 @@ _loop0_195_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_195[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (expression ['as' star_target])")); + D(fprintf(stderr, "%*c> _loop0_196[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (expression ['as' star_target])")); Token * _literal; void *elem; while ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (elem = _tmp_240_rule(p)) // expression ['as' star_target] + (elem = _tmp_242_rule(p)) // expression ['as' star_target] ) { _res = elem; @@ -35865,7 +35937,7 @@ _loop0_195_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_195[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_196[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (expression ['as' star_target])")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -35882,9 +35954,9 @@ _loop0_195_rule(Parser *p) return _seq; } -// _gather_194: (expression ['as' star_target]) _loop0_195 +// _gather_195: (expression ['as' star_target]) _loop0_196 static asdl_seq * -_gather_194_rule(Parser *p) +_gather_195_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -35896,27 +35968,27 @@ _gather_194_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // (expression ['as' star_target]) _loop0_195 + { // (expression ['as' star_target]) _loop0_196 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_194[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(expression ['as' star_target]) _loop0_195")); + D(fprintf(stderr, "%*c> _gather_195[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(expression ['as' star_target]) _loop0_196")); void *elem; asdl_seq * seq; if ( - (elem = _tmp_240_rule(p)) // expression ['as' star_target] + (elem = _tmp_242_rule(p)) // expression ['as' star_target] && - (seq = _loop0_195_rule(p)) // _loop0_195 + (seq = _loop0_196_rule(p)) // _loop0_196 ) { - D(fprintf(stderr, "%*c+ _gather_194[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(expression ['as' star_target]) _loop0_195")); + D(fprintf(stderr, "%*c+ _gather_195[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(expression ['as' star_target]) _loop0_196")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_194[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(expression ['as' star_target]) _loop0_195")); + D(fprintf(stderr, "%*c%s _gather_195[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(expression ['as' star_target]) _loop0_196")); } _res = NULL; done: @@ -35924,9 +35996,9 @@ _gather_194_rule(Parser *p) return _res; } -// _loop0_197: ',' (expressions ['as' star_target]) +// _loop0_198: ',' (expressions ['as' star_target]) static asdl_seq * -_loop0_197_rule(Parser *p) +_loop0_198_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -35952,13 +36024,13 @@ _loop0_197_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_197[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (expressions ['as' star_target])")); + D(fprintf(stderr, "%*c> _loop0_198[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (expressions ['as' star_target])")); Token * _literal; void *elem; while ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (elem = _tmp_241_rule(p)) // expressions ['as' star_target] + (elem = _tmp_243_rule(p)) // expressions ['as' star_target] ) { _res = elem; @@ -35984,7 +36056,7 @@ _loop0_197_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_197[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_198[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (expressions ['as' star_target])")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -36001,9 +36073,9 @@ _loop0_197_rule(Parser *p) return _seq; } -// _gather_196: (expressions ['as' star_target]) _loop0_197 +// _gather_197: (expressions ['as' star_target]) _loop0_198 static asdl_seq * -_gather_196_rule(Parser *p) +_gather_197_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -36015,27 +36087,27 @@ _gather_196_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // (expressions ['as' star_target]) _loop0_197 + { // (expressions ['as' star_target]) _loop0_198 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_196[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(expressions ['as' star_target]) _loop0_197")); + D(fprintf(stderr, "%*c> _gather_197[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(expressions ['as' star_target]) _loop0_198")); void *elem; asdl_seq * seq; if ( - (elem = _tmp_241_rule(p)) // expressions ['as' star_target] + (elem = _tmp_243_rule(p)) // expressions ['as' star_target] && - (seq = _loop0_197_rule(p)) // _loop0_197 + (seq = _loop0_198_rule(p)) // _loop0_198 ) { - D(fprintf(stderr, "%*c+ _gather_196[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(expressions ['as' star_target]) _loop0_197")); + D(fprintf(stderr, "%*c+ _gather_197[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(expressions ['as' star_target]) _loop0_198")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_196[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(expressions ['as' star_target]) _loop0_197")); + D(fprintf(stderr, "%*c%s _gather_197[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(expressions ['as' star_target]) _loop0_198")); } _res = NULL; done: @@ -36043,9 +36115,9 @@ _gather_196_rule(Parser *p) return _res; } -// _loop0_199: ',' (expression ['as' star_target]) +// _loop0_200: ',' (expression ['as' star_target]) static asdl_seq * -_loop0_199_rule(Parser *p) +_loop0_200_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -36071,13 +36143,13 @@ _loop0_199_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_199[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (expression ['as' star_target])")); + D(fprintf(stderr, "%*c> _loop0_200[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (expression ['as' star_target])")); Token * _literal; void *elem; while ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (elem = _tmp_242_rule(p)) // expression ['as' star_target] + (elem = _tmp_244_rule(p)) // expression ['as' star_target] ) { _res = elem; @@ -36103,7 +36175,7 @@ _loop0_199_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_199[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_200[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (expression ['as' star_target])")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -36120,9 +36192,9 @@ _loop0_199_rule(Parser *p) return _seq; } -// _gather_198: (expression ['as' star_target]) _loop0_199 +// _gather_199: (expression ['as' star_target]) _loop0_200 static asdl_seq * -_gather_198_rule(Parser *p) +_gather_199_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -36134,27 +36206,27 @@ _gather_198_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // (expression ['as' star_target]) _loop0_199 + { // (expression ['as' star_target]) _loop0_200 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_198[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(expression ['as' star_target]) _loop0_199")); + D(fprintf(stderr, "%*c> _gather_199[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(expression ['as' star_target]) _loop0_200")); void *elem; asdl_seq * seq; if ( - (elem = _tmp_242_rule(p)) // expression ['as' star_target] + (elem = _tmp_244_rule(p)) // expression ['as' star_target] && - (seq = _loop0_199_rule(p)) // _loop0_199 + (seq = _loop0_200_rule(p)) // _loop0_200 ) { - D(fprintf(stderr, "%*c+ _gather_198[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(expression ['as' star_target]) _loop0_199")); + D(fprintf(stderr, "%*c+ _gather_199[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(expression ['as' star_target]) _loop0_200")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_198[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(expression ['as' star_target]) _loop0_199")); + D(fprintf(stderr, "%*c%s _gather_199[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(expression ['as' star_target]) _loop0_200")); } _res = NULL; done: @@ -36162,9 +36234,9 @@ _gather_198_rule(Parser *p) return _res; } -// _loop0_201: ',' (expressions ['as' star_target]) +// _loop0_202: ',' (expressions ['as' star_target]) static asdl_seq * -_loop0_201_rule(Parser *p) +_loop0_202_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -36190,13 +36262,13 @@ _loop0_201_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_201[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (expressions ['as' star_target])")); + D(fprintf(stderr, "%*c> _loop0_202[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (expressions ['as' star_target])")); Token * _literal; void *elem; while ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (elem = _tmp_243_rule(p)) // expressions ['as' star_target] + (elem = _tmp_245_rule(p)) // expressions ['as' star_target] ) { _res = elem; @@ -36222,7 +36294,7 @@ _loop0_201_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_201[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_202[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (expressions ['as' star_target])")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -36239,9 +36311,9 @@ _loop0_201_rule(Parser *p) return _seq; } -// _gather_200: (expressions ['as' star_target]) _loop0_201 +// _gather_201: (expressions ['as' star_target]) _loop0_202 static asdl_seq * -_gather_200_rule(Parser *p) +_gather_201_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -36253,27 +36325,27 @@ _gather_200_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // (expressions ['as' star_target]) _loop0_201 + { // (expressions ['as' star_target]) _loop0_202 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_200[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(expressions ['as' star_target]) _loop0_201")); + D(fprintf(stderr, "%*c> _gather_201[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(expressions ['as' star_target]) _loop0_202")); void *elem; asdl_seq * seq; if ( - (elem = _tmp_243_rule(p)) // expressions ['as' star_target] + (elem = _tmp_245_rule(p)) // expressions ['as' star_target] && - (seq = _loop0_201_rule(p)) // _loop0_201 + (seq = _loop0_202_rule(p)) // _loop0_202 ) { - D(fprintf(stderr, "%*c+ _gather_200[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(expressions ['as' star_target]) _loop0_201")); + D(fprintf(stderr, "%*c+ _gather_201[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(expressions ['as' star_target]) _loop0_202")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_200[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(expressions ['as' star_target]) _loop0_201")); + D(fprintf(stderr, "%*c%s _gather_201[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(expressions ['as' star_target]) _loop0_202")); } _res = NULL; done: @@ -36281,9 +36353,9 @@ _gather_200_rule(Parser *p) return _res; } -// _tmp_202: 'except' | 'finally' +// _tmp_203: 'except' | 'finally' static void * -_tmp_202_rule(Parser *p) +_tmp_203_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -36300,18 +36372,18 @@ _tmp_202_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_202[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'except'")); + D(fprintf(stderr, "%*c> _tmp_203[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'except'")); Token * _keyword; if ( (_keyword = _PyPegen_expect_token(p, 634)) // token='except' ) { - D(fprintf(stderr, "%*c+ _tmp_202[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'except'")); + D(fprintf(stderr, "%*c+ _tmp_203[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'except'")); _res = _keyword; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_202[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_203[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'except'")); } { // 'finally' @@ -36319,18 +36391,18 @@ _tmp_202_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_202[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'finally'")); + D(fprintf(stderr, "%*c> _tmp_203[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'finally'")); Token * _keyword; if ( (_keyword = _PyPegen_expect_token(p, 630)) // token='finally' ) { - D(fprintf(stderr, "%*c+ _tmp_202[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'finally'")); + D(fprintf(stderr, "%*c+ _tmp_203[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'finally'")); _res = _keyword; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_202[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_203[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'finally'")); } _res = NULL; @@ -36339,9 +36411,9 @@ _tmp_202_rule(Parser *p) return _res; } -// _loop0_203: block +// _loop0_204: block static asdl_seq * -_loop0_203_rule(Parser *p) +_loop0_204_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -36367,7 +36439,7 @@ _loop0_203_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_203[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "block")); + D(fprintf(stderr, "%*c> _loop0_204[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "block")); asdl_stmt_seq* block_var; while ( (block_var = block_rule(p)) // block @@ -36390,7 +36462,7 @@ _loop0_203_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_203[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_204[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "block")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -36407,9 +36479,9 @@ _loop0_203_rule(Parser *p) return _seq; } -// _loop1_204: except_block +// _loop1_205: except_block static asdl_seq * -_loop1_204_rule(Parser *p) +_loop1_205_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -36435,7 +36507,7 @@ _loop1_204_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop1_204[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "except_block")); + D(fprintf(stderr, "%*c> _loop1_205[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "except_block")); excepthandler_ty except_block_var; while ( (except_block_var = except_block_rule(p)) // except_block @@ -36458,7 +36530,7 @@ _loop1_204_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop1_204[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop1_205[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "except_block")); } if (_n == 0 || p->error_indicator) { @@ -36480,9 +36552,9 @@ _loop1_204_rule(Parser *p) return _seq; } -// _tmp_205: 'as' NAME +// _tmp_206: 'as' NAME static void * -_tmp_205_rule(Parser *p) +_tmp_206_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -36499,7 +36571,7 @@ _tmp_205_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_205[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' NAME")); + D(fprintf(stderr, "%*c> _tmp_206[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' NAME")); Token * _keyword; expr_ty name_var; if ( @@ -36508,12 +36580,12 @@ _tmp_205_rule(Parser *p) (name_var = _PyPegen_name_token(p)) // NAME ) { - D(fprintf(stderr, "%*c+ _tmp_205[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' NAME")); + D(fprintf(stderr, "%*c+ _tmp_206[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' NAME")); _res = _PyPegen_dummy_name(p, _keyword, name_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_205[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_206[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'as' NAME")); } _res = NULL; @@ -36522,9 +36594,9 @@ _tmp_205_rule(Parser *p) return _res; } -// _loop0_206: block +// _loop0_207: block static asdl_seq * -_loop0_206_rule(Parser *p) +_loop0_207_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -36550,7 +36622,7 @@ _loop0_206_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_206[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "block")); + D(fprintf(stderr, "%*c> _loop0_207[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "block")); asdl_stmt_seq* block_var; while ( (block_var = block_rule(p)) // block @@ -36573,7 +36645,7 @@ _loop0_206_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_206[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_207[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "block")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -36590,9 +36662,9 @@ _loop0_206_rule(Parser *p) return _seq; } -// _loop1_207: except_star_block +// _loop1_208: except_star_block static asdl_seq * -_loop1_207_rule(Parser *p) +_loop1_208_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -36618,7 +36690,7 @@ _loop1_207_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop1_207[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "except_star_block")); + D(fprintf(stderr, "%*c> _loop1_208[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "except_star_block")); excepthandler_ty except_star_block_var; while ( (except_star_block_var = except_star_block_rule(p)) // except_star_block @@ -36641,7 +36713,7 @@ _loop1_207_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop1_207[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop1_208[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "except_star_block")); } if (_n == 0 || p->error_indicator) { @@ -36663,9 +36735,9 @@ _loop1_207_rule(Parser *p) return _seq; } -// _tmp_208: expression ['as' NAME] +// _tmp_209: expression ['as' NAME] static void * -_tmp_208_rule(Parser *p) +_tmp_209_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -36682,22 +36754,22 @@ _tmp_208_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_208[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression ['as' NAME]")); + D(fprintf(stderr, "%*c> _tmp_209[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression ['as' NAME]")); void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings expr_ty expression_var; if ( (expression_var = expression_rule(p)) // expression && - (_opt_var = _tmp_244_rule(p), !p->error_indicator) // ['as' NAME] + (_opt_var = _tmp_246_rule(p), !p->error_indicator) // ['as' NAME] ) { - D(fprintf(stderr, "%*c+ _tmp_208[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ['as' NAME]")); + D(fprintf(stderr, "%*c+ _tmp_209[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ['as' NAME]")); _res = _PyPegen_dummy_name(p, expression_var, _opt_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_208[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_209[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression ['as' NAME]")); } _res = NULL; @@ -36706,9 +36778,9 @@ _tmp_208_rule(Parser *p) return _res; } -// _tmp_209: 'as' NAME +// _tmp_210: 'as' NAME static void * -_tmp_209_rule(Parser *p) +_tmp_210_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -36725,7 +36797,7 @@ _tmp_209_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_209[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' NAME")); + D(fprintf(stderr, "%*c> _tmp_210[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' NAME")); Token * _keyword; expr_ty name_var; if ( @@ -36734,12 +36806,12 @@ _tmp_209_rule(Parser *p) (name_var = _PyPegen_name_token(p)) // NAME ) { - D(fprintf(stderr, "%*c+ _tmp_209[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' NAME")); + D(fprintf(stderr, "%*c+ _tmp_210[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' NAME")); _res = _PyPegen_dummy_name(p, _keyword, name_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_209[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_210[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'as' NAME")); } _res = NULL; @@ -36748,9 +36820,9 @@ _tmp_209_rule(Parser *p) return _res; } -// _tmp_210: 'as' NAME +// _tmp_211: 'as' NAME static void * -_tmp_210_rule(Parser *p) +_tmp_211_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -36767,7 +36839,7 @@ _tmp_210_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_210[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' NAME")); + D(fprintf(stderr, "%*c> _tmp_211[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' NAME")); Token * _keyword; expr_ty name_var; if ( @@ -36776,12 +36848,12 @@ _tmp_210_rule(Parser *p) (name_var = _PyPegen_name_token(p)) // NAME ) { - D(fprintf(stderr, "%*c+ _tmp_210[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' NAME")); + D(fprintf(stderr, "%*c+ _tmp_211[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' NAME")); _res = _PyPegen_dummy_name(p, _keyword, name_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_210[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_211[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'as' NAME")); } _res = NULL; @@ -36790,9 +36862,9 @@ _tmp_210_rule(Parser *p) return _res; } -// _tmp_211: NEWLINE | ':' +// _tmp_212: NEWLINE | ':' static void * -_tmp_211_rule(Parser *p) +_tmp_212_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -36809,18 +36881,18 @@ _tmp_211_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_211[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NEWLINE")); + D(fprintf(stderr, "%*c> _tmp_212[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NEWLINE")); Token * newline_var; if ( (newline_var = _PyPegen_expect_token(p, NEWLINE)) // token='NEWLINE' ) { - D(fprintf(stderr, "%*c+ _tmp_211[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NEWLINE")); + D(fprintf(stderr, "%*c+ _tmp_212[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NEWLINE")); _res = newline_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_211[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_212[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NEWLINE")); } { // ':' @@ -36828,18 +36900,18 @@ _tmp_211_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_211[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c> _tmp_212[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 11)) // token=':' ) { - D(fprintf(stderr, "%*c+ _tmp_211[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c+ _tmp_212[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_211[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_212[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'")); } _res = NULL; @@ -36848,9 +36920,9 @@ _tmp_211_rule(Parser *p) return _res; } -// _tmp_212: 'as' NAME +// _tmp_213: 'as' NAME static void * -_tmp_212_rule(Parser *p) +_tmp_213_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -36867,7 +36939,7 @@ _tmp_212_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_212[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' NAME")); + D(fprintf(stderr, "%*c> _tmp_213[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' NAME")); Token * _keyword; expr_ty name_var; if ( @@ -36876,12 +36948,12 @@ _tmp_212_rule(Parser *p) (name_var = _PyPegen_name_token(p)) // NAME ) { - D(fprintf(stderr, "%*c+ _tmp_212[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' NAME")); + D(fprintf(stderr, "%*c+ _tmp_213[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' NAME")); _res = _PyPegen_dummy_name(p, _keyword, name_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_212[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_213[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'as' NAME")); } _res = NULL; @@ -36890,9 +36962,9 @@ _tmp_212_rule(Parser *p) return _res; } -// _tmp_213: 'as' NAME +// _tmp_214: 'as' NAME static void * -_tmp_213_rule(Parser *p) +_tmp_214_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -36909,7 +36981,7 @@ _tmp_213_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_213[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' NAME")); + D(fprintf(stderr, "%*c> _tmp_214[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' NAME")); Token * _keyword; expr_ty name_var; if ( @@ -36918,12 +36990,12 @@ _tmp_213_rule(Parser *p) (name_var = _PyPegen_name_token(p)) // NAME ) { - D(fprintf(stderr, "%*c+ _tmp_213[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' NAME")); + D(fprintf(stderr, "%*c+ _tmp_214[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' NAME")); _res = _PyPegen_dummy_name(p, _keyword, name_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_213[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_214[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'as' NAME")); } _res = NULL; @@ -36932,9 +37004,9 @@ _tmp_213_rule(Parser *p) return _res; } -// _tmp_214: positional_patterns ',' +// _tmp_215: positional_patterns ',' static void * -_tmp_214_rule(Parser *p) +_tmp_215_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -36951,7 +37023,7 @@ _tmp_214_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_214[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "positional_patterns ','")); + D(fprintf(stderr, "%*c> _tmp_215[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "positional_patterns ','")); Token * _literal; asdl_pattern_seq* positional_patterns_var; if ( @@ -36960,12 +37032,12 @@ _tmp_214_rule(Parser *p) (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_214[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "positional_patterns ','")); + D(fprintf(stderr, "%*c+ _tmp_215[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "positional_patterns ','")); _res = _PyPegen_dummy_name(p, positional_patterns_var, _literal); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_214[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_215[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "positional_patterns ','")); } _res = NULL; @@ -36974,9 +37046,9 @@ _tmp_214_rule(Parser *p) return _res; } -// _tmp_215: '->' expression +// _tmp_216: '->' expression static void * -_tmp_215_rule(Parser *p) +_tmp_216_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -36993,7 +37065,7 @@ _tmp_215_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_215[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'->' expression")); + D(fprintf(stderr, "%*c> _tmp_216[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'->' expression")); Token * _literal; expr_ty expression_var; if ( @@ -37002,12 +37074,12 @@ _tmp_215_rule(Parser *p) (expression_var = expression_rule(p)) // expression ) { - D(fprintf(stderr, "%*c+ _tmp_215[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'->' expression")); + D(fprintf(stderr, "%*c+ _tmp_216[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'->' expression")); _res = _PyPegen_dummy_name(p, _literal, expression_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_215[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_216[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'->' expression")); } _res = NULL; @@ -37016,9 +37088,9 @@ _tmp_215_rule(Parser *p) return _res; } -// _tmp_216: '(' arguments? ')' +// _tmp_217: '(' arguments? ')' static void * -_tmp_216_rule(Parser *p) +_tmp_217_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -37035,7 +37107,7 @@ _tmp_216_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_216[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'(' arguments? ')'")); + D(fprintf(stderr, "%*c> _tmp_217[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'(' arguments? ')'")); Token * _literal; Token * _literal_1; void *_opt_var; @@ -37048,12 +37120,12 @@ _tmp_216_rule(Parser *p) (_literal_1 = _PyPegen_expect_token(p, 8)) // token=')' ) { - D(fprintf(stderr, "%*c+ _tmp_216[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'(' arguments? ')'")); + D(fprintf(stderr, "%*c+ _tmp_217[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'(' arguments? ')'")); _res = _PyPegen_dummy_name(p, _literal, _opt_var, _literal_1); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_216[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_217[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'(' arguments? ')'")); } _res = NULL; @@ -37062,9 +37134,9 @@ _tmp_216_rule(Parser *p) return _res; } -// _tmp_217: '(' arguments? ')' +// _tmp_218: '(' arguments? ')' static void * -_tmp_217_rule(Parser *p) +_tmp_218_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -37081,7 +37153,7 @@ _tmp_217_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_217[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'(' arguments? ')'")); + D(fprintf(stderr, "%*c> _tmp_218[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'(' arguments? ')'")); Token * _literal; Token * _literal_1; void *_opt_var; @@ -37094,12 +37166,12 @@ _tmp_217_rule(Parser *p) (_literal_1 = _PyPegen_expect_token(p, 8)) // token=')' ) { - D(fprintf(stderr, "%*c+ _tmp_217[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'(' arguments? ')'")); + D(fprintf(stderr, "%*c+ _tmp_218[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'(' arguments? ')'")); _res = _PyPegen_dummy_name(p, _literal, _opt_var, _literal_1); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_217[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_218[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'(' arguments? ')'")); } _res = NULL; @@ -37108,9 +37180,9 @@ _tmp_217_rule(Parser *p) return _res; } -// _loop0_219: ',' double_starred_kvpair +// _loop0_220: ',' double_starred_kvpair static asdl_seq * -_loop0_219_rule(Parser *p) +_loop0_220_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -37136,7 +37208,7 @@ _loop0_219_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_219[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' double_starred_kvpair")); + D(fprintf(stderr, "%*c> _loop0_220[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' double_starred_kvpair")); Token * _literal; KeyValuePair* elem; while ( @@ -37168,7 +37240,7 @@ _loop0_219_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_219[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_220[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' double_starred_kvpair")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -37185,9 +37257,9 @@ _loop0_219_rule(Parser *p) return _seq; } -// _gather_218: double_starred_kvpair _loop0_219 +// _gather_219: double_starred_kvpair _loop0_220 static asdl_seq * -_gather_218_rule(Parser *p) +_gather_219_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -37199,27 +37271,27 @@ _gather_218_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // double_starred_kvpair _loop0_219 + { // double_starred_kvpair _loop0_220 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_218[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "double_starred_kvpair _loop0_219")); + D(fprintf(stderr, "%*c> _gather_219[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "double_starred_kvpair _loop0_220")); KeyValuePair* elem; asdl_seq * seq; if ( (elem = double_starred_kvpair_rule(p)) // double_starred_kvpair && - (seq = _loop0_219_rule(p)) // _loop0_219 + (seq = _loop0_220_rule(p)) // _loop0_220 ) { - D(fprintf(stderr, "%*c+ _gather_218[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "double_starred_kvpair _loop0_219")); + D(fprintf(stderr, "%*c+ _gather_219[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "double_starred_kvpair _loop0_220")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_218[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "double_starred_kvpair _loop0_219")); + D(fprintf(stderr, "%*c%s _gather_219[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "double_starred_kvpair _loop0_220")); } _res = NULL; done: @@ -37227,9 +37299,9 @@ _gather_218_rule(Parser *p) return _res; } -// _tmp_220: '}' | ',' +// _tmp_221: '}' | ',' static void * -_tmp_220_rule(Parser *p) +_tmp_221_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -37246,18 +37318,18 @@ _tmp_220_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_220[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'")); + D(fprintf(stderr, "%*c> _tmp_221[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 26)) // token='}' ) { - D(fprintf(stderr, "%*c+ _tmp_220[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'")); + D(fprintf(stderr, "%*c+ _tmp_221[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_220[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_221[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'}'")); } { // ',' @@ -37265,18 +37337,18 @@ _tmp_220_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_220[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c> _tmp_221[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_220[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c+ _tmp_221[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_220[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_221[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); } _res = NULL; @@ -37285,9 +37357,9 @@ _tmp_220_rule(Parser *p) return _res; } -// _tmp_221: '}' | ',' +// _tmp_222: '}' | ',' static void * -_tmp_221_rule(Parser *p) +_tmp_222_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -37304,18 +37376,18 @@ _tmp_221_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_221[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'")); + D(fprintf(stderr, "%*c> _tmp_222[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 26)) // token='}' ) { - D(fprintf(stderr, "%*c+ _tmp_221[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'")); + D(fprintf(stderr, "%*c+ _tmp_222[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_221[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_222[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'}'")); } { // ',' @@ -37323,18 +37395,18 @@ _tmp_221_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_221[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c> _tmp_222[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_221[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c+ _tmp_222[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_221[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_222[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); } _res = NULL; @@ -37343,9 +37415,9 @@ _tmp_221_rule(Parser *p) return _res; } -// _tmp_222: star_targets '=' +// _tmp_223: star_targets '=' static void * -_tmp_222_rule(Parser *p) +_tmp_223_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -37362,7 +37434,7 @@ _tmp_222_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_222[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_targets '='")); + D(fprintf(stderr, "%*c> _tmp_223[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_targets '='")); Token * _literal; expr_ty z; if ( @@ -37371,7 +37443,7 @@ _tmp_222_rule(Parser *p) (_literal = _PyPegen_expect_token(p, 22)) // token='=' ) { - D(fprintf(stderr, "%*c+ _tmp_222[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_targets '='")); + D(fprintf(stderr, "%*c+ _tmp_223[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_targets '='")); _res = z; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -37381,7 +37453,7 @@ _tmp_222_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_222[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_223[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_targets '='")); } _res = NULL; @@ -37390,9 +37462,9 @@ _tmp_222_rule(Parser *p) return _res; } -// _tmp_223: '.' | '...' +// _tmp_224: '.' | '...' static void * -_tmp_223_rule(Parser *p) +_tmp_224_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -37409,18 +37481,18 @@ _tmp_223_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_223[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'.'")); + D(fprintf(stderr, "%*c> _tmp_224[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'.'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 23)) // token='.' ) { - D(fprintf(stderr, "%*c+ _tmp_223[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'.'")); + D(fprintf(stderr, "%*c+ _tmp_224[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'.'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_223[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_224[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'.'")); } { // '...' @@ -37428,18 +37500,18 @@ _tmp_223_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_223[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'...'")); + D(fprintf(stderr, "%*c> _tmp_224[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'...'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 52)) // token='...' ) { - D(fprintf(stderr, "%*c+ _tmp_223[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'...'")); + D(fprintf(stderr, "%*c+ _tmp_224[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'...'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_223[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_224[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'...'")); } _res = NULL; @@ -37448,9 +37520,9 @@ _tmp_223_rule(Parser *p) return _res; } -// _tmp_224: '.' | '...' +// _tmp_225: '.' | '...' static void * -_tmp_224_rule(Parser *p) +_tmp_225_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -37467,18 +37539,18 @@ _tmp_224_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_224[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'.'")); + D(fprintf(stderr, "%*c> _tmp_225[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'.'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 23)) // token='.' ) { - D(fprintf(stderr, "%*c+ _tmp_224[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'.'")); + D(fprintf(stderr, "%*c+ _tmp_225[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'.'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_224[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_225[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'.'")); } { // '...' @@ -37486,18 +37558,18 @@ _tmp_224_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_224[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'...'")); + D(fprintf(stderr, "%*c> _tmp_225[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'...'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 52)) // token='...' ) { - D(fprintf(stderr, "%*c+ _tmp_224[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'...'")); + D(fprintf(stderr, "%*c+ _tmp_225[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'...'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_224[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_225[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'...'")); } _res = NULL; @@ -37506,9 +37578,9 @@ _tmp_224_rule(Parser *p) return _res; } -// _tmp_225: '@' named_expression NEWLINE +// _tmp_226: '@' named_expression NEWLINE static void * -_tmp_225_rule(Parser *p) +_tmp_226_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -37525,7 +37597,7 @@ _tmp_225_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_225[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'@' named_expression NEWLINE")); + D(fprintf(stderr, "%*c> _tmp_226[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'@' named_expression NEWLINE")); Token * _literal; expr_ty f; Token * newline_var; @@ -37537,7 +37609,7 @@ _tmp_225_rule(Parser *p) (newline_var = _PyPegen_expect_token(p, NEWLINE)) // token='NEWLINE' ) { - D(fprintf(stderr, "%*c+ _tmp_225[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'@' named_expression NEWLINE")); + D(fprintf(stderr, "%*c+ _tmp_226[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'@' named_expression NEWLINE")); _res = f; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -37547,7 +37619,7 @@ _tmp_225_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_225[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_226[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'@' named_expression NEWLINE")); } _res = NULL; @@ -37556,9 +37628,9 @@ _tmp_225_rule(Parser *p) return _res; } -// _tmp_226: ',' expression +// _tmp_227: ',' expression static void * -_tmp_226_rule(Parser *p) +_tmp_227_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -37575,7 +37647,7 @@ _tmp_226_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_226[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' expression")); + D(fprintf(stderr, "%*c> _tmp_227[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' expression")); Token * _literal; expr_ty c; if ( @@ -37584,7 +37656,7 @@ _tmp_226_rule(Parser *p) (c = expression_rule(p)) // expression ) { - D(fprintf(stderr, "%*c+ _tmp_226[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' expression")); + D(fprintf(stderr, "%*c+ _tmp_227[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' expression")); _res = c; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -37594,7 +37666,7 @@ _tmp_226_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_226[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_227[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' expression")); } _res = NULL; @@ -37603,9 +37675,9 @@ _tmp_226_rule(Parser *p) return _res; } -// _tmp_227: ',' star_expression +// _tmp_228: ',' star_expression static void * -_tmp_227_rule(Parser *p) +_tmp_228_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -37622,7 +37694,7 @@ _tmp_227_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_227[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_expression")); + D(fprintf(stderr, "%*c> _tmp_228[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_expression")); Token * _literal; expr_ty c; if ( @@ -37631,7 +37703,7 @@ _tmp_227_rule(Parser *p) (c = star_expression_rule(p)) // star_expression ) { - D(fprintf(stderr, "%*c+ _tmp_227[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_expression")); + D(fprintf(stderr, "%*c+ _tmp_228[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_expression")); _res = c; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -37641,7 +37713,7 @@ _tmp_227_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_227[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_228[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' star_expression")); } _res = NULL; @@ -37650,9 +37722,9 @@ _tmp_227_rule(Parser *p) return _res; } -// _tmp_228: 'or' conjunction +// _tmp_229: 'or' conjunction static void * -_tmp_228_rule(Parser *p) +_tmp_229_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -37669,7 +37741,7 @@ _tmp_228_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_228[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'or' conjunction")); + D(fprintf(stderr, "%*c> _tmp_229[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'or' conjunction")); Token * _keyword; expr_ty c; if ( @@ -37678,7 +37750,7 @@ _tmp_228_rule(Parser *p) (c = conjunction_rule(p)) // conjunction ) { - D(fprintf(stderr, "%*c+ _tmp_228[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'or' conjunction")); + D(fprintf(stderr, "%*c+ _tmp_229[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'or' conjunction")); _res = c; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -37688,7 +37760,7 @@ _tmp_228_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_228[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_229[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'or' conjunction")); } _res = NULL; @@ -37697,9 +37769,9 @@ _tmp_228_rule(Parser *p) return _res; } -// _tmp_229: 'and' inversion +// _tmp_230: 'and' inversion static void * -_tmp_229_rule(Parser *p) +_tmp_230_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -37716,7 +37788,7 @@ _tmp_229_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_229[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'and' inversion")); + D(fprintf(stderr, "%*c> _tmp_230[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'and' inversion")); Token * _keyword; expr_ty c; if ( @@ -37725,7 +37797,7 @@ _tmp_229_rule(Parser *p) (c = inversion_rule(p)) // inversion ) { - D(fprintf(stderr, "%*c+ _tmp_229[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'and' inversion")); + D(fprintf(stderr, "%*c+ _tmp_230[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'and' inversion")); _res = c; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -37735,7 +37807,7 @@ _tmp_229_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_229[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_230[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'and' inversion")); } _res = NULL; @@ -37744,9 +37816,9 @@ _tmp_229_rule(Parser *p) return _res; } -// _tmp_230: slice | starred_expression +// _tmp_231: slice | starred_expression static void * -_tmp_230_rule(Parser *p) +_tmp_231_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -37763,18 +37835,18 @@ _tmp_230_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_230[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slice")); + D(fprintf(stderr, "%*c> _tmp_231[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slice")); expr_ty slice_var; if ( (slice_var = slice_rule(p)) // slice ) { - D(fprintf(stderr, "%*c+ _tmp_230[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slice")); + D(fprintf(stderr, "%*c+ _tmp_231[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slice")); _res = slice_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_230[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_231[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "slice")); } { // starred_expression @@ -37782,18 +37854,18 @@ _tmp_230_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_230[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression")); + D(fprintf(stderr, "%*c> _tmp_231[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression")); expr_ty starred_expression_var; if ( (starred_expression_var = starred_expression_rule(p)) // starred_expression ) { - D(fprintf(stderr, "%*c+ _tmp_230[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression")); + D(fprintf(stderr, "%*c+ _tmp_231[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression")); _res = starred_expression_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_230[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_231[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "starred_expression")); } _res = NULL; @@ -37802,9 +37874,9 @@ _tmp_230_rule(Parser *p) return _res; } -// _tmp_231: 'if' disjunction +// _tmp_232: 'if' disjunction static void * -_tmp_231_rule(Parser *p) +_tmp_232_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -37821,7 +37893,7 @@ _tmp_231_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_231[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); + D(fprintf(stderr, "%*c> _tmp_232[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); Token * _keyword; expr_ty z; if ( @@ -37830,7 +37902,7 @@ _tmp_231_rule(Parser *p) (z = disjunction_rule(p)) // disjunction ) { - D(fprintf(stderr, "%*c+ _tmp_231[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); + D(fprintf(stderr, "%*c+ _tmp_232[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); _res = z; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -37840,7 +37912,7 @@ _tmp_231_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_231[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_232[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'if' disjunction")); } _res = NULL; @@ -37849,9 +37921,9 @@ _tmp_231_rule(Parser *p) return _res; } -// _tmp_232: 'if' disjunction +// _tmp_233: 'if' disjunction static void * -_tmp_232_rule(Parser *p) +_tmp_233_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -37868,7 +37940,7 @@ _tmp_232_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_232[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); + D(fprintf(stderr, "%*c> _tmp_233[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); Token * _keyword; expr_ty z; if ( @@ -37877,7 +37949,7 @@ _tmp_232_rule(Parser *p) (z = disjunction_rule(p)) // disjunction ) { - D(fprintf(stderr, "%*c+ _tmp_232[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); + D(fprintf(stderr, "%*c+ _tmp_233[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); _res = z; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -37887,7 +37959,7 @@ _tmp_232_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_232[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_233[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'if' disjunction")); } _res = NULL; @@ -37896,9 +37968,9 @@ _tmp_232_rule(Parser *p) return _res; } -// _tmp_233: starred_expression | (assignment_expression | expression !':=') !'=' +// _tmp_234: starred_expression | (assignment_expression | expression !':=') !'=' static void * -_tmp_233_rule(Parser *p) +_tmp_234_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -37915,18 +37987,18 @@ _tmp_233_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_233[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression")); + D(fprintf(stderr, "%*c> _tmp_234[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression")); expr_ty starred_expression_var; if ( (starred_expression_var = starred_expression_rule(p)) // starred_expression ) { - D(fprintf(stderr, "%*c+ _tmp_233[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression")); + D(fprintf(stderr, "%*c+ _tmp_234[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression")); _res = starred_expression_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_233[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_234[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "starred_expression")); } { // (assignment_expression | expression !':=') !'=' @@ -37934,68 +38006,21 @@ _tmp_233_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_233[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(assignment_expression | expression !':=') !'='")); - void *_tmp_245_var; + D(fprintf(stderr, "%*c> _tmp_234[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(assignment_expression | expression !':=') !'='")); + void *_tmp_247_var; if ( - (_tmp_245_var = _tmp_245_rule(p)) // assignment_expression | expression !':=' + (_tmp_247_var = _tmp_247_rule(p)) // assignment_expression | expression !':=' && _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 22) // token='=' ) { - D(fprintf(stderr, "%*c+ _tmp_233[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(assignment_expression | expression !':=') !'='")); - _res = _tmp_245_var; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_233[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(assignment_expression | expression !':=') !'='")); - } - _res = NULL; - done: - p->level--; - return _res; -} - -// _tmp_234: ',' star_target -static void * -_tmp_234_rule(Parser *p) -{ - if (p->level++ == MAXSTACK) { - p->error_indicator = 1; - PyErr_NoMemory(); - } - if (p->error_indicator) { - p->level--; - return NULL; - } - void * _res = NULL; - int _mark = p->mark; - { // ',' star_target - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_234[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_target")); - Token * _literal; - expr_ty c; - if ( - (_literal = _PyPegen_expect_token(p, 12)) // token=',' - && - (c = star_target_rule(p)) // star_target - ) - { - D(fprintf(stderr, "%*c+ _tmp_234[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_target")); - _res = c; - if (_res == NULL && PyErr_Occurred()) { - p->error_indicator = 1; - p->level--; - return NULL; - } + D(fprintf(stderr, "%*c+ _tmp_234[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(assignment_expression | expression !':=') !'='")); + _res = _tmp_247_var; goto done; } p->mark = _mark; D(fprintf(stderr, "%*c%s _tmp_234[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' star_target")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(assignment_expression | expression !':=') !'='")); } _res = NULL; done: @@ -38050,9 +38075,102 @@ _tmp_235_rule(Parser *p) return _res; } -// _tmp_236: star_targets '=' +// _tmp_236: ',' star_target static void * _tmp_236_rule(Parser *p) +{ + if (p->level++ == MAXSTACK) { + p->error_indicator = 1; + PyErr_NoMemory(); + } + if (p->error_indicator) { + p->level--; + return NULL; + } + void * _res = NULL; + int _mark = p->mark; + { // ',' star_target + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _tmp_236[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_target")); + Token * _literal; + expr_ty c; + if ( + (_literal = _PyPegen_expect_token(p, 12)) // token=',' + && + (c = star_target_rule(p)) // star_target + ) + { + D(fprintf(stderr, "%*c+ _tmp_236[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_target")); + _res = c; + if (_res == NULL && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _tmp_236[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' star_target")); + } + _res = NULL; + done: + p->level--; + return _res; +} + +// _tmp_237: +// | ','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs +static void * +_tmp_237_rule(Parser *p) +{ + if (p->level++ == MAXSTACK) { + p->error_indicator = 1; + PyErr_NoMemory(); + } + if (p->error_indicator) { + p->level--; + return NULL; + } + void * _res = NULL; + int _mark = p->mark; + { // ','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _tmp_237[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs")); + asdl_seq * _gather_248_var; + Token * _literal; + asdl_seq* kwargs_var; + if ( + (_gather_248_var = _gather_248_rule(p)) // ','.(starred_expression | (assignment_expression | expression !':=') !'=')+ + && + (_literal = _PyPegen_expect_token(p, 12)) // token=',' + && + (kwargs_var = kwargs_rule(p)) // kwargs + ) + { + D(fprintf(stderr, "%*c+ _tmp_237[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs")); + _res = _PyPegen_dummy_name(p, _gather_248_var, _literal, kwargs_var); + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _tmp_237[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs")); + } + _res = NULL; + done: + p->level--; + return _res; +} + +// _tmp_238: star_targets '=' +static void * +_tmp_238_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -38069,7 +38187,7 @@ _tmp_236_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_236[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_targets '='")); + D(fprintf(stderr, "%*c> _tmp_238[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_targets '='")); Token * _literal; expr_ty star_targets_var; if ( @@ -38078,12 +38196,12 @@ _tmp_236_rule(Parser *p) (_literal = _PyPegen_expect_token(p, 22)) // token='=' ) { - D(fprintf(stderr, "%*c+ _tmp_236[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_targets '='")); + D(fprintf(stderr, "%*c+ _tmp_238[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_targets '='")); _res = _PyPegen_dummy_name(p, star_targets_var, _literal); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_236[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_238[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_targets '='")); } _res = NULL; @@ -38092,9 +38210,9 @@ _tmp_236_rule(Parser *p) return _res; } -// _tmp_237: star_targets '=' +// _tmp_239: star_targets '=' static void * -_tmp_237_rule(Parser *p) +_tmp_239_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -38111,7 +38229,7 @@ _tmp_237_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_237[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_targets '='")); + D(fprintf(stderr, "%*c> _tmp_239[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_targets '='")); Token * _literal; expr_ty star_targets_var; if ( @@ -38120,12 +38238,12 @@ _tmp_237_rule(Parser *p) (_literal = _PyPegen_expect_token(p, 22)) // token='=' ) { - D(fprintf(stderr, "%*c+ _tmp_237[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_targets '='")); + D(fprintf(stderr, "%*c+ _tmp_239[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_targets '='")); _res = _PyPegen_dummy_name(p, star_targets_var, _literal); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_237[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_239[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_targets '='")); } _res = NULL; @@ -38134,9 +38252,9 @@ _tmp_237_rule(Parser *p) return _res; } -// _tmp_238: ')' | '**' +// _tmp_240: ')' | '**' static void * -_tmp_238_rule(Parser *p) +_tmp_240_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -38153,18 +38271,18 @@ _tmp_238_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_238[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c> _tmp_240[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 8)) // token=')' ) { - D(fprintf(stderr, "%*c+ _tmp_238[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c+ _tmp_240[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_238[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_240[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "')'")); } { // '**' @@ -38172,18 +38290,18 @@ _tmp_238_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_238[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'")); + D(fprintf(stderr, "%*c> _tmp_240[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 35)) // token='**' ) { - D(fprintf(stderr, "%*c+ _tmp_238[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'")); + D(fprintf(stderr, "%*c+ _tmp_240[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_238[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_240[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'**'")); } _res = NULL; @@ -38192,9 +38310,9 @@ _tmp_238_rule(Parser *p) return _res; } -// _tmp_239: ':' | '**' +// _tmp_241: ':' | '**' static void * -_tmp_239_rule(Parser *p) +_tmp_241_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -38211,18 +38329,18 @@ _tmp_239_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_239[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c> _tmp_241[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 11)) // token=':' ) { - D(fprintf(stderr, "%*c+ _tmp_239[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c+ _tmp_241[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_239[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_241[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'")); } { // '**' @@ -38230,18 +38348,18 @@ _tmp_239_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_239[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'")); + D(fprintf(stderr, "%*c> _tmp_241[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 35)) // token='**' ) { - D(fprintf(stderr, "%*c+ _tmp_239[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'")); + D(fprintf(stderr, "%*c+ _tmp_241[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_239[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_241[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'**'")); } _res = NULL; @@ -38250,9 +38368,9 @@ _tmp_239_rule(Parser *p) return _res; } -// _tmp_240: expression ['as' star_target] +// _tmp_242: expression ['as' star_target] static void * -_tmp_240_rule(Parser *p) +_tmp_242_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -38269,22 +38387,22 @@ _tmp_240_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_240[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression ['as' star_target]")); + D(fprintf(stderr, "%*c> _tmp_242[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression ['as' star_target]")); void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings expr_ty expression_var; if ( (expression_var = expression_rule(p)) // expression && - (_opt_var = _tmp_246_rule(p), !p->error_indicator) // ['as' star_target] + (_opt_var = _tmp_250_rule(p), !p->error_indicator) // ['as' star_target] ) { - D(fprintf(stderr, "%*c+ _tmp_240[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ['as' star_target]")); + D(fprintf(stderr, "%*c+ _tmp_242[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ['as' star_target]")); _res = _PyPegen_dummy_name(p, expression_var, _opt_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_240[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_242[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression ['as' star_target]")); } _res = NULL; @@ -38293,9 +38411,9 @@ _tmp_240_rule(Parser *p) return _res; } -// _tmp_241: expressions ['as' star_target] +// _tmp_243: expressions ['as' star_target] static void * -_tmp_241_rule(Parser *p) +_tmp_243_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -38312,22 +38430,22 @@ _tmp_241_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_241[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expressions ['as' star_target]")); + D(fprintf(stderr, "%*c> _tmp_243[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expressions ['as' star_target]")); void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings expr_ty expressions_var; if ( (expressions_var = expressions_rule(p)) // expressions && - (_opt_var = _tmp_247_rule(p), !p->error_indicator) // ['as' star_target] + (_opt_var = _tmp_251_rule(p), !p->error_indicator) // ['as' star_target] ) { - D(fprintf(stderr, "%*c+ _tmp_241[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expressions ['as' star_target]")); + D(fprintf(stderr, "%*c+ _tmp_243[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expressions ['as' star_target]")); _res = _PyPegen_dummy_name(p, expressions_var, _opt_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_241[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_243[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expressions ['as' star_target]")); } _res = NULL; @@ -38336,9 +38454,9 @@ _tmp_241_rule(Parser *p) return _res; } -// _tmp_242: expression ['as' star_target] +// _tmp_244: expression ['as' star_target] static void * -_tmp_242_rule(Parser *p) +_tmp_244_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -38355,22 +38473,22 @@ _tmp_242_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_242[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression ['as' star_target]")); + D(fprintf(stderr, "%*c> _tmp_244[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression ['as' star_target]")); void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings expr_ty expression_var; if ( (expression_var = expression_rule(p)) // expression && - (_opt_var = _tmp_248_rule(p), !p->error_indicator) // ['as' star_target] + (_opt_var = _tmp_252_rule(p), !p->error_indicator) // ['as' star_target] ) { - D(fprintf(stderr, "%*c+ _tmp_242[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ['as' star_target]")); + D(fprintf(stderr, "%*c+ _tmp_244[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ['as' star_target]")); _res = _PyPegen_dummy_name(p, expression_var, _opt_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_242[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_244[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression ['as' star_target]")); } _res = NULL; @@ -38379,9 +38497,9 @@ _tmp_242_rule(Parser *p) return _res; } -// _tmp_243: expressions ['as' star_target] +// _tmp_245: expressions ['as' star_target] static void * -_tmp_243_rule(Parser *p) +_tmp_245_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -38398,22 +38516,22 @@ _tmp_243_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_243[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expressions ['as' star_target]")); + D(fprintf(stderr, "%*c> _tmp_245[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expressions ['as' star_target]")); void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings expr_ty expressions_var; if ( (expressions_var = expressions_rule(p)) // expressions && - (_opt_var = _tmp_249_rule(p), !p->error_indicator) // ['as' star_target] + (_opt_var = _tmp_253_rule(p), !p->error_indicator) // ['as' star_target] ) { - D(fprintf(stderr, "%*c+ _tmp_243[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expressions ['as' star_target]")); + D(fprintf(stderr, "%*c+ _tmp_245[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expressions ['as' star_target]")); _res = _PyPegen_dummy_name(p, expressions_var, _opt_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_243[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_245[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expressions ['as' star_target]")); } _res = NULL; @@ -38422,9 +38540,9 @@ _tmp_243_rule(Parser *p) return _res; } -// _tmp_244: 'as' NAME +// _tmp_246: 'as' NAME static void * -_tmp_244_rule(Parser *p) +_tmp_246_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -38441,7 +38559,7 @@ _tmp_244_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_244[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' NAME")); + D(fprintf(stderr, "%*c> _tmp_246[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' NAME")); Token * _keyword; expr_ty name_var; if ( @@ -38450,12 +38568,12 @@ _tmp_244_rule(Parser *p) (name_var = _PyPegen_name_token(p)) // NAME ) { - D(fprintf(stderr, "%*c+ _tmp_244[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' NAME")); + D(fprintf(stderr, "%*c+ _tmp_246[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' NAME")); _res = _PyPegen_dummy_name(p, _keyword, name_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_244[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_246[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'as' NAME")); } _res = NULL; @@ -38464,9 +38582,9 @@ _tmp_244_rule(Parser *p) return _res; } -// _tmp_245: assignment_expression | expression !':=' +// _tmp_247: assignment_expression | expression !':=' static void * -_tmp_245_rule(Parser *p) +_tmp_247_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -38483,18 +38601,18 @@ _tmp_245_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_245[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "assignment_expression")); + D(fprintf(stderr, "%*c> _tmp_247[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "assignment_expression")); expr_ty assignment_expression_var; if ( (assignment_expression_var = assignment_expression_rule(p)) // assignment_expression ) { - D(fprintf(stderr, "%*c+ _tmp_245[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "assignment_expression")); + D(fprintf(stderr, "%*c+ _tmp_247[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "assignment_expression")); _res = assignment_expression_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_245[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_247[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "assignment_expression")); } { // expression !':=' @@ -38502,7 +38620,7 @@ _tmp_245_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_245[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression !':='")); + D(fprintf(stderr, "%*c> _tmp_247[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression !':='")); expr_ty expression_var; if ( (expression_var = expression_rule(p)) // expression @@ -38510,12 +38628,12 @@ _tmp_245_rule(Parser *p) _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 53) // token=':=' ) { - D(fprintf(stderr, "%*c+ _tmp_245[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression !':='")); + D(fprintf(stderr, "%*c+ _tmp_247[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression !':='")); _res = expression_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_245[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_247[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression !':='")); } _res = NULL; @@ -38524,9 +38642,129 @@ _tmp_245_rule(Parser *p) return _res; } -// _tmp_246: 'as' star_target +// _loop0_249: ',' (starred_expression | (assignment_expression | expression !':=') !'=') +static asdl_seq * +_loop0_249_rule(Parser *p) +{ + if (p->level++ == MAXSTACK) { + p->error_indicator = 1; + PyErr_NoMemory(); + } + if (p->error_indicator) { + p->level--; + return NULL; + } + void *_res = NULL; + int _mark = p->mark; + void **_children = PyMem_Malloc(sizeof(void *)); + if (!_children) { + p->error_indicator = 1; + PyErr_NoMemory(); + p->level--; + return NULL; + } + Py_ssize_t _children_capacity = 1; + Py_ssize_t _n = 0; + { // ',' (starred_expression | (assignment_expression | expression !':=') !'=') + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _loop0_249[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (starred_expression | (assignment_expression | expression !':=') !'=')")); + Token * _literal; + void *elem; + while ( + (_literal = _PyPegen_expect_token(p, 12)) // token=',' + && + (elem = _tmp_254_rule(p)) // starred_expression | (assignment_expression | expression !':=') !'=' + ) + { + _res = elem; + if (_res == NULL && PyErr_Occurred()) { + p->error_indicator = 1; + PyMem_Free(_children); + p->level--; + return NULL; + } + if (_n == _children_capacity) { + _children_capacity *= 2; + void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); + if (!_new_children) { + PyMem_Free(_children); + p->error_indicator = 1; + PyErr_NoMemory(); + p->level--; + return NULL; + } + _children = _new_children; + } + _children[_n++] = _res; + _mark = p->mark; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _loop0_249[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (starred_expression | (assignment_expression | expression !':=') !'=')")); + } + asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); + if (!_seq) { + PyMem_Free(_children); + p->error_indicator = 1; + PyErr_NoMemory(); + p->level--; + return NULL; + } + for (int i = 0; i < _n; i++) asdl_seq_SET_UNTYPED(_seq, i, _children[i]); + PyMem_Free(_children); + p->level--; + return _seq; +} + +// _gather_248: +// | (starred_expression | (assignment_expression | expression !':=') !'=') _loop0_249 +static asdl_seq * +_gather_248_rule(Parser *p) +{ + if (p->level++ == MAXSTACK) { + p->error_indicator = 1; + PyErr_NoMemory(); + } + if (p->error_indicator) { + p->level--; + return NULL; + } + asdl_seq * _res = NULL; + int _mark = p->mark; + { // (starred_expression | (assignment_expression | expression !':=') !'=') _loop0_249 + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _gather_248[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(starred_expression | (assignment_expression | expression !':=') !'=') _loop0_249")); + void *elem; + asdl_seq * seq; + if ( + (elem = _tmp_254_rule(p)) // starred_expression | (assignment_expression | expression !':=') !'=' + && + (seq = _loop0_249_rule(p)) // _loop0_249 + ) + { + D(fprintf(stderr, "%*c+ _gather_248[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(starred_expression | (assignment_expression | expression !':=') !'=') _loop0_249")); + _res = _PyPegen_seq_insert_in_front(p, elem, seq); + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _gather_248[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(starred_expression | (assignment_expression | expression !':=') !'=') _loop0_249")); + } + _res = NULL; + done: + p->level--; + return _res; +} + +// _tmp_250: 'as' star_target static void * -_tmp_246_rule(Parser *p) +_tmp_250_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -38543,7 +38781,7 @@ _tmp_246_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_246[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' star_target")); + D(fprintf(stderr, "%*c> _tmp_250[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' star_target")); Token * _keyword; expr_ty star_target_var; if ( @@ -38552,12 +38790,12 @@ _tmp_246_rule(Parser *p) (star_target_var = star_target_rule(p)) // star_target ) { - D(fprintf(stderr, "%*c+ _tmp_246[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' star_target")); + D(fprintf(stderr, "%*c+ _tmp_250[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' star_target")); _res = _PyPegen_dummy_name(p, _keyword, star_target_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_246[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_250[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'as' star_target")); } _res = NULL; @@ -38566,9 +38804,9 @@ _tmp_246_rule(Parser *p) return _res; } -// _tmp_247: 'as' star_target +// _tmp_251: 'as' star_target static void * -_tmp_247_rule(Parser *p) +_tmp_251_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -38585,7 +38823,7 @@ _tmp_247_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_247[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' star_target")); + D(fprintf(stderr, "%*c> _tmp_251[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' star_target")); Token * _keyword; expr_ty star_target_var; if ( @@ -38594,12 +38832,12 @@ _tmp_247_rule(Parser *p) (star_target_var = star_target_rule(p)) // star_target ) { - D(fprintf(stderr, "%*c+ _tmp_247[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' star_target")); + D(fprintf(stderr, "%*c+ _tmp_251[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' star_target")); _res = _PyPegen_dummy_name(p, _keyword, star_target_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_247[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_251[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'as' star_target")); } _res = NULL; @@ -38608,9 +38846,9 @@ _tmp_247_rule(Parser *p) return _res; } -// _tmp_248: 'as' star_target +// _tmp_252: 'as' star_target static void * -_tmp_248_rule(Parser *p) +_tmp_252_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -38627,7 +38865,7 @@ _tmp_248_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_248[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' star_target")); + D(fprintf(stderr, "%*c> _tmp_252[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' star_target")); Token * _keyword; expr_ty star_target_var; if ( @@ -38636,12 +38874,12 @@ _tmp_248_rule(Parser *p) (star_target_var = star_target_rule(p)) // star_target ) { - D(fprintf(stderr, "%*c+ _tmp_248[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' star_target")); + D(fprintf(stderr, "%*c+ _tmp_252[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' star_target")); _res = _PyPegen_dummy_name(p, _keyword, star_target_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_248[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_252[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'as' star_target")); } _res = NULL; @@ -38650,9 +38888,9 @@ _tmp_248_rule(Parser *p) return _res; } -// _tmp_249: 'as' star_target +// _tmp_253: 'as' star_target static void * -_tmp_249_rule(Parser *p) +_tmp_253_rule(Parser *p) { if (p->level++ == MAXSTACK) { p->error_indicator = 1; @@ -38669,7 +38907,7 @@ _tmp_249_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_249[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' star_target")); + D(fprintf(stderr, "%*c> _tmp_253[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' star_target")); Token * _keyword; expr_ty star_target_var; if ( @@ -38678,12 +38916,12 @@ _tmp_249_rule(Parser *p) (star_target_var = star_target_rule(p)) // star_target ) { - D(fprintf(stderr, "%*c+ _tmp_249[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' star_target")); + D(fprintf(stderr, "%*c+ _tmp_253[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' star_target")); _res = _PyPegen_dummy_name(p, _keyword, star_target_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_249[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_253[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'as' star_target")); } _res = NULL; @@ -38692,6 +38930,126 @@ _tmp_249_rule(Parser *p) return _res; } +// _tmp_254: starred_expression | (assignment_expression | expression !':=') !'=' +static void * +_tmp_254_rule(Parser *p) +{ + if (p->level++ == MAXSTACK) { + p->error_indicator = 1; + PyErr_NoMemory(); + } + if (p->error_indicator) { + p->level--; + return NULL; + } + void * _res = NULL; + int _mark = p->mark; + { // starred_expression + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _tmp_254[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression")); + expr_ty starred_expression_var; + if ( + (starred_expression_var = starred_expression_rule(p)) // starred_expression + ) + { + D(fprintf(stderr, "%*c+ _tmp_254[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression")); + _res = starred_expression_var; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _tmp_254[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "starred_expression")); + } + { // (assignment_expression | expression !':=') !'=' + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _tmp_254[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(assignment_expression | expression !':=') !'='")); + void *_tmp_255_var; + if ( + (_tmp_255_var = _tmp_255_rule(p)) // assignment_expression | expression !':=' + && + _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 22) // token='=' + ) + { + D(fprintf(stderr, "%*c+ _tmp_254[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(assignment_expression | expression !':=') !'='")); + _res = _tmp_255_var; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _tmp_254[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(assignment_expression | expression !':=') !'='")); + } + _res = NULL; + done: + p->level--; + return _res; +} + +// _tmp_255: assignment_expression | expression !':=' +static void * +_tmp_255_rule(Parser *p) +{ + if (p->level++ == MAXSTACK) { + p->error_indicator = 1; + PyErr_NoMemory(); + } + if (p->error_indicator) { + p->level--; + return NULL; + } + void * _res = NULL; + int _mark = p->mark; + { // assignment_expression + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _tmp_255[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "assignment_expression")); + expr_ty assignment_expression_var; + if ( + (assignment_expression_var = assignment_expression_rule(p)) // assignment_expression + ) + { + D(fprintf(stderr, "%*c+ _tmp_255[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "assignment_expression")); + _res = assignment_expression_var; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _tmp_255[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "assignment_expression")); + } + { // expression !':=' + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _tmp_255[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression !':='")); + expr_ty expression_var; + if ( + (expression_var = expression_rule(p)) // expression + && + _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 53) // token=':=' + ) + { + D(fprintf(stderr, "%*c+ _tmp_255[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression !':='")); + _res = expression_var; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _tmp_255[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression !':='")); + } + _res = NULL; + done: + p->level--; + return _res; +} + void * _PyPegen_parse(Parser *p) { From 8e3d90c332b3e9ee84f5cd3d47f98e3c77b0f286 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 18 Oct 2023 15:25:04 +0200 Subject: [PATCH 495/632] [3.11] gh-111015: Install IDLE.app and Python Launcher.app on macOS with correct permissions (gh-111038) (cherry picked from commit cb1bf89c4066f30c80f7d1193b586a2ff8c40579) Co-authored-by: Joshua Root Co-authored-by: Ned Deily --- Mac/Makefile.in | 2 ++ Mac/PythonLauncher/Makefile.in | 2 ++ .../next/macOS/2023-10-18-01-40-36.gh-issue-111015.NaLI2L.rst | 1 + 3 files changed, 5 insertions(+) create mode 100644 Misc/NEWS.d/next/macOS/2023-10-18-01-40-36.gh-issue-111015.NaLI2L.rst diff --git a/Mac/Makefile.in b/Mac/Makefile.in index f9691288414538..a9c126bae5b336 100644 --- a/Mac/Makefile.in +++ b/Mac/Makefile.in @@ -260,6 +260,8 @@ install_IDLE: rm "$(DESTDIR)$(LIBDEST)/idlelib/config-extensions.def~" ; \ fi touch "$(DESTDIR)$(PYTHONAPPSDIR)/IDLE.app" + chmod -R ugo+rX,go-w "$(DESTDIR)$(PYTHONAPPSDIR)/IDLE.app" + chmod ugo+x "$(DESTDIR)$(PYTHONAPPSDIR)/IDLE.app/Contents/MacOS/IDLE" $(INSTALLED_PYTHONAPP): install_Python diff --git a/Mac/PythonLauncher/Makefile.in b/Mac/PythonLauncher/Makefile.in index 4c05f26e8358bc..9decadbdc60f00 100644 --- a/Mac/PythonLauncher/Makefile.in +++ b/Mac/PythonLauncher/Makefile.in @@ -27,6 +27,8 @@ install: Python\ Launcher.app -test -d "$(DESTDIR)$(PYTHONAPPSDIR)/Python Launcher.app" && rm -r "$(DESTDIR)$(PYTHONAPPSDIR)/Python Launcher.app" /bin/cp -r "Python Launcher.app" "$(DESTDIR)$(PYTHONAPPSDIR)" touch "$(DESTDIR)$(PYTHONAPPSDIR)/Python Launcher.app" + chmod -R ugo+rX,go-w "$(DESTDIR)$(PYTHONAPPSDIR)/Python Launcher.app" + chmod ugo+x "$(DESTDIR)$(PYTHONAPPSDIR)/Python Launcher.app/Contents/MacOS/Python Launcher" clean: rm -f *.o "Python Launcher" 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 new file mode 100644 index 00000000000000..4c6eea136554c8 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2023-10-18-01-40-36.gh-issue-111015.NaLI2L.rst @@ -0,0 +1 @@ +Ensure that IDLE.app and Python Launcher.app are installed with appropriate permissions on macOS builds. From faa7c207bfe6b04c88f8440d95c36739ae69d518 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 18 Oct 2023 22:34:20 +0200 Subject: [PATCH 496/632] [3.11] GH-104232: Fix statement about trace return values (GH-111045) (cherry picked from commit d9246c7b734b8958da03494045208681d95f5b74) --- Doc/library/sys.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 34cb4cf038fe7e..65c20748f862b5 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -1520,9 +1520,8 @@ always available. function to be used for the new scope, or ``None`` if the scope shouldn't be traced. - The local trace function should return a reference to itself (or to another - function for further tracing in that scope), or ``None`` to turn off tracing - in that scope. + The local trace function should return a reference to itself, or to another + function which would then be used as the local trace function for the scope. If there is any error occurred in the trace function, it will be unset, just like ``settrace(None)`` is called. From fabfc2ccfc50e58284da48db9cb63f9dd58606a6 Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Wed, 18 Oct 2023 16:42:36 -0700 Subject: [PATCH 497/632] [3.11] GH-65052: Prevent pdb from crashing when trying to display objects (GH-111002) (cherry picked from commit c523ce0f434582580a3721e15cb7dd6b56ad0236) --- Lib/pdb.py | 23 ++++++--- Lib/test/test_pdb.py | 47 +++++++++++++++++++ ...3-10-09-19-09-32.gh-issue-65052.C2mRlo.rst | 1 + 3 files changed, 64 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-10-09-19-09-32.gh-issue-65052.C2mRlo.rst diff --git a/Lib/pdb.py b/Lib/pdb.py index fe9eab9b5e1ff0..5dfe5077654c21 100755 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -405,8 +405,9 @@ def preloop(self): # fields are changed to be displayed if newvalue is not oldvalue and newvalue != oldvalue: displaying[expr] = newvalue - self.message('display %s: %r [old: %r]' % - (expr, newvalue, oldvalue)) + self.message('display %s: %s [old: %s]' % + (expr, self._safe_repr(newvalue, expr), + self._safe_repr(oldvalue, expr))) def interaction(self, frame, traceback): # Restore the previous signal handler at the Pdb prompt. @@ -1221,7 +1222,7 @@ def do_args(self, arg): for i in range(n): name = co.co_varnames[i] if name in dict: - self.message('%s = %r' % (name, dict[name])) + self.message('%s = %s' % (name, self._safe_repr(dict[name], name))) else: self.message('%s = *** undefined ***' % (name,)) do_a = do_args @@ -1231,7 +1232,7 @@ def do_retval(self, arg): Print the return value for the last return of a function. """ if '__return__' in self.curframe_locals: - self.message(repr(self.curframe_locals['__return__'])) + self.message(self._safe_repr(self.curframe_locals['__return__'], "retval")) else: self.error('Not yet returned!') do_rv = do_retval @@ -1268,6 +1269,12 @@ def _msg_val_func(self, arg, func): except: self._error_exc() + def _safe_repr(self, obj, expr): + try: + return repr(obj) + except Exception as e: + return _rstr(f"*** repr({expr}) failed: {self._format_exc(e)} ***") + def do_p(self, arg): """p expression Print the value of the expression. @@ -1438,12 +1445,12 @@ def do_display(self, arg): """ if not arg: self.message('Currently displaying:') - for item in self.displaying.get(self.curframe, {}).items(): - self.message('%s: %r' % item) + for key, val in self.displaying.get(self.curframe, {}).items(): + self.message('%s: %s' % (key, self._safe_repr(val, key))) else: val = self._getval_except(arg) self.displaying.setdefault(self.curframe, {})[arg] = val - self.message('display %s: %r' % (arg, val)) + self.message('display %s: %s' % (arg, self._safe_repr(val, arg))) complete_display = _complete_expression @@ -1645,6 +1652,8 @@ def _run(self, target: Union[_ModuleTarget, _ScriptTarget]): self.run(target.code) + def _format_exc(self, exc: BaseException): + return traceback.format_exception_only(exc)[-1].strip() def _getsourcelines(self, obj): # GH-103319 diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 22fc2b35819466..8a0350b8590d8e 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -1704,6 +1704,53 @@ def test_pdb_issue_gh_103225(): (Pdb) continue """ +def test_pdb_issue_gh_65052(): + """See GH-65052 + + args, retval and display should not crash if the object is not displayable + >>> class A: + ... def __new__(cls): + ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() + ... return object.__new__(cls) + ... def __init__(self): + ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() + ... self.a = 1 + ... def __repr__(self): + ... return self.a + + >>> def test_function(): + ... A() + >>> with PdbTestInput([ # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE + ... 's', + ... 'retval', + ... 'continue', + ... 'args', + ... 'display self', + ... 'display', + ... 'continue', + ... ]): + ... test_function() + > (4)__new__() + -> return object.__new__(cls) + (Pdb) s + --Return-- + > (4)__new__()-> + -> return object.__new__(cls) + (Pdb) retval + *** repr(retval) failed: AttributeError: 'A' object has no attribute 'a' *** + (Pdb) continue + > (7)__init__() + -> self.a = 1 + (Pdb) args + self = *** repr(self) failed: AttributeError: 'A' object has no attribute 'a' *** + (Pdb) display self + display self: *** repr(self) failed: AttributeError: 'A' object has no attribute 'a' *** + (Pdb) display + Currently displaying: + self: *** repr(self) failed: AttributeError: 'A' object has no attribute 'a' *** + (Pdb) continue + """ + @support.requires_subprocess() class PdbTestCase(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2023-10-09-19-09-32.gh-issue-65052.C2mRlo.rst b/Misc/NEWS.d/next/Library/2023-10-09-19-09-32.gh-issue-65052.C2mRlo.rst new file mode 100644 index 00000000000000..4739c63bb3cc9d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-10-09-19-09-32.gh-issue-65052.C2mRlo.rst @@ -0,0 +1 @@ +Prevent :mod:`pdb` from crashing when trying to display undisplayable objects From 2258d6cfa2fcd0c290328e81b68aff25abf8c3d8 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 19 Oct 2023 06:30:43 +0200 Subject: [PATCH 498/632] [3.11] gh-111050: IDLE - Simplify configdialog.HighPage.theme_elements (GH-111053) (#111056) gh-111050: IDLE - Simplify configdialog.HighPage.theme_elements (GH-111053) Replace tuple value with internal name, removing numbers. Remove sorting of already ordered dislay names. Remove '[0]' indexing into now-gone tuple. (cherry picked from commit 642eb8df951f2f1d4bf4d93ee568707c5bf40a96) Co-authored-by: Terry Jan Reedy --- Lib/idlelib/configdialog.py | 44 +++++++++++----------- Lib/idlelib/idle_test/test_configdialog.py | 2 +- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index d56afe8a807fea..eedf97bf74fe6a 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -576,24 +576,23 @@ def create_page_highlight(self): (*)theme_message: Label """ self.theme_elements = { - # Display_name: ('internal_name, sort_number'). - # TODO: remove sort_number unneeded with dict ordering. - 'Normal Code or Text': ('normal', '00'), - 'Code Context': ('context', '01'), - 'Python Keywords': ('keyword', '02'), - 'Python Definitions': ('definition', '03'), - 'Python Builtins': ('builtin', '04'), - 'Python Comments': ('comment', '05'), - 'Python Strings': ('string', '06'), - 'Selected Text': ('hilite', '07'), - 'Found Text': ('hit', '08'), - 'Cursor': ('cursor', '09'), - 'Editor Breakpoint': ('break', '10'), - 'Shell Prompt': ('console', '11'), - 'Error Text': ('error', '12'), - 'Shell User Output': ('stdout', '13'), - 'Shell User Exception': ('stderr', '14'), - 'Line Number': ('linenumber', '16'), + # Display-name: internal-config-tag-name. + 'Normal Code or Text': 'normal', + 'Code Context': 'context', + 'Python Keywords': 'keyword', + 'Python Definitions': 'definition', + 'Python Builtins': 'builtin', + 'Python Comments': 'comment', + 'Python Strings': 'string', + 'Selected Text': 'hilite', + 'Found Text': 'hit', + 'Cursor': 'cursor', + 'Editor Breakpoint': 'break', + 'Shell Prompt': 'console', + 'Error Text': 'error', + 'Shell User Output': 'stdout', + 'Shell User Exception': 'stderr', + 'Line Number': 'linenumber', } self.builtin_name = tracers.add( StringVar(self), self.var_changed_builtin_name) @@ -653,7 +652,7 @@ def tem(event, elem=element): # event.widget.winfo_top_level().highlight_target.set(elem) self.highlight_target.set(elem) text.tag_bind( - self.theme_elements[element][0], '', tem) + self.theme_elements[element], '', tem) text['state'] = 'disabled' self.style.configure('frame_color_set.TFrame', borderwidth=1, relief='solid') @@ -761,7 +760,6 @@ def load_theme_cfg(self): self.set_theme_type() # Load theme element option menu. theme_names = list(self.theme_elements) - theme_names.sort(key=lambda x: self.theme_elements[x][1]) self.targetlist.SetMenu(theme_names, theme_names[0]) self.paint_theme_sample() self.set_highlight_target() @@ -888,7 +886,7 @@ def on_new_color_set(self): new_color = self.color.get() self.style.configure('frame_color_set.TFrame', background=new_color) plane = 'foreground' if self.fg_bg_toggle.get() else 'background' - sample_element = self.theme_elements[self.highlight_target.get()][0] + sample_element = self.theme_elements[self.highlight_target.get()] self.highlight_sample.tag_config(sample_element, **{plane: new_color}) theme = self.custom_name.get() theme_element = sample_element + '-' + plane @@ -1002,7 +1000,7 @@ def set_color_sample(self): frame_color_set """ # Set the color sample area. - tag = self.theme_elements[self.highlight_target.get()][0] + tag = self.theme_elements[self.highlight_target.get()] plane = 'foreground' if self.fg_bg_toggle.get() else 'background' color = self.highlight_sample.tag_cget(tag, plane) self.style.configure('frame_color_set.TFrame', background=color) @@ -1032,7 +1030,7 @@ def paint_theme_sample(self): else: # User theme theme = self.custom_name.get() for element_title in self.theme_elements: - element = self.theme_elements[element_title][0] + element = self.theme_elements[element_title] colors = idleConf.GetHighlight(theme, element) if element == 'cursor': # Cursor sample needs special painting. colors['background'] = idleConf.GetHighlight( diff --git a/Lib/idlelib/idle_test/test_configdialog.py b/Lib/idlelib/idle_test/test_configdialog.py index e5d5b4013fca57..6f8518a9bb19d0 100644 --- a/Lib/idlelib/idle_test/test_configdialog.py +++ b/Lib/idlelib/idle_test/test_configdialog.py @@ -430,7 +430,7 @@ def test_highlight_target_text_mouse(self): def tag_to_element(elem): for element, tag in d.theme_elements.items(): - elem[tag[0]] = element + elem[tag] = element def click_it(start): x, y, dx, dy = hs.bbox(start) From 26a028269b807d599ea2f5fa266fccfd1421775b Mon Sep 17 00:00:00 2001 From: Radislav Chugunov <52372310+chgnrdv@users.noreply.github.com> Date: Thu, 19 Oct 2023 16:26:40 +0300 Subject: [PATCH 499/632] [3.11] gh-108791: Fix pdb CLI invalid argument handling (GH-108816) (#111063) * [3.11] gh-108791: Fix `pdb` CLI invalid argument handling (GH-108816) (cherry picked from commit 162213f2db3835e1115178d38741544f4b4db416) Co-authored-by: Radislav Chugunov <52372310+chgnrdv@users.noreply.github.com> --- Lib/pdb.py | 6 ++++++ Lib/test/test_pdb.py | 19 +++++++++++++++++-- ...-09-02-16-07-23.gh-issue-108791.fBcAqh.rst | 1 + 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-09-02-16-07-23.gh-issue-108791.fBcAqh.rst diff --git a/Lib/pdb.py b/Lib/pdb.py index 5dfe5077654c21..4a4a0b9d90f6d2 100755 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -136,6 +136,9 @@ def check(self): if not os.path.exists(self): print('Error:', self.orig, 'does not exist') sys.exit(1) + if os.path.isdir(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) @@ -162,6 +165,9 @@ class _ModuleTarget(str): def check(self): try: self._details + except ImportError as e: + print(f"ImportError: {e}") + sys.exit(1) except Exception: traceback.print_exc() sys.exit(1) diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 8a0350b8590d8e..5f9bcc5af82ad6 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -2159,8 +2159,7 @@ def test_module_without_a_main(self): stdout, stderr = self._run_pdb( ['-m', module_name], "", expected_returncode=1 ) - self.assertIn("ImportError: No module named t_main.__main__", - stdout.splitlines()) + self.assertIn("ImportError: No module named t_main.__main__;", stdout) def test_package_without_a_main(self): pkg_name = 't_pkg' @@ -2178,6 +2177,22 @@ def test_package_without_a_main(self): "'t_pkg.t_main' is a package and cannot be directly executed", stdout) + def test_nonexistent_module(self): + assert not os.path.exists(os_helper.TESTFN) + stdout, stderr = self._run_pdb(["-m", os_helper.TESTFN], "", expected_returncode=1) + self.assertIn(f"ImportError: No module named {os_helper.TESTFN}", stdout) + + def test_dir_as_script(self): + with os_helper.temp_dir() as temp_dir: + stdout, stderr = self._run_pdb([temp_dir], "", expected_returncode=1) + self.assertIn(f"Error: {temp_dir} is a directory", stdout) + + def test_invalid_cmd_line_options(self): + stdout, stderr = self._run_pdb(["-c"], "", expected_returncode=1) + self.assertIn(f"Error: option -c requires argument", stdout) + stdout, stderr = self._run_pdb(["--spam"], "", expected_returncode=1) + self.assertIn(f"Error: option --spam not recognized", stdout) + def test_blocks_at_first_code_line(self): script = """ #This is a comment, on line 2 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 new file mode 100644 index 00000000000000..84a2cd589e10d5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-09-02-16-07-23.gh-issue-108791.fBcAqh.rst @@ -0,0 +1 @@ +Improved error handling in :mod:`pdb` command line interface, making it produce more concise error messages. From 4fc5352a0d52d8077b4c2ad03c963a99b1fbe790 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 19 Oct 2023 17:31:29 +0200 Subject: [PATCH 500/632] [3.11] gh-101100: Fix sphinx warnings in `library/getpass.rst` (GH-110461) (#111072) Co-authored-by: Nikita Sobolev --- Doc/library/getpass.rst | 2 +- Doc/tools/.nitignore | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Doc/library/getpass.rst b/Doc/library/getpass.rst index d5bbe67fb30a62..5c79daf0f47d8e 100644 --- a/Doc/library/getpass.rst +++ b/Doc/library/getpass.rst @@ -43,7 +43,7 @@ The :mod:`getpass` module provides two functions: Return the "login name" of the user. This function checks the environment variables :envvar:`LOGNAME`, - :envvar:`USER`, :envvar:`LNAME` and :envvar:`USERNAME`, in order, and + :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 diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 0ff65ccb21d378..b47924f25348fa 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -71,7 +71,6 @@ Doc/library/fcntl.rst Doc/library/ftplib.rst Doc/library/functions.rst Doc/library/functools.rst -Doc/library/getpass.rst Doc/library/gettext.rst Doc/library/gzip.rst Doc/library/http.client.rst From 1b60244b97f9181d943bc834c83fc5f9440df16b Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 19 Oct 2023 17:33:12 +0200 Subject: [PATCH 501/632] [3.11] GH-101100: Fix reference warnings for ``__getitem__`` (GH-110118) (#111074) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/glossary.rst | 8 ++++---- Doc/library/abc.rst | 2 +- Doc/library/collections.abc.rst | 8 ++++---- Doc/library/collections.rst | 6 +++--- Doc/library/email.compat32-message.rst | 2 +- Doc/library/email.message.rst | 2 +- Doc/library/functions.rst | 4 ++-- Doc/library/mailbox.rst | 2 +- Doc/library/operator.rst | 4 ++-- Doc/library/stdtypes.rst | 2 +- Doc/library/unittest.mock.rst | 2 +- Doc/library/wsgiref.rst | 4 ++-- Doc/library/xml.dom.pulldom.rst | 2 +- Doc/reference/compound_stmts.rst | 2 +- Doc/reference/expressions.rst | 6 +++--- Doc/whatsnew/2.2.rst | 10 +++++----- Doc/whatsnew/2.3.rst | 4 ++-- Doc/whatsnew/3.8.rst | 2 +- Misc/NEWS.d/3.11.0a1.rst | 2 +- Misc/NEWS.d/3.8.0a1.rst | 2 +- 20 files changed, 38 insertions(+), 38 deletions(-) diff --git a/Doc/glossary.rst b/Doc/glossary.rst index 81599477fc9534..7c91a0a8184e6b 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -645,7 +645,7 @@ 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:`__getitem__` method + with an :meth:`__iter__` method or with a :meth:`~object.__getitem__` method that implements :term:`sequence` semantics. Iterables can be @@ -1085,17 +1085,17 @@ Glossary sequence An :term:`iterable` which supports efficient element access using integer - indices via the :meth:`__getitem__` special method and defines a + indices via the :meth:`~object.__getitem__` special method and defines a :meth:`__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:`__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:`__getitem__` and :meth:`__len__`, adding :meth:`count`, + :meth:`~object.__getitem__` and :meth:`__len__`, adding :meth:`count`, :meth:`index`, :meth:`__contains__`, and :meth:`__reversed__`. Types that implement this expanded interface can be registered explicitly using diff --git a/Doc/library/abc.rst b/Doc/library/abc.rst index 274b2d69f0ab5c..fb4f9da169c5ab 100644 --- a/Doc/library/abc.rst +++ b/Doc/library/abc.rst @@ -154,7 +154,7 @@ 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 - :meth:`__getitem__`). Note that this will not make ``get_iterator`` + :meth:`~object.__getitem__`). Note that this will not make ``get_iterator`` available as a method of ``Foo``, so it is provided separately. diff --git a/Doc/library/collections.abc.rst b/Doc/library/collections.abc.rst index 1ada0d352a0cc6..e4695b44320600 100644 --- a/Doc/library/collections.abc.rst +++ b/Doc/library/collections.abc.rst @@ -191,7 +191,7 @@ ABC Inherits from Abstract Methods Mi .. [2] Checking ``isinstance(obj, Iterable)`` detects classes that are registered as :class:`Iterable` or that have an :meth:`__iter__` method, but it does not detect classes that iterate with the - :meth:`__getitem__` method. The only reliable way to determine + :meth:`~object.__getitem__` method. The only reliable way to determine whether an object is :term:`iterable` is to call ``iter(obj)``. @@ -221,7 +221,7 @@ Collections Abstract Base Classes -- Detailed Descriptions Checking ``isinstance(obj, Iterable)`` detects classes that are registered as :class:`Iterable` or that have an :meth:`__iter__` method, but it does - not detect classes that iterate with the :meth:`__getitem__` method. + not detect classes that iterate with the :meth:`~object.__getitem__` method. The only reliable way to determine whether an object is :term:`iterable` is to call ``iter(obj)``. @@ -261,8 +261,8 @@ Collections Abstract Base Classes -- Detailed Descriptions Implementation note: Some of the mixin methods, such as :meth:`__iter__`, :meth:`__reversed__` and :meth:`index`, make - repeated calls to the underlying :meth:`__getitem__` method. - Consequently, if :meth:`__getitem__` is implemented with constant + repeated calls to the underlying :meth:`~object.__getitem__` method. + Consequently, if :meth:`~object.__getitem__` is implemented with constant access speed, the mixin methods will have linear performance; however, if the underlying method is linear (as it would be with a linked list), the mixins will have quadratic performance and will diff --git a/Doc/library/collections.rst b/Doc/library/collections.rst index 5ea3a5dd6254c7..a29cc9530390bc 100644 --- a/Doc/library/collections.rst +++ b/Doc/library/collections.rst @@ -742,12 +742,12 @@ stack manipulations such as ``dup``, ``drop``, ``swap``, ``over``, ``pick``, If calling :attr:`default_factory` raises an exception this exception is propagated unchanged. - This method is called by the :meth:`__getitem__` method of the + This method is called by the :meth:`~object.__getitem__` method of the :class:`dict` class when the requested key is not found; whatever it - returns or raises is then returned or raised by :meth:`__getitem__`. + returns or raises is then returned or raised by :meth:`~object.__getitem__`. Note that :meth:`__missing__` is *not* called for any operations besides - :meth:`__getitem__`. This means that :meth:`get` will, like normal + :meth:`~object.__getitem__`. This means that :meth:`get` will, like normal dictionaries, return ``None`` as a default rather than using :attr:`default_factory`. diff --git a/Doc/library/email.compat32-message.rst b/Doc/library/email.compat32-message.rst index 5bef155a4af310..c4c322a82e1f44 100644 --- a/Doc/library/email.compat32-message.rst +++ b/Doc/library/email.compat32-message.rst @@ -367,7 +367,7 @@ Here are the methods of the :class:`Message` class: .. method:: get(name, failobj=None) Return the value of the named header field. This is identical to - :meth:`__getitem__` except that optional *failobj* is returned if the + :meth:`~object.__getitem__` except that optional *failobj* is returned if the named header is missing (defaults to ``None``). Here are some additional useful methods: diff --git a/Doc/library/email.message.rst b/Doc/library/email.message.rst index 225f498781fa86..f58d93da6ed687 100644 --- a/Doc/library/email.message.rst +++ b/Doc/library/email.message.rst @@ -247,7 +247,7 @@ message objects. .. method:: get(name, failobj=None) Return the value of the named header field. This is identical to - :meth:`__getitem__` except that optional *failobj* is returned if the + :meth:`~object.__getitem__` except that optional *failobj* is returned if the named header is missing (*failobj* defaults to ``None``). diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 5e5fd467058edf..c287f52156ba63 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -982,7 +982,7 @@ are always available. They are listed here in alphabetical order. 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 - the sequence protocol (the :meth:`__getitem__` method with integer arguments + 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, then *object* must be a callable object. The iterator created in this case @@ -1562,7 +1562,7 @@ are always available. They are listed here in alphabetical order. 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:`__getitem__` method with integer + :meth:`__len__` method and the :meth:`~object.__getitem__` method with integer arguments starting at ``0``). diff --git a/Doc/library/mailbox.rst b/Doc/library/mailbox.rst index 91df07d914cae2..b27deb20f13236 100644 --- a/Doc/library/mailbox.rst +++ b/Doc/library/mailbox.rst @@ -167,7 +167,7 @@ Supported mailbox formats are Maildir, mbox, MH, Babyl, and MMDF. Return a representation of the message corresponding to *key*. If no such message exists, *default* is returned if the method was called as :meth:`get` and a :exc:`KeyError` exception is raised if the method was - called as :meth:`__getitem__`. The message is represented as an instance + called as :meth:`~object.__getitem__`. The message is represented as an instance of the appropriate format-specific :class:`Message` subclass unless a custom message factory was specified when the :class:`Mailbox` instance was initialized. diff --git a/Doc/library/operator.rst b/Doc/library/operator.rst index 57c67bcf3aa12e..96f2c287875d41 100644 --- a/Doc/library/operator.rst +++ b/Doc/library/operator.rst @@ -306,7 +306,7 @@ expect a function argument. itemgetter(*items) Return a callable object that fetches *item* from its operand using the - operand's :meth:`__getitem__` method. If multiple items are specified, + operand's :meth:`~object.__getitem__` method. If multiple items are specified, returns a tuple of lookup values. For example: * After ``f = itemgetter(2)``, the call ``f(r)`` returns ``r[2]``. @@ -326,7 +326,7 @@ expect a function argument. return tuple(obj[item] for item in items) return g - The items can be any type accepted by the operand's :meth:`__getitem__` + The items can be any type accepted by the operand's :meth:`~object.__getitem__` method. Dictionaries accept any :term:`hashable` value. Lists, tuples, and strings accept an index or a slice: diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index e9ecd7821ec837..ad3135cbc146ed 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -2220,7 +2220,7 @@ expression support in the :mod:`re` module). Return a copy of the string in which each character has been mapped through the given translation table. The table must be an object that implements - indexing via :meth:`__getitem__`, typically a :term:`mapping` or + indexing via :meth:`~object.__getitem__`, typically a :term:`mapping` or :term:`sequence`. When indexed by a Unicode ordinal (an integer), the table object can do any of the following: return a Unicode ordinal or a string, to map the character to one or more other characters; return diff --git a/Doc/library/unittest.mock.rst b/Doc/library/unittest.mock.rst index d3398e9407d40f..dee5c9e88a0a44 100644 --- a/Doc/library/unittest.mock.rst +++ b/Doc/library/unittest.mock.rst @@ -1656,7 +1656,7 @@ Keywords can be used in the :func:`patch.dict` call to set values in the diction :func:`patch.dict` can be used with dictionary like objects that aren't actually dictionaries. At the very minimum they must support item getting, setting, deleting and either iteration or membership test. This corresponds to the -magic methods :meth:`__getitem__`, :meth:`__setitem__`, :meth:`__delitem__` and either +magic methods :meth:`~object.__getitem__`, :meth:`__setitem__`, :meth:`__delitem__` and either :meth:`__iter__` or :meth:`__contains__`. >>> class Container: diff --git a/Doc/library/wsgiref.rst b/Doc/library/wsgiref.rst index 39a4c1ba142338..be9e56b04c1fbf 100644 --- a/Doc/library/wsgiref.rst +++ b/Doc/library/wsgiref.rst @@ -180,7 +180,7 @@ also provides these miscellaneous utilities: print(chunk) .. versionchanged:: 3.11 - Support for :meth:`__getitem__` method has been removed. + Support for :meth:`~object.__getitem__` method has been removed. :mod:`wsgiref.headers` -- WSGI response header tools @@ -201,7 +201,7 @@ manipulation of WSGI response headers using a mapping-like interface. an empty list. :class:`Headers` objects support typical mapping operations including - :meth:`__getitem__`, :meth:`get`, :meth:`__setitem__`, :meth:`setdefault`, + :meth:`~object.__getitem__`, :meth:`get`, :meth:`__setitem__`, :meth:`setdefault`, :meth:`__delitem__` and :meth:`__contains__`. For each of these methods, the key is the header name (treated case-insensitively), and the value is the first value associated with that header name. Setting a header diff --git a/Doc/library/xml.dom.pulldom.rst b/Doc/library/xml.dom.pulldom.rst index d1df465a598e53..843c2fd7fdb937 100644 --- a/Doc/library/xml.dom.pulldom.rst +++ b/Doc/library/xml.dom.pulldom.rst @@ -115,7 +115,7 @@ DOMEventStream Objects .. class:: DOMEventStream(stream, parser, bufsize) .. versionchanged:: 3.11 - Support for :meth:`__getitem__` method has been removed. + Support for :meth:`~object.__getitem__` method has been removed. .. method:: getEvent() diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index 19df7f63f12536..4efe30dbd25b83 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -1060,7 +1060,7 @@ subject value: .. note:: Key-value pairs are matched using the two-argument form of the mapping subject's ``get()`` method. Matched key-value pairs must already be present in the mapping, and not created on-the-fly via :meth:`__missing__` or - :meth:`__getitem__`. + :meth:`~object.__getitem__`. In simple terms ``{KEY1: P1, KEY2: P2, ... }`` matches only if all the following happens: diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index 3c4706714f306c..5cba4a6934f05b 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -872,7 +872,7 @@ to the index so that, for example, ``x[-1]`` selects the last item of ``x``. The resulting value must be a nonnegative integer less than the number of items in the sequence, and the subscription selects the item whose index is that value (counting from zero). Since the support for negative indices and slicing -occurs in the object's :meth:`__getitem__` method, subclasses overriding +occurs in the object's :meth:`~object.__getitem__` method, subclasses overriding this method will need to explicitly add that support. .. index:: @@ -927,7 +927,7 @@ slice list contains no proper slice). single: step (slice object attribute) The semantics for a slicing are as follows. The primary is indexed (using the -same :meth:`__getitem__` method as +same :meth:`~object.__getitem__` method as normal subscription) with a key that is constructed from the slice list, as follows. If the slice list contains at least one comma, the key is a tuple containing the conversion of the slice items; otherwise, the conversion of the @@ -1653,7 +1653,7 @@ If an exception is raised during the iteration, it is as if :keyword:`in` raised that exception. Lastly, the old-style iteration protocol is tried: if a class defines -:meth:`__getitem__`, ``x in y`` is ``True`` if and only if there is a non-negative +:meth:`~object.__getitem__`, ``x in y`` is ``True`` if and only if there is a non-negative integer index *i* such that ``x is y[i] or x == y[i]``, and no lower integer index raises the :exc:`IndexError` exception. (If any other exception is raised, it is as if :keyword:`in` raised that exception). diff --git a/Doc/whatsnew/2.2.rst b/Doc/whatsnew/2.2.rst index d9ead57413cbbf..6dfe79cef00987 100644 --- a/Doc/whatsnew/2.2.rst +++ b/Doc/whatsnew/2.2.rst @@ -424,22 +424,22 @@ Another significant addition to 2.2 is an iteration interface at both the C and Python levels. Objects can define how they can be looped over by callers. In Python versions up to 2.1, the usual way to make ``for item in obj`` work is -to define a :meth:`__getitem__` method that looks something like this:: +to define a :meth:`~object.__getitem__` method that looks something like this:: def __getitem__(self, index): return -:meth:`__getitem__` is more properly used to define an indexing operation on an +:meth:`~object.__getitem__` is more properly used to define an indexing operation on an object so that you can write ``obj[5]`` to retrieve the sixth element. It's a bit misleading when you're using this only to support :keyword:`for` loops. Consider some file-like object that wants to be looped over; the *index* parameter is essentially meaningless, as the class probably assumes that a -series of :meth:`__getitem__` calls will be made with *index* incrementing by -one each time. In other words, the presence of the :meth:`__getitem__` method +series of :meth:`~object.__getitem__` calls will be made with *index* incrementing by +one each time. In other words, the presence of the :meth:`~object.__getitem__` method doesn't mean that using ``file[5]`` to randomly access the sixth element will work, though it really should. -In Python 2.2, iteration can be implemented separately, and :meth:`__getitem__` +In Python 2.2, iteration can be implemented separately, and :meth:`~object.__getitem__` methods can be limited to classes that really do support random access. The basic idea of iterators is simple. A new built-in function, ``iter(obj)`` or ``iter(C, sentinel)``, is used to get an iterator. ``iter(obj)`` returns diff --git a/Doc/whatsnew/2.3.rst b/Doc/whatsnew/2.3.rst index 3460421fe45cce..8a4a9c412cf259 100644 --- a/Doc/whatsnew/2.3.rst +++ b/Doc/whatsnew/2.3.rst @@ -925,7 +925,7 @@ Deletion is more straightforward:: >>> a [1, 3] -One can also now pass slice objects to the :meth:`__getitem__` methods of the +One can also now pass slice objects to the :meth:`~object.__getitem__` methods of the built-in sequences:: >>> range(10).__getitem__(slice(0, 5, 2)) @@ -1596,7 +1596,7 @@ complete list of changes, or look through the CVS logs for all the details. module. Adding the mix-in as a superclass provides the full dictionary interface - whenever the class defines :meth:`__getitem__`, :meth:`__setitem__`, + whenever the class defines :meth:`~object.__getitem__`, :meth:`__setitem__`, :meth:`__delitem__`, and :meth:`keys`. For example:: >>> import UserDict diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index 3789164cc1f72e..0a402bd0176004 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -1650,7 +1650,7 @@ Deprecated deprecated and will be prohibited in Python 3.9. (Contributed by Elvis Pranskevichus in :issue:`34075`.) -* The :meth:`__getitem__` methods of :class:`xml.dom.pulldom.DOMEventStream`, +* The :meth:`~object.__getitem__` methods of :class:`xml.dom.pulldom.DOMEventStream`, :class:`wsgiref.util.FileWrapper` and :class:`fileinput.FileInput` have been deprecated. diff --git a/Misc/NEWS.d/3.11.0a1.rst b/Misc/NEWS.d/3.11.0a1.rst index 46c65300abf2c2..38f1e15f5b1087 100644 --- a/Misc/NEWS.d/3.11.0a1.rst +++ b/Misc/NEWS.d/3.11.0a1.rst @@ -1720,7 +1720,7 @@ Improve the speed and accuracy of statistics.pvariance(). .. nonce: WI9zQY .. section: Library -Remove :meth:`__getitem__` methods of +Remove :meth:`~object.__getitem__` methods of :class:`xml.dom.pulldom.DOMEventStream`, :class:`wsgiref.util.FileWrapper` and :class:`fileinput.FileInput`, deprecated since Python 3.9. diff --git a/Misc/NEWS.d/3.8.0a1.rst b/Misc/NEWS.d/3.8.0a1.rst index 4adacfd41809d5..104e6649f24120 100644 --- a/Misc/NEWS.d/3.8.0a1.rst +++ b/Misc/NEWS.d/3.8.0a1.rst @@ -3592,7 +3592,7 @@ Python 3.5. .. nonce: V8Ou3K .. section: Library -Deprecate :meth:`__getitem__` methods of +Deprecate :meth:`~object.__getitem__` methods of :class:`xml.dom.pulldom.DOMEventStream`, :class:`wsgiref.util.FileWrapper` and :class:`fileinput.FileInput`. From f22cdecb1950d87154f15b9a67819ecc472ff510 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 19 Oct 2023 17:34:27 +0200 Subject: [PATCH 502/632] [3.11] GH-101100: Fix reference warnings for ``__enter__`` and ``__exit__`` (GH-110112) (#111076) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/glossary.rst | 2 +- Doc/library/contextlib.rst | 22 +++++++++++----------- Doc/library/stdtypes.rst | 2 +- Doc/library/test.rst | 2 +- Doc/library/unittest.mock.rst | 4 ++-- Doc/reference/compound_stmts.rst | 20 ++++++++++---------- Doc/reference/datamodel.rst | 6 +++--- Doc/whatsnew/2.5.rst | 22 +++++++++++----------- Doc/whatsnew/2.6.rst | 26 +++++++++++++------------- Doc/whatsnew/2.7.rst | 8 ++++---- Misc/NEWS.d/3.10.0a7.rst | 2 +- 11 files changed, 58 insertions(+), 58 deletions(-) diff --git a/Doc/glossary.rst b/Doc/glossary.rst index 7c91a0a8184e6b..f318a37b654e9d 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -248,7 +248,7 @@ Glossary context manager An object which controls the environment seen in a :keyword:`with` - statement by defining :meth:`__enter__` and :meth:`__exit__` methods. + statement by defining :meth:`~object.__enter__` and :meth:`~object.__exit__` methods. See :pep:`343`. context variable diff --git a/Doc/library/contextlib.rst b/Doc/library/contextlib.rst index 1b55868c3aa62f..80a683d4756fe8 100644 --- a/Doc/library/contextlib.rst +++ b/Doc/library/contextlib.rst @@ -45,7 +45,7 @@ Functions and classes provided: This function is a :term:`decorator` that can be used to define a factory function for :keyword:`with` statement context managers, without needing to - create a class or separate :meth:`__enter__` and :meth:`__exit__` methods. + create a class or separate :meth:`~object.__enter__` and :meth:`~object.__exit__` methods. While many objects natively support use in with statements, sometimes a resource needs to be managed that isn't a context manager in its own right, @@ -508,7 +508,7 @@ Functions and classes provided: # the with statement, even if attempts to open files later # in the list raise an exception - The :meth:`__enter__` method returns the :class:`ExitStack` instance, and + The :meth:`~object.__enter__` method returns the :class:`ExitStack` instance, and performs no additional operations. Each instance maintains a stack of registered callbacks that are called in @@ -536,9 +536,9 @@ Functions and classes provided: .. method:: enter_context(cm) - Enters a new context manager and adds its :meth:`__exit__` method to + Enters a new context manager and adds its :meth:`~object.__exit__` method to the callback stack. The return value is the result of the context - manager's own :meth:`__enter__` method. + manager's own :meth:`~object.__enter__` method. These context managers may suppress exceptions just as they normally would if used directly as part of a :keyword:`with` statement. @@ -549,18 +549,18 @@ Functions and classes provided: .. method:: push(exit) - Adds a context manager's :meth:`__exit__` method to the callback stack. + Adds a context manager's :meth:`~object.__exit__` method to the callback stack. As ``__enter__`` is *not* invoked, this method can be used to cover - part of an :meth:`__enter__` implementation with a context manager's own - :meth:`__exit__` method. + part of an :meth:`~object.__enter__` implementation with a context manager's own + :meth:`~object.__exit__` method. If passed an object that is not a context manager, this method assumes it is a callback with the same signature as a context manager's - :meth:`__exit__` method and adds it directly to the callback stack. + :meth:`~object.__exit__` method and adds it directly to the callback stack. By returning true values, these callbacks can suppress exceptions the - same way context manager :meth:`__exit__` methods can. + same way context manager :meth:`~object.__exit__` methods can. The passed in object is returned from the function, allowing this method to be used as a function decorator. @@ -707,7 +707,7 @@ Cleaning up in an ``__enter__`` implementation As noted in the documentation of :meth:`ExitStack.push`, this method can be useful in cleaning up an already allocated resource if later -steps in the :meth:`__enter__` implementation fail. +steps in the :meth:`~object.__enter__` implementation fail. Here's an example of doing this for a context manager that accepts resource acquisition and release functions, along with an optional validation function, @@ -864,7 +864,7 @@ And also as a function decorator:: Note that there is one additional limitation when using context managers as function decorators: there's no way to access the return value of -:meth:`__enter__`. If that value is needed, then it is still necessary to use +:meth:`~object.__enter__`. If that value is needed, then it is still necessary to use an explicit ``with`` statement. .. seealso:: diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index ad3135cbc146ed..fd3218a89c7cd2 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -4802,7 +4802,7 @@ before the statement body is executed and exited when the statement ends: The exception passed in should never be reraised explicitly - instead, this method should return a false value to indicate that the method completed successfully and does not want to suppress the raised exception. This allows - context management code to easily detect whether or not an :meth:`__exit__` + context management code to easily detect whether or not an :meth:`~object.__exit__` method has actually failed. Python defines several context managers to support easy thread synchronisation, diff --git a/Doc/library/test.rst b/Doc/library/test.rst index d3eb0ae00a08dc..aa67a9480ddfcb 100644 --- a/Doc/library/test.rst +++ b/Doc/library/test.rst @@ -1021,7 +1021,7 @@ The :mod:`test.support` module defines the following classes: :const:`resource.RLIMIT_CORE`'s soft limit to 0 to prevent coredump file creation. - On both platforms, the old value is restored by :meth:`__exit__`. + On both platforms, the old value is restored by :meth:`~object.__exit__`. .. class:: SaveSignals() diff --git a/Doc/library/unittest.mock.rst b/Doc/library/unittest.mock.rst index dee5c9e88a0a44..b7e03dfd642b39 100644 --- a/Doc/library/unittest.mock.rst +++ b/Doc/library/unittest.mock.rst @@ -2480,8 +2480,8 @@ are closed properly and is becoming common:: f.write('something') The issue is that even if you mock out the call to :func:`open` it is the -*returned object* that is used as a context manager (and has :meth:`__enter__` and -:meth:`__exit__` called). +*returned object* that is used as a context manager (and has :meth:`~object.__enter__` and +:meth:`~object.__exit__` called). Mocking context managers with a :class:`MagicMock` is common enough and fiddly enough that a helper function is useful. :: diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index 4efe30dbd25b83..fd3091105d2273 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -491,37 +491,37 @@ The execution of the :keyword:`with` statement with one "item" proceeds as follo #. The context expression (the expression given in the :token:`~python-grammar:with_item`) is evaluated to obtain a context manager. -#. The context manager's :meth:`__enter__` is loaded for later use. +#. The context manager's :meth:`~object.__enter__` is loaded for later use. -#. The context manager's :meth:`__exit__` is loaded for later use. +#. The context manager's :meth:`~object.__exit__` is loaded for later use. -#. The context manager's :meth:`__enter__` method is invoked. +#. The context manager's :meth:`~object.__enter__` method is invoked. #. If a target was included in the :keyword:`with` statement, the return value - from :meth:`__enter__` is assigned to it. + from :meth:`~object.__enter__` is assigned to it. .. note:: - The :keyword:`with` statement guarantees that if the :meth:`__enter__` - method returns without an error, then :meth:`__exit__` will always be + The :keyword:`with` statement guarantees that if the :meth:`~object.__enter__` + method returns without an error, then :meth:`~object.__exit__` will always be called. Thus, if an error occurs during the assignment to the target list, it will be treated the same as an error occurring within the suite would be. See step 7 below. #. The suite is executed. -#. The context manager's :meth:`__exit__` method is invoked. If an exception +#. The context manager's :meth:`~object.__exit__` method is invoked. If an exception caused the suite to be exited, its type, value, and traceback are passed as - arguments to :meth:`__exit__`. Otherwise, three :const:`None` arguments are + arguments to :meth:`~object.__exit__`. Otherwise, three :const:`None` arguments are supplied. If the suite was exited due to an exception, and the return value from the - :meth:`__exit__` method was false, the exception is reraised. If the return + :meth:`~object.__exit__` method was false, the exception is reraised. If the return value was true, the exception is suppressed, and execution continues with the statement following the :keyword:`with` statement. If the suite was exited for any reason other than an exception, the return - value from :meth:`__exit__` is ignored, and execution proceeds at the normal + value from :meth:`~object.__exit__` is ignored, and execution proceeds at the normal location for the kind of exit that was taken. The following code:: diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 27ac00216a01df..ce92a120609046 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2920,7 +2920,7 @@ For more information on context managers, see :ref:`typecontextmanager`. (i.e., prevent it from being propagated), it should return a true value. Otherwise, the exception will be processed normally upon exit from this method. - Note that :meth:`__exit__` methods should not reraise the passed-in exception; + Note that :meth:`~object.__exit__` methods should not reraise the passed-in exception; this is the caller's responsibility. @@ -3192,12 +3192,12 @@ Asynchronous context managers can be used in an :keyword:`async with` statement. .. method:: object.__aenter__(self) - Semantically similar to :meth:`__enter__`, the only + Semantically similar to :meth:`~object.__enter__`, the only difference being that it must return an *awaitable*. .. method:: object.__aexit__(self, exc_type, exc_value, traceback) - Semantically similar to :meth:`__exit__`, the only + Semantically similar to :meth:`~object.__exit__`, the only difference being that it must return an *awaitable*. An example of an asynchronous context manager class:: diff --git a/Doc/whatsnew/2.5.rst b/Doc/whatsnew/2.5.rst index f58b3ede27facc..ad0931ecbed060 100644 --- a/Doc/whatsnew/2.5.rst +++ b/Doc/whatsnew/2.5.rst @@ -575,15 +575,15 @@ structure is:: with-block The expression is evaluated, and it should result in an object that supports the -context management protocol (that is, has :meth:`__enter__` and :meth:`__exit__` +context management protocol (that is, has :meth:`~object.__enter__` and :meth:`~object.__exit__` methods. -The object's :meth:`__enter__` is called before *with-block* is executed and +The object's :meth:`~object.__enter__` is called before *with-block* is executed and therefore can run set-up code. It also may return a value that is bound to the name *variable*, if given. (Note carefully that *variable* is *not* assigned the result of *expression*.) -After execution of the *with-block* is finished, the object's :meth:`__exit__` +After execution of the *with-block* is finished, the object's :meth:`~object.__exit__` method is called, even if the block raised an exception, and can therefore run clean-up code. @@ -609,7 +609,7 @@ part-way through the block. .. note:: In this case, *f* is the same object created by :func:`open`, because - :meth:`file.__enter__` returns *self*. + :meth:`~object.__enter__` returns *self*. The :mod:`threading` module's locks and condition variables also support the ':keyword:`with`' statement:: @@ -652,10 +652,10 @@ underlying implementation and should keep reading. A high-level explanation of the context management protocol is: * The expression is evaluated and should result in an object called a "context - manager". The context manager must have :meth:`__enter__` and :meth:`__exit__` + manager". The context manager must have :meth:`~object.__enter__` and :meth:`~object.__exit__` methods. -* The context manager's :meth:`__enter__` method is called. The value returned +* The context manager's :meth:`~object.__enter__` method is called. The value returned is assigned to *VAR*. If no ``'as VAR'`` clause is present, the value is simply discarded. @@ -669,7 +669,7 @@ A high-level explanation of the context management protocol is: if you do the author of the code containing the ':keyword:`with`' statement will never realize anything went wrong. -* If *BLOCK* didn't raise an exception, the :meth:`__exit__` method is still +* If *BLOCK* didn't raise an exception, the :meth:`~object.__exit__` method is still called, but *type*, *value*, and *traceback* are all ``None``. Let's think through an example. I won't present detailed code but will only @@ -703,7 +703,7 @@ rolled back if there's an exception. Here's the basic interface for def rollback (self): "Rolls back current transaction" -The :meth:`__enter__` method is pretty easy, having only to start a new +The :meth:`~object.__enter__` method is pretty easy, having only to start a new transaction. For this application the resulting cursor object would be a useful result, so the method will return it. The user can then add ``as cursor`` to their ':keyword:`with`' statement to bind the cursor to a variable name. :: @@ -715,7 +715,7 @@ their ':keyword:`with`' statement to bind the cursor to a variable name. :: cursor = self.cursor() return cursor -The :meth:`__exit__` method is the most complicated because it's where most of +The :meth:`~object.__exit__` method is the most complicated because it's where most of the work has to be done. The method has to check if an exception occurred. If there was no exception, the transaction is committed. The transaction is rolled back if there was an exception. @@ -748,10 +748,10 @@ are useful for writing objects for use with the ':keyword:`with`' statement. The decorator is called :func:`contextmanager`, and lets you write a single generator function instead of defining a new class. The generator should yield exactly one value. The code up to the :keyword:`yield` will be executed as the -:meth:`__enter__` method, and the value yielded will be the method's return +:meth:`~object.__enter__` method, and the value yielded will be the method's return value that will get bound to the variable in the ':keyword:`with`' statement's :keyword:`!as` clause, if any. The code after the :keyword:`yield` will be -executed in the :meth:`__exit__` method. Any exception raised in the block will +executed in the :meth:`~object.__exit__` method. Any exception raised in the block will be raised by the :keyword:`!yield` statement. Our database example from the previous section could be written using this diff --git a/Doc/whatsnew/2.6.rst b/Doc/whatsnew/2.6.rst index 759a0757ebefa7..2b7ef2cd4fe4a9 100644 --- a/Doc/whatsnew/2.6.rst +++ b/Doc/whatsnew/2.6.rst @@ -269,15 +269,15 @@ structure is:: with-block The expression is evaluated, and it should result in an object that supports the -context management protocol (that is, has :meth:`__enter__` and :meth:`__exit__` +context management protocol (that is, has :meth:`~object.__enter__` and :meth:`~object.__exit__` methods). -The object's :meth:`__enter__` is called before *with-block* is executed and +The object's :meth:`~object.__enter__` is called before *with-block* is executed and therefore can run set-up code. It also may return a value that is bound to the name *variable*, if given. (Note carefully that *variable* is *not* assigned the result of *expression*.) -After execution of the *with-block* is finished, the object's :meth:`__exit__` +After execution of the *with-block* is finished, the object's :meth:`~object.__exit__` method is called, even if the block raised an exception, and can therefore run clean-up code. @@ -296,7 +296,7 @@ part-way through the block. .. note:: In this case, *f* is the same object created by :func:`open`, because - :meth:`file.__enter__` returns *self*. + :meth:`~object.__enter__` returns *self*. The :mod:`threading` module's locks and condition variables also support the ':keyword:`with`' statement:: @@ -339,16 +339,16 @@ underlying implementation and should keep reading. A high-level explanation of the context management protocol is: * The expression is evaluated and should result in an object called a "context - manager". The context manager must have :meth:`__enter__` and :meth:`__exit__` + manager". The context manager must have :meth:`~object.__enter__` and :meth:`~object.__exit__` methods. -* The context manager's :meth:`__enter__` method is called. The value returned +* The context manager's :meth:`~object.__enter__` method is called. The value returned is assigned to *VAR*. If no ``as VAR`` clause is present, the value is simply discarded. * The code in *BLOCK* is executed. -* If *BLOCK* raises an exception, the context manager's :meth:`__exit__` method +* If *BLOCK* raises an exception, the context manager's :meth:`~object.__exit__` method is called with three arguments, the exception details (``type, value, traceback``, the same values returned by :func:`sys.exc_info`, which can also be ``None`` if no exception occurred). The method's return value controls whether an exception @@ -357,7 +357,7 @@ A high-level explanation of the context management protocol is: if you do the author of the code containing the ':keyword:`with`' statement will never realize anything went wrong. -* If *BLOCK* didn't raise an exception, the :meth:`__exit__` method is still +* If *BLOCK* didn't raise an exception, the :meth:`~object.__exit__` method is still called, but *type*, *value*, and *traceback* are all ``None``. Let's think through an example. I won't present detailed code but will only @@ -391,7 +391,7 @@ rolled back if there's an exception. Here's the basic interface for def rollback(self): "Rolls back current transaction" -The :meth:`__enter__` method is pretty easy, having only to start a new +The :meth:`~object.__enter__` method is pretty easy, having only to start a new transaction. For this application the resulting cursor object would be a useful result, so the method will return it. The user can then add ``as cursor`` to their ':keyword:`with`' statement to bind the cursor to a variable name. :: @@ -403,7 +403,7 @@ their ':keyword:`with`' statement to bind the cursor to a variable name. :: cursor = self.cursor() return cursor -The :meth:`__exit__` method is the most complicated because it's where most of +The :meth:`~object.__exit__` method is the most complicated because it's where most of the work has to be done. The method has to check if an exception occurred. If there was no exception, the transaction is committed. The transaction is rolled back if there was an exception. @@ -436,10 +436,10 @@ are useful when writing objects for use with the ':keyword:`with`' statement. The decorator is called :func:`contextmanager`, and lets you write a single generator function instead of defining a new class. The generator should yield exactly one value. The code up to the :keyword:`yield` will be executed as the -:meth:`__enter__` method, and the value yielded will be the method's return +:meth:`~object.__enter__` method, and the value yielded will be the method's return value that will get bound to the variable in the ':keyword:`with`' statement's :keyword:`!as` clause, if any. The code after the :keyword:`!yield` will be -executed in the :meth:`__exit__` method. Any exception raised in the block will +executed in the :meth:`~object.__exit__` method. Any exception raised in the block will be raised by the :keyword:`!yield` statement. Using this decorator, our database example from the previous section @@ -1737,7 +1737,7 @@ Optimizations (Contributed by Antoine Pitrou.) Memory usage is reduced by using pymalloc for the Unicode string's data. -* The ``with`` statement now stores the :meth:`__exit__` method on the stack, +* The ``with`` statement now stores the :meth:`~object.__exit__` method on the stack, producing a small speedup. (Implemented by Jeffrey Yasskin.) * To reduce memory usage, the garbage collector will now clear internal diff --git a/Doc/whatsnew/2.7.rst b/Doc/whatsnew/2.7.rst index caf28ff50b3aa2..5979d77da5d6b6 100644 --- a/Doc/whatsnew/2.7.rst +++ b/Doc/whatsnew/2.7.rst @@ -930,8 +930,8 @@ Optimizations Several performance enhancements have been added: * A new opcode was added to perform the initial setup for - :keyword:`with` statements, looking up the :meth:`__enter__` and - :meth:`__exit__` methods. (Contributed by Benjamin Peterson.) + :keyword:`with` statements, looking up the :meth:`~object.__enter__` and + :meth:`~object.__exit__` methods. (Contributed by Benjamin Peterson.) * The garbage collector now performs better for one common usage pattern: when many objects are being allocated without deallocating @@ -2448,13 +2448,13 @@ that may require changes to your code: (Changed by Eric Smith; :issue:`5920`.) * Because of an optimization for the :keyword:`with` statement, the special - methods :meth:`__enter__` and :meth:`__exit__` must belong to the object's + methods :meth:`~object.__enter__` and :meth:`~object.__exit__` must belong to the object's type, and cannot be directly attached to the object's instance. This affects new-style classes (derived from :class:`object`) and C extension types. (:issue:`6101`.) * Due to a bug in Python 2.6, the *exc_value* parameter to - :meth:`__exit__` methods was often the string representation of the + :meth:`~object.__exit__` methods was often the string representation of the exception, not an instance. This was fixed in 2.7, so *exc_value* will be an instance as expected. (Fixed by Florent Xicluna; :issue:`7853`.) diff --git a/Misc/NEWS.d/3.10.0a7.rst b/Misc/NEWS.d/3.10.0a7.rst index 3a1694f444616a..d9cdfbd04c88d4 100644 --- a/Misc/NEWS.d/3.10.0a7.rst +++ b/Misc/NEWS.d/3.10.0a7.rst @@ -274,7 +274,7 @@ Co-authored-by: Tim Peters Only handle asynchronous exceptions and requests to drop the GIL when returning from a call or on the back edges of loops. Makes sure that -:meth:`__exit__` is always called in with statements, even for interrupts. +:meth:`~object.__exit__` is always called in with statements, even for interrupts. .. From 2b3f9a5a1dc6a4e335bdcd4454b298007f7fad4a Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 19 Oct 2023 17:35:07 +0200 Subject: [PATCH 503/632] [3.11] gh-109510: Clearly explain "Which Docstrings Are Examined" (GH-109696) (#111078) Co-authored-by: Unique-Usman <86585626+Unique-Usman@users.noreply.github.com> Co-authored-by: Mariatta Co-authored-by: Jacob Coffee Co-authored-by: Hugo van Kemenade Co-authored-by: C.A.M. Gerlach --- Doc/library/doctest.rst | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/Doc/library/doctest.rst b/Doc/library/doctest.rst index c106d5a3383a5e..d00c4c88d57176 100644 --- a/Doc/library/doctest.rst +++ b/Doc/library/doctest.rst @@ -277,13 +277,34 @@ Which Docstrings Are Examined? The module docstring, and all function, class and method docstrings are searched. Objects imported into the module are not searched. -In addition, if ``M.__test__`` exists and "is true", it must be a dict, and each +In addition, there are cases when you want tests to be part of a module but not part +of the help text, which requires that the tests not be included in the docstring. +Doctest looks for a module-level variable called ``__test__`` and uses it to locate other +tests. If ``M.__test__`` exists and is truthy, it must be a dict, and each entry maps a (string) name to a function object, class object, or string. Function and class object docstrings found from ``M.__test__`` are searched, and strings are treated as if they were docstrings. In output, a key ``K`` in -``M.__test__`` appears with name :: +``M.__test__`` appears with name ``M.__test__.K``. - .__test__.K +For example, place this block of code at the top of :file:`example.py`: + +.. code-block:: python + + __test__ = { + 'numbers': """ + >>> factorial(6) + 720 + + >>> [factorial(n) for n in range(6)] + [1, 1, 2, 6, 24, 120] + """ + } + +The value of ``example.__test__["numbers"]`` will be treated as a +docstring and all the tests inside it will be run. It is +important to note that the value can be mapped to a function, +class object, or module; if so, :mod:`!doctest` +searches them recursively for docstrings, which are then scanned for tests. Any classes found are recursively searched similarly, to test docstrings in their contained methods and nested classes. From 69f0c900114d32023d663b7448417e7a252e80eb Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 20 Oct 2023 06:25:17 +0200 Subject: [PATCH 504/632] [3.11] gh-111092: Make turtledemo run without default root enabled (GH-111093) (#111096) gh-111092: Make turtledemo run without default root enabled (GH-111093) Add missing 'root' argument to PanedWindow call. Other root children already have it. (cherry picked from commit b802882fb2bff8b431df661322908c07491f3ce7) Co-authored-by: Terry Jan Reedy --- Lib/turtledemo/__main__.py | 2 +- .../next/Library/2023-10-19-22-46-34.gh-issue-111092.hgut12.rst | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2023-10-19-22-46-34.gh-issue-111092.hgut12.rst diff --git a/Lib/turtledemo/__main__.py b/Lib/turtledemo/__main__.py index f6c9d6aa6f9a32..2ab6c15e2c079e 100755 --- a/Lib/turtledemo/__main__.py +++ b/Lib/turtledemo/__main__.py @@ -161,7 +161,7 @@ def __init__(self, filename=None): label='Help', underline=0) root['menu'] = self.mBar - pane = PanedWindow(orient=HORIZONTAL, sashwidth=5, + pane = PanedWindow(root, orient=HORIZONTAL, sashwidth=5, sashrelief=SOLID, bg='#ddd') pane.add(self.makeTextFrame(pane)) pane.add(self.makeGraphFrame(pane)) 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 new file mode 100644 index 00000000000000..487bd177d27e31 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-10-19-22-46-34.gh-issue-111092.hgut12.rst @@ -0,0 +1 @@ +Make turtledemo run without default root enabled. From f1dbde0d3a448e623bd8c4c76139284174e0f90d Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 20 Oct 2023 06:50:17 +0200 Subject: [PATCH 505/632] [3.11] gh-101100: Fix Sphinx warnings in `library/tty.rst` (GH-111079) (#111098) gh-101100: Fix Sphinx warnings in `library/tty.rst` (GH-111079) Fix Sphinx warnings in library/tty.rst (cherry picked from commit c42c68aa7bd19b0de7f2132ed468bc4ce83d8aa9) Co-authored-by: Hugo van Kemenade --- Doc/library/termios.rst | 18 ++++++++++++++---- Doc/tools/.nitignore | 1 - 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Doc/library/termios.rst b/Doc/library/termios.rst index fb1ff567d49e5c..03806178e9d3fb 100644 --- a/Doc/library/termios.rst +++ b/Doc/library/termios.rst @@ -43,10 +43,20 @@ The module defines the following functions: Set the tty attributes for file descriptor *fd* from the *attributes*, which is a list like the one returned by :func:`tcgetattr`. The *when* argument - determines when the attributes are changed: :const:`TCSANOW` to change - immediately, :const:`TCSADRAIN` to change after transmitting all queued output, - or :const:`TCSAFLUSH` to change after transmitting all queued output and - discarding all queued input. + determines when the attributes are changed: + + .. data:: TCSANOW + + Change attributes immediately. + + .. data:: TCSADRAIN + + Change attributes after transmitting all queued output. + + .. data:: TCSAFLUSH + + Change attributes after transmitting all queued output and + discarding all queued input. .. function:: tcsendbreak(fd, duration) diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index b47924f25348fa..b49fce00cf4886 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -124,7 +124,6 @@ Doc/library/tkinter.rst Doc/library/tkinter.scrolledtext.rst Doc/library/tkinter.ttk.rst Doc/library/traceback.rst -Doc/library/tty.rst Doc/library/unittest.mock.rst Doc/library/unittest.rst Doc/library/urllib.parse.rst From 6df935c2128f59c327fe29ff8c114a2bc644471a Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 20 Oct 2023 10:09:23 +0200 Subject: [PATCH 506/632] [3.11] gh-101100: Fix sphinx warnings in `library/codecs.rst` (GH-110979) (#111071) Co-authored-by: Nikita Sobolev Co-authored-by: Hugo van Kemenade --- Doc/library/codecs.rst | 71 ++++++++++++++++++++++-------------------- Doc/tools/.nitignore | 1 - 2 files changed, 38 insertions(+), 34 deletions(-) diff --git a/Doc/library/codecs.rst b/Doc/library/codecs.rst index 2db4a67d1973d5..9ce584874783da 100644 --- a/Doc/library/codecs.rst +++ b/Doc/library/codecs.rst @@ -520,44 +520,46 @@ The base :class:`Codec` class defines these methods which also define the function interfaces of the stateless encoder and decoder: -.. method:: Codec.encode(input, errors='strict') +.. class:: Codec - Encodes the object *input* and returns a tuple (output object, length consumed). - For instance, :term:`text encoding` converts - a string object to a bytes object using a particular - character set encoding (e.g., ``cp1252`` or ``iso-8859-1``). + .. method:: encode(input, errors='strict') - The *errors* argument defines the error handling to apply. - It defaults to ``'strict'`` handling. + Encodes the object *input* and returns a tuple (output object, length consumed). + For instance, :term:`text encoding` converts + a string object to a bytes object using a particular + character set encoding (e.g., ``cp1252`` or ``iso-8859-1``). - The method may not store state in the :class:`Codec` instance. Use - :class:`StreamWriter` for codecs which have to keep state in order to make - encoding efficient. + The *errors* argument defines the error handling to apply. + It defaults to ``'strict'`` handling. - The encoder must be able to handle zero length input and return an empty object - of the output object type in this situation. + The method may not store state in the :class:`Codec` instance. Use + :class:`StreamWriter` for codecs which have to keep state in order to make + encoding efficient. + The encoder must be able to handle zero length input and return an empty object + of the output object type in this situation. -.. method:: Codec.decode(input, errors='strict') - Decodes the object *input* and returns a tuple (output object, length - consumed). For instance, for a :term:`text encoding`, decoding converts - a bytes object encoded using a particular - character set encoding to a string object. + .. method:: decode(input, errors='strict') - For text encodings and bytes-to-bytes codecs, - *input* must be a bytes object or one which provides the read-only - buffer interface -- for example, buffer objects and memory mapped files. + Decodes the object *input* and returns a tuple (output object, length + consumed). For instance, for a :term:`text encoding`, decoding converts + a bytes object encoded using a particular + character set encoding to a string object. - The *errors* argument defines the error handling to apply. - It defaults to ``'strict'`` handling. + For text encodings and bytes-to-bytes codecs, + *input* must be a bytes object or one which provides the read-only + buffer interface -- for example, buffer objects and memory mapped files. - The method may not store state in the :class:`Codec` instance. Use - :class:`StreamReader` for codecs which have to keep state in order to make - decoding efficient. + The *errors* argument defines the error handling to apply. + It defaults to ``'strict'`` handling. - The decoder must be able to handle zero length input and return an empty object - of the output object type in this situation. + The method may not store state in the :class:`Codec` instance. Use + :class:`StreamReader` for codecs which have to keep state in order to make + decoding efficient. + + The decoder must be able to handle zero length input and return an empty object + of the output object type in this situation. Incremental Encoding and Decoding @@ -705,7 +707,7 @@ Stream Encoding and Decoding The :class:`StreamWriter` and :class:`StreamReader` classes provide generic working interfaces which can be used to implement new encoding submodules very -easily. See :mod:`encodings.utf_8` for an example of how this is done. +easily. See :mod:`!encodings.utf_8` for an example of how this is done. .. _stream-writer-objects: @@ -895,9 +897,10 @@ The design is such that one can use the factory functions returned by the .. class:: StreamRecoder(stream, encode, decode, Reader, Writer, errors='strict') Creates a :class:`StreamRecoder` instance which implements a two-way conversion: - *encode* and *decode* work on the frontend — the data visible to - code calling :meth:`read` and :meth:`write`, while *Reader* and *Writer* - work on the backend — the data in *stream*. + *encode* and *decode* work on the frontend — the data visible to + code calling :meth:`~StreamReader.read` and :meth:`~StreamWriter.write`, + while *Reader* and *Writer* + work on the backend — the data in *stream*. You can use these objects to do transparent transcodings, e.g., from Latin-1 to UTF-8 and back. @@ -1417,8 +1420,10 @@ to :class:`bytes` mappings. They are not supported by :meth:`bytes.decode` | | quotedprintable, | quoted printable. | ``quotetabs=True`` / | | | quoted_printable | | :meth:`quopri.decode` | +----------------------+------------------+------------------------------+------------------------------+ -| uu_codec | uu | Convert the operand using | :meth:`uu.encode` / | -| | | uuencode. | :meth:`uu.decode` | +| uu_codec | uu | Convert the operand using | :meth:`!uu.encode` / | +| | | uuencode. | :meth:`!uu.decode` | +| | | | (Note: :mod:`uu` is | +| | | | deprecated.) | +----------------------+------------------+------------------------------+------------------------------+ | zlib_codec | zip, zlib | Compress the operand using | :meth:`zlib.compress` / | | | | gzip. | :meth:`zlib.decompress` | diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index b49fce00cf4886..d42292aed04ea4 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -43,7 +43,6 @@ Doc/library/bisect.rst Doc/library/bz2.rst Doc/library/calendar.rst Doc/library/cmd.rst -Doc/library/codecs.rst Doc/library/collections.abc.rst Doc/library/collections.rst Doc/library/concurrent.futures.rst From 69bcaf7e0e68f5bdead833a5aa47f67f256c6440 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 20 Oct 2023 14:19:04 +0200 Subject: [PATCH 507/632] gh-110913: Fix WindowsConsoleIO chunking of UTF-8 text (GH-111007) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 11312eae6ec3acf51aacafce4cb6d1a5edfd5f2e) Co-authored-by: Tamás Hegedűs --- ...-10-19-21-46-18.gh-issue-110913.CWlPfg.rst | 1 + Modules/_io/winconsoleio.c | 36 ++++++++++--------- 2 files changed, 21 insertions(+), 16 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2023-10-19-21-46-18.gh-issue-110913.CWlPfg.rst 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 new file mode 100644 index 00000000000000..d4c1b56d98ef0e --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-10-19-21-46-18.gh-issue-110913.CWlPfg.rst @@ -0,0 +1 @@ +WindowsConsoleIO now correctly chunks large buffers without splitting up UTF-8 sequences. diff --git a/Modules/_io/winconsoleio.c b/Modules/_io/winconsoleio.c index 89431b1e4c3c7b..dcb7d32e78c26f 100644 --- a/Modules/_io/winconsoleio.c +++ b/Modules/_io/winconsoleio.c @@ -132,6 +132,23 @@ char _PyIO_get_console_type(PyObject *path_or_fd) { return m; } +static DWORD +_find_last_utf8_boundary(const char *buf, DWORD len) +{ + /* This function never returns 0, returns the original len instead */ + DWORD count = 1; + if (len == 0 || (buf[len - 1] & 0x80) == 0) { + return len; + } + for (;; count++) { + if (count > 3 || count >= len) { + return len; + } + if ((buf[len - count] & 0xc0) != 0x80) { + return len - count; + } + } +} /*[clinic input] module _io @@ -954,7 +971,7 @@ _io__WindowsConsoleIO_write_impl(winconsoleio *self, Py_buffer *b) { BOOL res = TRUE; wchar_t *wbuf; - DWORD len, wlen, orig_len, n = 0; + DWORD len, wlen, n = 0; HANDLE handle; if (self->fd == -1) @@ -984,21 +1001,8 @@ _io__WindowsConsoleIO_write_impl(winconsoleio *self, Py_buffer *b) have to reduce and recalculate. */ while (wlen > 32766 / sizeof(wchar_t)) { len /= 2; - orig_len = len; - /* Reduce the length until we hit the final byte of a UTF-8 sequence - * (top bit is unset). Fix for github issue 82052. - */ - while (len > 0 && (((char *)b->buf)[len-1] & 0x80) != 0) - --len; - /* If we hit a length of 0, something has gone wrong. This shouldn't - * be possible, as valid UTF-8 can have at most 3 non-final bytes - * before a final one, and our buffer is way longer than that. - * But to be on the safe side, if we hit this issue we just restore - * the original length and let the console API sort it out. - */ - if (len == 0) { - len = orig_len; - } + /* Fix for github issues gh-110913 and gh-82052. */ + len = _find_last_utf8_boundary(b->buf, len); wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len, NULL, 0); } Py_END_ALLOW_THREADS From 7213fc248db1c2ae4ee122a3fde69105171428f2 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 20 Oct 2023 16:33:22 +0200 Subject: [PATCH 508/632] [3.11] Synchronize test_contextlib with test_contextlib_async (GH-111000) (GH-111115) (cherry picked from commit ff4e53cb747063e95eaec181fd396f062f885ac2) Co-authored-by: Serhiy Storchaka --- Lib/test/test_contextlib.py | 46 +++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/Lib/test/test_contextlib.py b/Lib/test/test_contextlib.py index 093f2593a4a464..0a79b3617defe8 100644 --- a/Lib/test/test_contextlib.py +++ b/Lib/test/test_contextlib.py @@ -162,6 +162,15 @@ def whoo(): # The "gen" attribute is an implementation detail. self.assertFalse(ctx.gen.gi_suspended) + def test_contextmanager_trap_no_yield(self): + @contextmanager + def whoo(): + if False: + yield + ctx = whoo() + with self.assertRaises(RuntimeError): + ctx.__enter__() + def test_contextmanager_trap_second_yield(self): @contextmanager def whoo(): @@ -175,6 +184,19 @@ def whoo(): # The "gen" attribute is an implementation detail. self.assertFalse(ctx.gen.gi_suspended) + def test_contextmanager_non_normalised(self): + @contextmanager + def whoo(): + try: + yield + except RuntimeError: + raise SyntaxError + + ctx = whoo() + ctx.__enter__() + with self.assertRaises(SyntaxError): + ctx.__exit__(RuntimeError, None, None) + def test_contextmanager_except(self): state = [] @contextmanager @@ -254,6 +276,25 @@ def test_issue29692(): self.assertEqual(ex.args[0], 'issue29692:Unchained') self.assertIsNone(ex.__cause__) + def test_contextmanager_wrap_runtimeerror(self): + @contextmanager + def woohoo(): + try: + yield + except Exception as exc: + raise RuntimeError(f'caught {exc}') from exc + + with self.assertRaises(RuntimeError): + with woohoo(): + 1 / 0 + + # If the context manager wrapped StopIteration in a RuntimeError, + # we also unwrap it, because we can't tell whether the wrapping was + # done by the generator machinery or by the generator itself. + with self.assertRaises(StopIteration): + with woohoo(): + raise StopIteration + def _create_contextmanager_attribs(self): def attribs(**kw): def decorate(func): @@ -265,6 +306,7 @@ def decorate(func): @attribs(foo='bar') def baz(spam): """Whee!""" + yield return baz def test_contextmanager_attribs(self): @@ -321,8 +363,11 @@ def woohoo(a, *, b): def test_recursive(self): depth = 0 + ncols = 0 @contextmanager def woohoo(): + nonlocal ncols + ncols += 1 nonlocal depth before = depth depth += 1 @@ -336,6 +381,7 @@ def recursive(): recursive() recursive() + self.assertEqual(ncols, 10) self.assertEqual(depth, 0) From 9addf2cf117c9b14bbc0c7640bb1f1d6300673b7 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 20 Oct 2023 20:28:41 +0200 Subject: [PATCH 509/632] [3.11] gh-111126: Use `isinstance` instead of `assert[Not]IsInstance` in `test_typing` (GH-111127) (#111131) gh-111126: Use `isinstance` instead of `assert[Not]IsInstance` in `test_typing` (GH-111127) (cherry picked from commit ea7c26e4b89c71234c4a603567a93f0a44c9cc97) Co-authored-by: Nikita Sobolev --- Lib/test/test_typing.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 177155a6365662..fa279b557de2c6 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -1944,13 +1944,13 @@ def test_callable_instance_type_error(self): def f(): pass with self.assertRaises(TypeError): - self.assertIsInstance(f, Callable[[], None]) + isinstance(f, Callable[[], None]) with self.assertRaises(TypeError): - self.assertIsInstance(f, Callable[[], Any]) + isinstance(f, Callable[[], Any]) with self.assertRaises(TypeError): - self.assertNotIsInstance(None, Callable[[], None]) + isinstance(None, Callable[[], None]) with self.assertRaises(TypeError): - self.assertNotIsInstance(None, Callable[[], Any]) + isinstance(None, Callable[[], Any]) def test_repr(self): Callable = self.Callable From 47670fbdd0b6c42a0d026f26c67178371b86069a Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 21 Oct 2023 11:01:10 +0200 Subject: [PATCH 510/632] [3.11] gh-110932: Fix regrtest for SOURCE_DATE_EPOCH (GH-111143) (#111153) gh-110932: Fix regrtest for SOURCE_DATE_EPOCH (GH-111143) If the SOURCE_DATE_EPOCH environment variable is defined, use its value as the random seed. (cherry picked from commit 7237fb578dc9db9dc557759a24d8083425107b91) Co-authored-by: Victor Stinner --- Lib/test/libregrtest/main.py | 21 +++--- Lib/test/libregrtest/runtests.py | 2 +- Lib/test/test_regrtest.py | 72 +++++++++++++++---- ...-10-21-00-10-36.gh-issue-110932.jktjJU.rst | 2 + 4 files changed, 75 insertions(+), 22 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2023-10-21-00-10-36.gh-issue-110932.jktjJU.rst diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index fe35df05c80e89..e765ed5c613acb 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -129,14 +129,19 @@ def __init__(self, ns: Namespace, _add_python_opts: bool = False): # Randomize self.randomize: bool = ns.randomize - self.random_seed: int | None = ( - ns.random_seed - if ns.random_seed is not None - else random.getrandbits(32) - ) - if 'SOURCE_DATE_EPOCH' in os.environ: + if ('SOURCE_DATE_EPOCH' in os.environ + # don't use the variable if empty + and os.environ['SOURCE_DATE_EPOCH'] + ): self.randomize = False - self.random_seed = None + # SOURCE_DATE_EPOCH should be an integer, but use a string to not + # fail if it's not integer. random.seed() accepts a string. + # https://reproducible-builds.org/docs/source-date-epoch/ + self.random_seed: int | str = os.environ['SOURCE_DATE_EPOCH'] + elif ns.random_seed is None: + self.random_seed = random.getrandbits(32) + else: + self.random_seed = ns.random_seed # tests self.first_runtests: RunTests | None = None @@ -441,7 +446,7 @@ def _run_tests(self, selected: TestTuple, tests: TestList | None) -> int: or tests or self.cmdline_args)): display_header(self.use_resources, self.python_cmd) - print("Using random seed", self.random_seed) + print("Using random seed:", self.random_seed) runtests = self.create_run_tests(selected) self.first_runtests = runtests diff --git a/Lib/test/libregrtest/runtests.py b/Lib/test/libregrtest/runtests.py index 4da312db4cb02e..893b311c31297c 100644 --- a/Lib/test/libregrtest/runtests.py +++ b/Lib/test/libregrtest/runtests.py @@ -91,7 +91,7 @@ class RunTests: use_resources: tuple[str, ...] python_cmd: tuple[str, ...] | None randomize: bool - random_seed: int | None + random_seed: int | str json_file: JsonFile | None def copy(self, **override): diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py index c8e182397c835e..91f2fb0286adfe 100644 --- a/Lib/test/test_regrtest.py +++ b/Lib/test/test_regrtest.py @@ -143,18 +143,26 @@ def test_header(self): self.assertTrue(ns.header) def test_randomize(self): - for opt in '-r', '--randomize': + for opt in ('-r', '--randomize'): with self.subTest(opt=opt): ns = self.parse_args([opt]) self.assertTrue(ns.randomize) with os_helper.EnvironmentVarGuard() as env: - env['SOURCE_DATE_EPOCH'] = '1' - + # with SOURCE_DATE_EPOCH + env['SOURCE_DATE_EPOCH'] = '1697839080' ns = self.parse_args(['--randomize']) regrtest = main.Regrtest(ns) self.assertFalse(regrtest.randomize) - self.assertIsNone(regrtest.random_seed) + self.assertIsInstance(regrtest.random_seed, str) + self.assertEqual(regrtest.random_seed, '1697839080') + + # without SOURCE_DATE_EPOCH + del env['SOURCE_DATE_EPOCH'] + ns = self.parse_args(['--randomize']) + regrtest = main.Regrtest(ns) + self.assertTrue(regrtest.randomize) + self.assertIsInstance(regrtest.random_seed, int) def test_randseed(self): ns = self.parse_args(['--randseed', '12345']) @@ -388,7 +396,13 @@ def check_ci_mode(self, args, use_resources, rerun=True): # Check Regrtest attributes which are more reliable than Namespace # which has an unclear API - regrtest = main.Regrtest(ns) + with os_helper.EnvironmentVarGuard() as env: + # Ignore SOURCE_DATE_EPOCH env var if it's set + if 'SOURCE_DATE_EPOCH' in env: + del env['SOURCE_DATE_EPOCH'] + + regrtest = main.Regrtest(ns) + self.assertEqual(regrtest.num_workers, -1) self.assertEqual(regrtest.want_rerun, rerun) self.assertTrue(regrtest.randomize) @@ -662,21 +676,26 @@ def list_regex(line_format, tests): state = f'{state} then {new_state}' self.check_line(output, f'Result: {state}', full=True) - def parse_random_seed(self, output): - match = self.regex_search(r'Using random seed ([0-9]+)', output) - randseed = int(match.group(1)) - self.assertTrue(0 <= randseed, randseed) - return randseed + def parse_random_seed(self, output: str) -> str: + match = self.regex_search(r'Using random seed: (.*)', output) + return match.group(1) def run_command(self, args, input=None, exitcode=0, **kw): if not input: input = '' if 'stderr' not in kw: kw['stderr'] = subprocess.STDOUT + + env = kw.pop('env', None) + if env is None: + env = dict(os.environ) + env.pop('SOURCE_DATE_EPOCH', None) + proc = subprocess.run(args, text=True, input=input, stdout=subprocess.PIPE, + env=env, **kw) if proc.returncode != exitcode: msg = ("Command %s failed with exit code %s, but exit code %s expected!\n" @@ -752,7 +771,9 @@ def setUp(self): self.regrtest_args.append('-n') def check_output(self, output): - self.parse_random_seed(output) + randseed = self.parse_random_seed(output) + self.assertTrue(randseed.isdigit(), randseed) + self.check_executed_tests(output, self.tests, randomize=True, stats=len(self.tests)) @@ -943,7 +964,7 @@ def test_random(self): test_random = int(match.group(1)) # try to reproduce with the random seed - output = self.run_tests('-r', '--randseed=%s' % randseed, test, + output = self.run_tests('-r', f'--randseed={randseed}', test, exitcode=EXITCODE_NO_TESTS_RAN) randseed2 = self.parse_random_seed(output) self.assertEqual(randseed2, randseed) @@ -954,7 +975,32 @@ def test_random(self): # check that random.seed is used by default output = self.run_tests(test, exitcode=EXITCODE_NO_TESTS_RAN) - self.assertIsInstance(self.parse_random_seed(output), int) + randseed = self.parse_random_seed(output) + self.assertTrue(randseed.isdigit(), randseed) + + # check SOURCE_DATE_EPOCH (integer) + timestamp = '1697839080' + env = dict(os.environ, SOURCE_DATE_EPOCH=timestamp) + output = self.run_tests('-r', test, exitcode=EXITCODE_NO_TESTS_RAN, + env=env) + randseed = self.parse_random_seed(output) + self.assertEqual(randseed, timestamp) + self.check_line(output, 'TESTRANDOM: 520') + + # check SOURCE_DATE_EPOCH (string) + env = dict(os.environ, SOURCE_DATE_EPOCH='XYZ') + output = self.run_tests('-r', test, exitcode=EXITCODE_NO_TESTS_RAN, + env=env) + randseed = self.parse_random_seed(output) + self.assertEqual(randseed, 'XYZ') + self.check_line(output, 'TESTRANDOM: 22') + + # check SOURCE_DATE_EPOCH (empty string): ignore the env var + env = dict(os.environ, SOURCE_DATE_EPOCH='') + output = self.run_tests('-r', test, exitcode=EXITCODE_NO_TESTS_RAN, + env=env) + randseed = self.parse_random_seed(output) + self.assertTrue(randseed.isdigit(), randseed) def test_fromfile(self): # test --fromfile 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 new file mode 100644 index 00000000000000..45bb0774a9abe3 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-10-21-00-10-36.gh-issue-110932.jktjJU.rst @@ -0,0 +1,2 @@ +Fix regrtest if the ``SOURCE_DATE_EPOCH`` environment variable is defined: +use the variable value as the random seed. Patch by Victor Stinner. From 17b8e35b65315efbb859100c9d0393eed338a815 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 21 Oct 2023 19:06:51 +0200 Subject: [PATCH 511/632] [3.11] gh-111157: Mention `__notes__` in `traceback.format_exception_only` docstring (GH-111158) (#111164) gh-111157: Mention `__notes__` in `traceback.format_exception_only` docstring (GH-111158) (cherry picked from commit 5e7727b05232b43589d177c15263d7f4f8c584a0) Co-authored-by: Nikita Sobolev --- Lib/traceback.py | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/Lib/traceback.py b/Lib/traceback.py index d3edd3a63efed9..0e229553cb5d25 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -145,14 +145,11 @@ def format_exception_only(exc, /, value=_sentinel): The return value is a list of strings, each ending in a newline. - Normally, the list contains a single string; however, for - SyntaxError exceptions, it contains several lines that (when - printed) display detailed information about where the syntax - error occurred. - - The message indicating which exception occurred is always the last - string in the list. - + The list contains the exception's message, which is + normally a single string; however, for :exc:`SyntaxError` exceptions, it + contains several lines that (when printed) display detailed information + about where the syntax error occurred. Following the message, the list + contains the exception's ``__notes__``. """ if value is _sentinel: value = exc @@ -817,13 +814,13 @@ def format_exception_only(self): The return value is a generator of strings, each ending in a newline. - Normally, the generator emits a single string; however, for - SyntaxError exceptions, it emits several lines that (when - printed) display detailed information about where the syntax - error occurred. - - The message indicating which exception occurred is always the last - string in the output. + Generator yields the exception message. + For :exc:`SyntaxError` exceptions, it + also yields (before the exception message) + several lines that (when printed) + display detailed information about where the syntax error occurred. + Following the message, generator also yields + all the exception's ``__notes__``. """ if self.exc_type is None: yield _format_final_exc_line(None, self._str) From 4222dd93af710ba9f427d4f5d95a9241298ff0db Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 21 Oct 2023 20:30:19 +0300 Subject: [PATCH 512/632] [3.11] gh-110918: regrtest: allow to intermix --match and --ignore options (GH-110919) (GH-111168) 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. (cherry picked from commit 9a1fe09622cd0f1e24c2ba5335c94c5d70306fd0) --- Lib/test/libregrtest/cmdline.py | 40 +++++----- Lib/test/libregrtest/findtests.py | 7 +- Lib/test/libregrtest/main.py | 15 +--- Lib/test/libregrtest/run_workers.py | 2 +- Lib/test/libregrtest/runtests.py | 5 +- Lib/test/libregrtest/setup.py | 2 +- Lib/test/libregrtest/utils.py | 1 + Lib/test/libregrtest/worker.py | 6 +- Lib/test/support/__init__.py | 76 +++++++------------ Lib/test/test_regrtest.py | 42 +++++----- Lib/test/test_support.py | 67 +++++++++------- ...-10-16-13-47-24.gh-issue-110918.aFgZK3.rst | 4 + 12 files changed, 126 insertions(+), 141 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2023-10-16-13-47-24.gh-issue-110918.aFgZK3.rst diff --git a/Lib/test/libregrtest/cmdline.py b/Lib/test/libregrtest/cmdline.py index b1b00893402d8e..905b3f862281f1 100644 --- a/Lib/test/libregrtest/cmdline.py +++ b/Lib/test/libregrtest/cmdline.py @@ -161,8 +161,7 @@ def __init__(self, **kwargs) -> None: self.forever = False self.header = False self.failfast = False - self.match_tests = None - self.ignore_tests = None + self.match_tests = [] self.pgo = False self.pgo_extended = False self.worker_json = None @@ -183,6 +182,20 @@ def error(self, message): super().error(message + "\nPass -h or --help for complete help.") +class FilterAction(argparse.Action): + def __call__(self, parser, namespace, value, option_string=None): + items = getattr(namespace, self.dest) + items.append((value, self.const)) + + +class FromFileFilterAction(argparse.Action): + def __call__(self, parser, namespace, value, option_string=None): + items = getattr(namespace, self.dest) + with open(value, encoding='utf-8') as fp: + for line in fp: + items.append((line.strip(), self.const)) + + def _create_parser(): # Set prog to prevent the uninformative "__main__.py" from displaying in # error messages when using "python -m test ...". @@ -192,6 +205,7 @@ def _create_parser(): epilog=EPILOG, add_help=False, formatter_class=argparse.RawDescriptionHelpFormatter) + parser.set_defaults(match_tests=[]) # Arguments with this clause added to its help are described further in # the epilog's "Additional option details" section. @@ -251,17 +265,19 @@ def _create_parser(): help='single step through a set of tests.' + more_details) group.add_argument('-m', '--match', metavar='PAT', - dest='match_tests', action='append', + dest='match_tests', action=FilterAction, const=True, help='match test cases and methods with glob pattern PAT') group.add_argument('-i', '--ignore', metavar='PAT', - dest='ignore_tests', action='append', + dest='match_tests', action=FilterAction, const=False, help='ignore test cases and methods with glob pattern PAT') group.add_argument('--matchfile', metavar='FILENAME', - dest='match_filename', + dest='match_tests', + action=FromFileFilterAction, const=True, help='similar to --match but get patterns from a ' 'text file, one pattern per line') group.add_argument('--ignorefile', metavar='FILENAME', - dest='ignore_filename', + dest='match_tests', + action=FromFileFilterAction, const=False, help='similar to --matchfile but it receives patterns ' 'from text file to ignore') group.add_argument('-G', '--failfast', action='store_true', @@ -483,18 +499,6 @@ def _parse_args(args, **kwargs): print("WARNING: Disable --verbose3 because it's incompatible with " "--huntrleaks: see http://bugs.python.org/issue27103", file=sys.stderr) - if ns.match_filename: - if ns.match_tests is None: - ns.match_tests = [] - with open(ns.match_filename) as fp: - for line in fp: - ns.match_tests.append(line.strip()) - if ns.ignore_filename: - if ns.ignore_tests is None: - ns.ignore_tests = [] - with open(ns.ignore_filename) as fp: - for line in fp: - ns.ignore_tests.append(line.strip()) if ns.forever: # --forever implies --failfast ns.failfast = True diff --git a/Lib/test/libregrtest/findtests.py b/Lib/test/libregrtest/findtests.py index 96cc3e0d021184..dd39fe1ce20a77 100644 --- a/Lib/test/libregrtest/findtests.py +++ b/Lib/test/libregrtest/findtests.py @@ -5,7 +5,7 @@ from test import support from .utils import ( - StrPath, TestName, TestTuple, TestList, FilterTuple, + StrPath, TestName, TestTuple, TestList, TestFilter, abs_module_name, count, printlist) @@ -82,11 +82,10 @@ def _list_cases(suite): print(test.id()) def list_cases(tests: TestTuple, *, - match_tests: FilterTuple | None = None, - ignore_tests: FilterTuple | None = None, + match_tests: TestFilter | None = None, test_dir: StrPath | None = None): support.verbose = False - support.set_match_tests(match_tests, ignore_tests) + support.set_match_tests(match_tests) skipped = [] for test_name in tests: diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index e765ed5c613acb..8544bb484c8be0 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -19,7 +19,7 @@ from .setup import setup_process, setup_test_dir from .single import run_single_test, PROGRESS_MIN_TIME from .utils import ( - StrPath, StrJSON, TestName, TestList, TestTuple, FilterTuple, + StrPath, StrJSON, TestName, TestList, TestTuple, TestFilter, strip_py_suffix, count, format_duration, printlist, get_temp_dir, get_work_dir, exit_timeout, display_header, cleanup_temp_dir, print_warning, @@ -78,14 +78,7 @@ def __init__(self, ns: Namespace, _add_python_opts: bool = False): and ns._add_python_opts) # Select tests - if ns.match_tests: - self.match_tests: FilterTuple | None = tuple(ns.match_tests) - else: - self.match_tests = None - if ns.ignore_tests: - self.ignore_tests: FilterTuple | None = tuple(ns.ignore_tests) - else: - self.ignore_tests = None + self.match_tests: TestFilter = ns.match_tests self.exclude: bool = ns.exclude self.fromfile: StrPath | None = ns.fromfile self.starting_test: TestName | None = ns.start @@ -389,7 +382,7 @@ def finalize_tests(self, tracer): def display_summary(self): duration = time.perf_counter() - self.logger.start_time - filtered = bool(self.match_tests) or bool(self.ignore_tests) + filtered = bool(self.match_tests) # Total duration print() @@ -407,7 +400,6 @@ def create_run_tests(self, tests: TestTuple): fail_fast=self.fail_fast, fail_env_changed=self.fail_env_changed, match_tests=self.match_tests, - ignore_tests=self.ignore_tests, match_tests_dict=None, rerun=False, forever=self.forever, @@ -660,7 +652,6 @@ def main(self, tests: TestList | None = None): elif self.want_list_cases: list_cases(selected, match_tests=self.match_tests, - ignore_tests=self.ignore_tests, test_dir=self.test_dir) else: exitcode = self.run_tests(selected, tests) diff --git a/Lib/test/libregrtest/run_workers.py b/Lib/test/libregrtest/run_workers.py index 16f8331abd32f9..ab03cb54d6122e 100644 --- a/Lib/test/libregrtest/run_workers.py +++ b/Lib/test/libregrtest/run_workers.py @@ -261,7 +261,7 @@ def create_worker_runtests(self, test_name: TestName, json_file: JsonFile) -> Ru kwargs = {} if match_tests: - kwargs['match_tests'] = match_tests + kwargs['match_tests'] = [(test, True) for test in match_tests] if self.runtests.output_on_failure: kwargs['verbose'] = True kwargs['output_on_failure'] = False diff --git a/Lib/test/libregrtest/runtests.py b/Lib/test/libregrtest/runtests.py index 893b311c31297c..bfed1b4a2a5817 100644 --- a/Lib/test/libregrtest/runtests.py +++ b/Lib/test/libregrtest/runtests.py @@ -8,7 +8,7 @@ from test import support from .utils import ( - StrPath, StrJSON, TestTuple, FilterTuple, FilterDict) + StrPath, StrJSON, TestTuple, TestFilter, FilterTuple, FilterDict) class JsonFileType: @@ -72,8 +72,7 @@ class RunTests: tests: TestTuple fail_fast: bool fail_env_changed: bool - match_tests: FilterTuple | None - ignore_tests: FilterTuple | None + match_tests: TestFilter match_tests_dict: FilterDict | None rerun: bool forever: bool diff --git a/Lib/test/libregrtest/setup.py b/Lib/test/libregrtest/setup.py index 793347f60ad93c..6a96b051394d20 100644 --- a/Lib/test/libregrtest/setup.py +++ b/Lib/test/libregrtest/setup.py @@ -92,7 +92,7 @@ def setup_tests(runtests: RunTests): support.PGO = runtests.pgo support.PGO_EXTENDED = runtests.pgo_extended - support.set_match_tests(runtests.match_tests, runtests.ignore_tests) + support.set_match_tests(runtests.match_tests) if runtests.use_junit: support.junit_xml_list = [] diff --git a/Lib/test/libregrtest/utils.py b/Lib/test/libregrtest/utils.py index f62184ad4fec6c..f6de62af1f0ed5 100644 --- a/Lib/test/libregrtest/utils.py +++ b/Lib/test/libregrtest/utils.py @@ -52,6 +52,7 @@ TestList = list[TestName] # --match and --ignore options: list of patterns # ('*' joker character can be used) +TestFilter = list[tuple[TestName, bool]] FilterTuple = tuple[TestName, ...] FilterDict = dict[TestName, FilterTuple] diff --git a/Lib/test/libregrtest/worker.py b/Lib/test/libregrtest/worker.py index a9c8be0bb65d08..2eccfabc25223a 100644 --- a/Lib/test/libregrtest/worker.py +++ b/Lib/test/libregrtest/worker.py @@ -10,7 +10,7 @@ from .runtests import RunTests, JsonFile, JsonFileType from .single import run_single_test from .utils import ( - StrPath, StrJSON, FilterTuple, + StrPath, StrJSON, TestFilter, get_temp_dir, get_work_dir, exit_timeout) @@ -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) test_name = runtests.tests[0] - match_tests: FilterTuple | None = runtests.match_tests + match_tests: TestFilter = runtests.match_tests json_file: JsonFile = runtests.json_file setup_test_dir(runtests.test_dir) @@ -81,7 +81,7 @@ def worker_process(worker_json: StrJSON) -> NoReturn: if runtests.rerun: if match_tests: - matching = "matching: " + ", ".join(match_tests) + matching = "matching: " + ", ".join(pattern for pattern, result in match_tests if result) print(f"Re-running {test_name} in verbose mode ({matching})", flush=True) else: print(f"Re-running {test_name} in verbose mode", flush=True) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 9066f0cd5b3819..2d53f635cef7d3 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -6,7 +6,9 @@ import contextlib import dataclasses import functools +import itertools import getpass +import operator import os import re import stat @@ -1172,18 +1174,17 @@ def _run_suite(suite): # By default, don't filter tests -_match_test_func = None - -_accept_test_patterns = None -_ignore_test_patterns = None +_test_matchers = () +_test_patterns = () def match_test(test): # Function used by support.run_unittest() and regrtest --list-cases - if _match_test_func is None: - return True - else: - return _match_test_func(test.id()) + result = False + for matcher, result in reversed(_test_matchers): + if matcher(test.id()): + return result + return not result def _is_full_match_test(pattern): @@ -1196,47 +1197,30 @@ def _is_full_match_test(pattern): return ('.' in pattern) and (not re.search(r'[?*\[\]]', pattern)) -def set_match_tests(accept_patterns=None, ignore_patterns=None): - global _match_test_func, _accept_test_patterns, _ignore_test_patterns - - if accept_patterns is None: - accept_patterns = () - if ignore_patterns is None: - ignore_patterns = () - - accept_func = ignore_func = None - - if accept_patterns != _accept_test_patterns: - accept_patterns, accept_func = _compile_match_function(accept_patterns) - if ignore_patterns != _ignore_test_patterns: - ignore_patterns, ignore_func = _compile_match_function(ignore_patterns) - - # Create a copy since patterns can be mutable and so modified later - _accept_test_patterns = tuple(accept_patterns) - _ignore_test_patterns = tuple(ignore_patterns) +def set_match_tests(patterns): + global _test_matchers, _test_patterns - if accept_func is not None or ignore_func is not None: - def match_function(test_id): - accept = True - ignore = False - if accept_func: - accept = accept_func(test_id) - if ignore_func: - ignore = ignore_func(test_id) - return accept and not ignore - - _match_test_func = match_function + if not patterns: + _test_matchers = () + _test_patterns = () + else: + itemgetter = operator.itemgetter + patterns = tuple(patterns) + if patterns != _test_patterns: + _test_matchers = [ + (_compile_match_function(map(itemgetter(0), it)), result) + for result, it in itertools.groupby(patterns, itemgetter(1)) + ] + _test_patterns = patterns def _compile_match_function(patterns): - if not patterns: - func = None - # set_match_tests(None) behaves as set_match_tests(()) - patterns = () - elif all(map(_is_full_match_test, patterns)): + patterns = list(patterns) + + if all(map(_is_full_match_test, patterns)): # Simple case: all patterns are full test identifier. # The test.bisect_cmd utility only uses such full test identifiers. - func = set(patterns).__contains__ + return set(patterns).__contains__ else: import fnmatch regex = '|'.join(map(fnmatch.translate, patterns)) @@ -1244,7 +1228,7 @@ def _compile_match_function(patterns): # don't use flags=re.IGNORECASE regex_match = re.compile(regex).match - def match_test_regex(test_id): + def match_test_regex(test_id, regex_match=regex_match): if regex_match(test_id): # The regex matches the whole identifier, for example # 'test.test_os.FileTests.test_access'. @@ -1255,9 +1239,7 @@ def match_test_regex(test_id): # into: 'test', 'test_os', 'FileTests' and 'test_access'. return any(map(regex_match, test_id.split("."))) - func = match_test_regex - - return patterns, func + return match_test_regex def run_unittest(*classes): diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py index 91f2fb0286adfe..0c12b0efff88e5 100644 --- a/Lib/test/test_regrtest.py +++ b/Lib/test/test_regrtest.py @@ -192,34 +192,27 @@ def test_single(self): self.assertTrue(ns.single) self.checkError([opt, '-f', 'foo'], "don't go together") - def test_ignore(self): - for opt in '-i', '--ignore': + def test_match(self): + for opt in '-m', '--match': with self.subTest(opt=opt): ns = self.parse_args([opt, 'pattern']) - self.assertEqual(ns.ignore_tests, ['pattern']) + self.assertEqual(ns.match_tests, [('pattern', True)]) self.checkError([opt], 'expected one argument') - self.addCleanup(os_helper.unlink, os_helper.TESTFN) - with open(os_helper.TESTFN, "w") as fp: - print('matchfile1', file=fp) - print('matchfile2', file=fp) - - filename = os.path.abspath(os_helper.TESTFN) - ns = self.parse_args(['-m', 'match', - '--ignorefile', filename]) - self.assertEqual(ns.ignore_tests, - ['matchfile1', 'matchfile2']) - - def test_match(self): - for opt in '-m', '--match': + for opt in '-i', '--ignore': with self.subTest(opt=opt): ns = self.parse_args([opt, 'pattern']) - self.assertEqual(ns.match_tests, ['pattern']) + self.assertEqual(ns.match_tests, [('pattern', False)]) self.checkError([opt], 'expected one argument') - ns = self.parse_args(['-m', 'pattern1', - '-m', 'pattern2']) - self.assertEqual(ns.match_tests, ['pattern1', 'pattern2']) + ns = self.parse_args(['-m', 'pattern1', '-m', 'pattern2']) + self.assertEqual(ns.match_tests, [('pattern1', True), ('pattern2', True)]) + + ns = self.parse_args(['-m', 'pattern1', '-i', 'pattern2']) + self.assertEqual(ns.match_tests, [('pattern1', True), ('pattern2', False)]) + + ns = self.parse_args(['-i', 'pattern1', '-m', 'pattern2']) + self.assertEqual(ns.match_tests, [('pattern1', False), ('pattern2', True)]) self.addCleanup(os_helper.unlink, os_helper.TESTFN) with open(os_helper.TESTFN, "w") as fp: @@ -227,10 +220,13 @@ def test_match(self): print('matchfile2', file=fp) filename = os.path.abspath(os_helper.TESTFN) - ns = self.parse_args(['-m', 'match', - '--matchfile', filename]) + ns = self.parse_args(['-m', 'match', '--matchfile', filename]) + self.assertEqual(ns.match_tests, + [('match', True), ('matchfile1', True), ('matchfile2', True)]) + + ns = self.parse_args(['-i', 'match', '--ignorefile', filename]) self.assertEqual(ns.match_tests, - ['match', 'matchfile1', 'matchfile2']) + [('match', False), ('matchfile1', False), ('matchfile2', False)]) def test_failfast(self): for opt in '-G', '--failfast': diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index 01ceeec3c0a73c..3f9159642ba178 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -560,100 +560,109 @@ def id(self): test_access = Test('test.test_os.FileTests.test_access') test_chdir = Test('test.test_os.Win32ErrorTests.test_chdir') + test_copy = Test('test.test_shutil.TestCopy.test_copy') # Test acceptance - with support.swap_attr(support, '_match_test_func', None): + with support.swap_attr(support, '_test_matchers', ()): # match all support.set_match_tests([]) self.assertTrue(support.match_test(test_access)) self.assertTrue(support.match_test(test_chdir)) # match all using None - support.set_match_tests(None, None) + support.set_match_tests(None) self.assertTrue(support.match_test(test_access)) self.assertTrue(support.match_test(test_chdir)) # match the full test identifier - support.set_match_tests([test_access.id()], None) + support.set_match_tests([(test_access.id(), True)]) self.assertTrue(support.match_test(test_access)) self.assertFalse(support.match_test(test_chdir)) # match the module name - support.set_match_tests(['test_os'], None) + support.set_match_tests([('test_os', True)]) self.assertTrue(support.match_test(test_access)) self.assertTrue(support.match_test(test_chdir)) + self.assertFalse(support.match_test(test_copy)) # Test '*' pattern - support.set_match_tests(['test_*'], None) + support.set_match_tests([('test_*', True)]) self.assertTrue(support.match_test(test_access)) self.assertTrue(support.match_test(test_chdir)) # Test case sensitivity - support.set_match_tests(['filetests'], None) + support.set_match_tests([('filetests', True)]) self.assertFalse(support.match_test(test_access)) - support.set_match_tests(['FileTests'], None) + support.set_match_tests([('FileTests', True)]) self.assertTrue(support.match_test(test_access)) # Test pattern containing '.' and a '*' metacharacter - support.set_match_tests(['*test_os.*.test_*'], None) + support.set_match_tests([('*test_os.*.test_*', True)]) self.assertTrue(support.match_test(test_access)) self.assertTrue(support.match_test(test_chdir)) + self.assertFalse(support.match_test(test_copy)) # Multiple patterns - support.set_match_tests([test_access.id(), test_chdir.id()], None) + support.set_match_tests([(test_access.id(), True), (test_chdir.id(), True)]) self.assertTrue(support.match_test(test_access)) self.assertTrue(support.match_test(test_chdir)) + self.assertFalse(support.match_test(test_copy)) - support.set_match_tests(['test_access', 'DONTMATCH'], None) + support.set_match_tests([('test_access', True), ('DONTMATCH', True)]) self.assertTrue(support.match_test(test_access)) self.assertFalse(support.match_test(test_chdir)) # Test rejection - with support.swap_attr(support, '_match_test_func', None): - # match all - support.set_match_tests(ignore_patterns=[]) - self.assertTrue(support.match_test(test_access)) - self.assertTrue(support.match_test(test_chdir)) - - # match all using None - support.set_match_tests(None, None) - self.assertTrue(support.match_test(test_access)) - self.assertTrue(support.match_test(test_chdir)) - + with support.swap_attr(support, '_test_matchers', ()): # match the full test identifier - support.set_match_tests(None, [test_access.id()]) + support.set_match_tests([(test_access.id(), False)]) self.assertFalse(support.match_test(test_access)) self.assertTrue(support.match_test(test_chdir)) # match the module name - support.set_match_tests(None, ['test_os']) + support.set_match_tests([('test_os', False)]) self.assertFalse(support.match_test(test_access)) self.assertFalse(support.match_test(test_chdir)) + self.assertTrue(support.match_test(test_copy)) # Test '*' pattern - support.set_match_tests(None, ['test_*']) + support.set_match_tests([('test_*', False)]) self.assertFalse(support.match_test(test_access)) self.assertFalse(support.match_test(test_chdir)) # Test case sensitivity - support.set_match_tests(None, ['filetests']) + support.set_match_tests([('filetests', False)]) self.assertTrue(support.match_test(test_access)) - support.set_match_tests(None, ['FileTests']) + support.set_match_tests([('FileTests', False)]) self.assertFalse(support.match_test(test_access)) # Test pattern containing '.' and a '*' metacharacter - support.set_match_tests(None, ['*test_os.*.test_*']) + support.set_match_tests([('*test_os.*.test_*', False)]) self.assertFalse(support.match_test(test_access)) self.assertFalse(support.match_test(test_chdir)) + self.assertTrue(support.match_test(test_copy)) # Multiple patterns - support.set_match_tests(None, [test_access.id(), test_chdir.id()]) + support.set_match_tests([(test_access.id(), False), (test_chdir.id(), False)]) + self.assertFalse(support.match_test(test_access)) + self.assertFalse(support.match_test(test_chdir)) + self.assertTrue(support.match_test(test_copy)) + + support.set_match_tests([('test_access', False), ('DONTMATCH', False)]) self.assertFalse(support.match_test(test_access)) + self.assertTrue(support.match_test(test_chdir)) + + # Test mixed filters + with support.swap_attr(support, '_test_matchers', ()): + support.set_match_tests([('*test_os', False), ('test_access', True)]) + self.assertTrue(support.match_test(test_access)) self.assertFalse(support.match_test(test_chdir)) + self.assertTrue(support.match_test(test_copy)) - support.set_match_tests(None, ['test_access', 'DONTMATCH']) + support.set_match_tests([('*test_os', True), ('test_access', False)]) self.assertFalse(support.match_test(test_access)) self.assertTrue(support.match_test(test_chdir)) + self.assertFalse(support.match_test(test_copy)) @unittest.skipIf(support.is_emscripten, "Unstable in Emscripten") @unittest.skipIf(support.is_wasi, "Unavailable on WASI") 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 new file mode 100644 index 00000000000000..7cb79c0cbf29f1 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-10-16-13-47-24.gh-issue-110918.aFgZK3.rst @@ -0,0 +1,4 @@ +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. From cf28c61c73174638b7596d9fff70f3c4e4965b30 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 21 Oct 2023 20:23:38 +0200 Subject: [PATCH 513/632] [3.11] gh-111159: Fix `doctest` output comparison for exceptions with notes (GH-111160) (#111170) gh-111159: Fix `doctest` output comparison for exceptions with notes (GH-111160) (cherry picked from commit fd60549c0ac6c81f05594a5141d24b4433ae39be) Co-authored-by: Nikita Sobolev --- Lib/doctest.py | 15 +- Lib/test/test_doctest.py | 144 ++++++++++++++++++ ...-10-21-13-57-06.gh-issue-111159.GoHp7s.rst | 1 + 3 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2023-10-21-13-57-06.gh-issue-111159.GoHp7s.rst diff --git a/Lib/doctest.py b/Lib/doctest.py index 8fcb4ee0a02ee0..783664a0d3554e 100644 --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -1370,7 +1370,20 @@ def __run(self, test, compileflags, out): # The example raised an exception: check if it was expected. else: - exc_msg = traceback.format_exception_only(*exception[:2])[-1] + formatted_ex = traceback.format_exception_only(*exception[:2]) + if issubclass(exception[0], SyntaxError): + # SyntaxError / IndentationError is special: + # we don't care about the carets / suggestions / etc + # We only care about the error message and notes. + # They start with `SyntaxError:` (or any other class name) + exc_msg_index = next( + index + for index, line in enumerate(formatted_ex) + if line.startswith(f"{exception[0].__name__}:") + ) + formatted_ex = formatted_ex[exc_msg_index:] + + exc_msg = "".join(formatted_ex) if not quiet: got += _exception_traceback(exception) diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py index e48d91fafeda7d..fbeed96e5bee36 100644 --- a/Lib/test/test_doctest.py +++ b/Lib/test/test_doctest.py @@ -3168,6 +3168,150 @@ def test_run_doctestsuite_multiple_times(): """ +def test_exception_with_note(note): + """ + >>> test_exception_with_note('Note') + Traceback (most recent call last): + ... + ValueError: Text + Note + + >>> test_exception_with_note('Note') # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + ValueError: Text + Note + + >>> test_exception_with_note('''Note + ... multiline + ... example''') + Traceback (most recent call last): + ValueError: Text + Note + multiline + example + + Different note will fail the test: + + >>> def f(x): + ... r''' + ... >>> exc = ValueError('message') + ... >>> exc.add_note('note') + ... >>> raise exc + ... Traceback (most recent call last): + ... ValueError: message + ... wrong note + ... ''' + >>> test = doctest.DocTestFinder().find(f)[0] + >>> doctest.DocTestRunner(verbose=False).run(test) + ... # doctest: +ELLIPSIS + ********************************************************************** + File "...", line 5, in f + Failed example: + raise exc + Expected: + Traceback (most recent call last): + ValueError: message + wrong note + Got: + Traceback (most recent call last): + ... + ValueError: message + note + TestResults(failed=1, attempted=...) + """ + exc = ValueError('Text') + exc.add_note(note) + raise exc + + +def test_exception_with_multiple_notes(): + """ + >>> test_exception_with_multiple_notes() + Traceback (most recent call last): + ... + ValueError: Text + One + Two + """ + exc = ValueError('Text') + exc.add_note('One') + exc.add_note('Two') + raise exc + + +def test_syntax_error_with_note(cls, multiline=False): + """ + >>> test_syntax_error_with_note(SyntaxError) + Traceback (most recent call last): + ... + SyntaxError: error + Note + + >>> test_syntax_error_with_note(SyntaxError) + Traceback (most recent call last): + SyntaxError: error + Note + + >>> test_syntax_error_with_note(SyntaxError) + Traceback (most recent call last): + ... + File "x.py", line 23 + bad syntax + SyntaxError: error + Note + + >>> test_syntax_error_with_note(IndentationError) + Traceback (most recent call last): + ... + IndentationError: error + Note + + >>> test_syntax_error_with_note(TabError, multiline=True) + Traceback (most recent call last): + ... + TabError: error + Note + Line + """ + exc = cls("error", ("x.py", 23, None, "bad syntax")) + exc.add_note('Note\nLine' if multiline else 'Note') + raise exc + + +def test_syntax_error_with_incorrect_expected_note(): + """ + >>> def f(x): + ... r''' + ... >>> exc = SyntaxError("error", ("x.py", 23, None, "bad syntax")) + ... >>> exc.add_note('note1') + ... >>> exc.add_note('note2') + ... >>> raise exc + ... Traceback (most recent call last): + ... SyntaxError: error + ... wrong note + ... ''' + >>> test = doctest.DocTestFinder().find(f)[0] + >>> doctest.DocTestRunner(verbose=False).run(test) + ... # doctest: +ELLIPSIS + ********************************************************************** + File "...", line 6, in f + Failed example: + raise exc + Expected: + Traceback (most recent call last): + SyntaxError: error + wrong note + Got: + Traceback (most recent call last): + ... + SyntaxError: error + note1 + note2 + TestResults(failed=1, attempted=...) + """ + + def load_tests(loader, tests, pattern): tests.addTest(doctest.DocTestSuite(doctest)) tests.addTest(doctest.DocTestSuite()) 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 new file mode 100644 index 00000000000000..bdec4f4443d80b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-10-21-13-57-06.gh-issue-111159.GoHp7s.rst @@ -0,0 +1 @@ +Fix :mod:`doctest` output comparison for exceptions with notes. From cf777399a93f837bca623e8cf887fc0ec42340e6 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 21 Oct 2023 21:40:07 +0200 Subject: [PATCH 514/632] [3.11] gh-111085: Fix invalid state handling in TaskGroup and Timeout (GH-111111) (GH-111172) asyncio.TaskGroup and asyncio.Timeout classes now raise proper RuntimeError if they are improperly used. * When they are used without entering the context manager. * When they are used after finishing. * When the context manager is entered more than once (simultaneously or sequentially). * If there is no current task when entering the context manager. They now remain in a consistent state after an exception is thrown, so subsequent operations can be performed correctly (if they are allowed). (cherry picked from commit 6c23635f2b7067ef091a550954e09f8b7c329e3f) Co-authored-by: Serhiy Storchaka Co-authored-by: James Hilton-Balfe --- Lib/asyncio/taskgroups.py | 6 +-- Lib/asyncio/timeouts.py | 12 +++-- Lib/test/test_asyncio/test_taskgroups.py | 45 +++++++++++++++++ Lib/test/test_asyncio/test_timeouts.py | 48 ++++++++++++++++++- Lib/test/test_asyncio/utils.py | 15 ++++++ ...-10-20-15-29-10.gh-issue-110910.u2oPwX.rst | 3 ++ 6 files changed, 120 insertions(+), 9 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-10-20-15-29-10.gh-issue-110910.u2oPwX.rst diff --git a/Lib/asyncio/taskgroups.py b/Lib/asyncio/taskgroups.py index 0fdea3697ece3d..bfdbe63049f9bd 100644 --- a/Lib/asyncio/taskgroups.py +++ b/Lib/asyncio/taskgroups.py @@ -54,16 +54,14 @@ def __repr__(self): async def __aenter__(self): if self._entered: raise RuntimeError( - f"TaskGroup {self!r} has been already entered") - self._entered = True - + f"TaskGroup {self!r} has already been entered") if self._loop is None: self._loop = events.get_running_loop() - self._parent_task = tasks.current_task(self._loop) if self._parent_task is None: raise RuntimeError( f'TaskGroup {self!r} cannot determine the parent task') + self._entered = True return self diff --git a/Lib/asyncio/timeouts.py b/Lib/asyncio/timeouts.py index 029c468739bf2d..30042abb3ad804 100644 --- a/Lib/asyncio/timeouts.py +++ b/Lib/asyncio/timeouts.py @@ -49,8 +49,9 @@ def when(self) -> Optional[float]: def reschedule(self, when: Optional[float]) -> None: """Reschedule the timeout.""" - assert self._state is not _State.CREATED if self._state is not _State.ENTERED: + if self._state is _State.CREATED: + raise RuntimeError("Timeout has not been entered") raise RuntimeError( f"Cannot change state of {self._state.value} Timeout", ) @@ -82,11 +83,14 @@ def __repr__(self) -> str: return f"" async def __aenter__(self) -> "Timeout": + if self._state is not _State.CREATED: + raise RuntimeError("Timeout has already been entered") + task = tasks.current_task() + if task is None: + raise RuntimeError("Timeout should be used inside a task") self._state = _State.ENTERED - self._task = tasks.current_task() + self._task = task self._cancelling = self._task.cancelling() - if self._task is None: - raise RuntimeError("Timeout should be used inside a task") self.reschedule(self._when) return self diff --git a/Lib/test/test_asyncio/test_taskgroups.py b/Lib/test/test_asyncio/test_taskgroups.py index 6a0231f2859a62..7a18362b54e469 100644 --- a/Lib/test/test_asyncio/test_taskgroups.py +++ b/Lib/test/test_asyncio/test_taskgroups.py @@ -8,6 +8,8 @@ from asyncio import taskgroups import unittest +from test.test_asyncio.utils import await_without_task + # To prevent a warning "test altered the execution environment" def tearDownModule(): @@ -779,6 +781,49 @@ async def main(): await asyncio.create_task(main()) + async def test_taskgroup_already_entered(self): + tg = taskgroups.TaskGroup() + async with tg: + with self.assertRaisesRegex(RuntimeError, "has already been entered"): + async with tg: + pass + + async def test_taskgroup_double_enter(self): + tg = taskgroups.TaskGroup() + async with tg: + pass + with self.assertRaisesRegex(RuntimeError, "has already been entered"): + async with tg: + pass + + async def test_taskgroup_finished(self): + tg = taskgroups.TaskGroup() + async with tg: + pass + coro = asyncio.sleep(0) + with self.assertRaisesRegex(RuntimeError, "is finished"): + tg.create_task(coro) + # We still have to await coro to avoid a warning + await coro + + async def test_taskgroup_not_entered(self): + tg = taskgroups.TaskGroup() + coro = asyncio.sleep(0) + with self.assertRaisesRegex(RuntimeError, "has not been entered"): + tg.create_task(coro) + # We still have to await coro to avoid a warning + await coro + + async def test_taskgroup_without_parent_task(self): + tg = taskgroups.TaskGroup() + with self.assertRaisesRegex(RuntimeError, "parent task"): + await await_without_task(tg.__aenter__()) + coro = asyncio.sleep(0) + with self.assertRaisesRegex(RuntimeError, "has not been entered"): + tg.create_task(coro) + # We still have to await coro to avoid a warning + await coro + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_asyncio/test_timeouts.py b/Lib/test/test_asyncio/test_timeouts.py index 5a4093e94707bc..bfa3f1bff694ad 100644 --- a/Lib/test/test_asyncio/test_timeouts.py +++ b/Lib/test/test_asyncio/test_timeouts.py @@ -6,11 +6,12 @@ import asyncio from asyncio import tasks +from test.test_asyncio.utils import await_without_task + def tearDownModule(): asyncio.set_event_loop_policy(None) - class TimeoutTests(unittest.IsolatedAsyncioTestCase): async def test_timeout_basic(self): @@ -258,6 +259,51 @@ async def test_timeout_exception_cause (self): cause = exc.exception.__cause__ assert isinstance(cause, asyncio.CancelledError) + async def test_timeout_already_entered(self): + async with asyncio.timeout(0.01) as cm: + with self.assertRaisesRegex(RuntimeError, "has already been entered"): + async with cm: + pass + + async def test_timeout_double_enter(self): + async with asyncio.timeout(0.01) as cm: + pass + with self.assertRaisesRegex(RuntimeError, "has already been entered"): + async with cm: + pass + + async def test_timeout_finished(self): + async with asyncio.timeout(0.01) as cm: + pass + with self.assertRaisesRegex(RuntimeError, "finished"): + cm.reschedule(0.02) + + async def test_timeout_expired(self): + with self.assertRaises(TimeoutError): + async with asyncio.timeout(0.01) as cm: + await asyncio.sleep(1) + with self.assertRaisesRegex(RuntimeError, "expired"): + cm.reschedule(0.02) + + async def test_timeout_expiring(self): + async with asyncio.timeout(0.01) as cm: + with self.assertRaises(asyncio.CancelledError): + await asyncio.sleep(1) + with self.assertRaisesRegex(RuntimeError, "expiring"): + cm.reschedule(0.02) + + async def test_timeout_not_entered(self): + cm = asyncio.timeout(0.01) + with self.assertRaisesRegex(RuntimeError, "has not been entered"): + cm.reschedule(0.02) + + async def test_timeout_without_task(self): + cm = asyncio.timeout(0.01) + with self.assertRaisesRegex(RuntimeError, "task"): + await await_without_task(cm.__aenter__()) + with self.assertRaisesRegex(RuntimeError, "has not been entered"): + cm.reschedule(0.02) + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_asyncio/utils.py b/Lib/test/test_asyncio/utils.py index d6f60db10f9b3f..7940855b19efed 100644 --- a/Lib/test/test_asyncio/utils.py +++ b/Lib/test/test_asyncio/utils.py @@ -612,3 +612,18 @@ def mock_nonblocking_socket(proto=socket.IPPROTO_TCP, type=socket.SOCK_STREAM, sock.family = family sock.gettimeout.return_value = 0.0 return sock + + +async def await_without_task(coro): + exc = None + def func(): + try: + for _ in coro.__await__(): + pass + except BaseException as err: + nonlocal exc + exc = err + asyncio.get_running_loop().call_soon(func) + await asyncio.sleep(0) + if exc is not None: + raise exc 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 new file mode 100644 index 00000000000000..c750447e9fe4a5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-10-20-15-29-10.gh-issue-110910.u2oPwX.rst @@ -0,0 +1,3 @@ +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. From d0502a9c6786474a56d35744d3d803908df0b794 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 22 Oct 2023 14:03:39 +0200 Subject: [PATCH 515/632] [3.11] gh-101100: Fix Sphinx warning in `tutorial/introduction.rst` (GH-111173) (#111176) gh-101100: Fix Sphinx warning in `tutorial/introduction.rst` (GH-111173) (cherry picked from commit 663cf513b0e973ab7aa4a8609d6616ad2c283f22) Co-authored-by: Maciej Olko --- Doc/tools/.nitignore | 1 - Doc/tutorial/introduction.rst | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index d42292aed04ea4..08b9f6d688c587 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -144,7 +144,6 @@ Doc/reference/expressions.rst Doc/reference/import.rst Doc/reference/simple_stmts.rst Doc/tutorial/datastructures.rst -Doc/tutorial/introduction.rst Doc/using/windows.rst Doc/whatsnew/2.0.rst Doc/whatsnew/2.1.rst diff --git a/Doc/tutorial/introduction.rst b/Doc/tutorial/introduction.rst index b3b8e423a09fa1..731655a5885d14 100644 --- a/Doc/tutorial/introduction.rst +++ b/Doc/tutorial/introduction.rst @@ -428,7 +428,7 @@ type, i.e. it is possible to change their content:: [1, 8, 27, 64, 125] You can also add new items at the end of the list, by using -the :meth:`~list.append` *method* (we will see more about methods later):: +the :meth:`!list.append` *method* (we will see more about methods later):: >>> cubes.append(216) # add the cube of 6 >>> cubes.append(7 ** 3) # and the cube of 7 From aaa755dd48ac35a392bad211b21bef8808f81719 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 22 Oct 2023 19:29:06 +0200 Subject: [PATCH 516/632] [3.11] gh-101100: Fix sphinx warnings in `library/asyncio-dev.rst` (GH-111179) (#111186) gh-101100: Fix sphinx warnings in `library/asyncio-dev.rst` (GH-111179) * gh-101100: Fix sphinx warnings in `library/asyncio-dev.rst` * Update Doc/library/asyncio-eventloop.rst * Update Doc/library/asyncio-eventloop.rst --------- (cherry picked from commit 8c689c9b88426384a9736c708701923a1ab1da79) Co-authored-by: Nikita Sobolev Co-authored-by: Carol Willing --- Doc/library/asyncio-eventloop.rst | 14 +++++++++++--- Doc/tools/.nitignore | 1 - 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index 07be0d0eea0888..aeebc3bae85e13 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -238,9 +238,9 @@ Scheduling callbacks See the :ref:`concurrency and multithreading ` section of the documentation. -.. versionchanged:: 3.7 - The *context* keyword-only parameter was added. See :pep:`567` - for more details. + .. versionchanged:: 3.7 + The *context* keyword-only parameter was added. See :pep:`567` + for more details. .. _asyncio-pass-keywords: @@ -1365,6 +1365,14 @@ Enabling debug mode The new :ref:`Python Development Mode ` can now also be used to enable the debug mode. +.. attribute:: loop.slow_callback_duration + + This attribute can be used to set the + minimum execution duration in seconds that is considered "slow". + When debug mode is enabled, "slow" callbacks are logged. + + Default value is 100 milliseconds. + .. seealso:: The :ref:`debug mode of asyncio `. diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 08b9f6d688c587..8fd7bcab8b2dff 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -31,7 +31,6 @@ Doc/howto/urllib2.rst Doc/library/__future__.rst Doc/library/abc.rst Doc/library/ast.rst -Doc/library/asyncio-dev.rst Doc/library/asyncio-eventloop.rst Doc/library/asyncio-extending.rst Doc/library/asyncio-policy.rst From 3c3c489d41f3bca783d48bd6bbc70333093d9a99 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 22 Oct 2023 21:12:58 +0200 Subject: [PATCH 517/632] [3.11] gh-110196: Fix ipaddress.IPv6Address.__reduce__ (GH-110198) (GH-111190) (cherry picked from commit 767f416feb551f495bacfff1e9ba1e6672c2f24e) Co-authored-by: Tian Gao --- Lib/ipaddress.py | 3 +++ Lib/test/test_ipaddress.py | 7 +++++++ .../Library/2023-10-02-05-23-27.gh-issue-110196.djwt0z.rst | 1 + 3 files changed, 11 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2023-10-02-05-23-27.gh-issue-110196.djwt0z.rst diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py index 1cb71d8032e173..16ba16cd7de49a 100644 --- a/Lib/ipaddress.py +++ b/Lib/ipaddress.py @@ -1941,6 +1941,9 @@ def __eq__(self, other): return False return self._scope_id == getattr(other, '_scope_id', None) + def __reduce__(self): + return (self.__class__, (str(self),)) + @property def scope_id(self): """Identifier of a particular zone of the address's scope. diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py index a5388b2e5debd8..fc27628af17f8d 100644 --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -4,6 +4,7 @@ """Unittest for ipaddress module.""" +import copy import unittest import re import contextlib @@ -542,11 +543,17 @@ def assertBadPart(addr, part): def test_pickle(self): self.pickle_test('2001:db8::') + self.pickle_test('2001:db8::%scope') def test_weakref(self): weakref.ref(self.factory('2001:db8::')) weakref.ref(self.factory('2001:db8::%scope')) + def test_copy(self): + addr = self.factory('2001:db8::%scope') + self.assertEqual(addr, copy.copy(addr)) + self.assertEqual(addr, copy.deepcopy(addr)) + class NetmaskTestMixin_v4(CommonTestMixin_v4): """Input validation on interfaces and networks is very similar""" 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 new file mode 100644 index 00000000000000..341f3380fffd60 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-10-02-05-23-27.gh-issue-110196.djwt0z.rst @@ -0,0 +1 @@ +Add ``__reduce__`` method to :class:`IPv6Address` in order to keep ``scope_id`` From 6020a3e736e83f82048fd46dad8719f738f24cdd Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 23 Oct 2023 09:31:32 +0200 Subject: [PATCH 518/632] [3.11] gh-110383: Added explanation about simplest regex use case for quantifiers. (GH-111110) (#111205) Co-authored-by: Nick Co-authored-by: Hugo van Kemenade --- Doc/howto/regex.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Doc/howto/regex.rst b/Doc/howto/regex.rst index 8d95d86ba398b6..2cc17b4f745cb6 100644 --- a/Doc/howto/regex.rst +++ b/Doc/howto/regex.rst @@ -245,6 +245,9 @@ You can omit either *m* or *n*; in that case, a reasonable value is assumed for the missing value. Omitting *m* is interpreted as a lower limit of 0, while omitting *n* results in an upper bound of infinity. +The simplest case ``{m}`` matches the preceding item exactly **m** times. +For example, ``a/{2}b`` will only match ``'a//b'``. + Readers of a reductionist bent may notice that the three other quantifiers can all be expressed using this notation. ``{0,}`` is the same as ``*``, ``{1,}`` is equivalent to ``+``, and ``{0,1}`` is the same as ``?``. It's better to use From 0a23960266fe486c53bca9bc569709dacd3e03c2 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 23 Oct 2023 10:11:54 +0200 Subject: [PATCH 519/632] [3.11] gh-110383: Italicize variable name (GH-111206) (#111208) Co-authored-by: Nick --- Doc/howto/regex.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/howto/regex.rst b/Doc/howto/regex.rst index 2cc17b4f745cb6..b69d19e212bdc9 100644 --- a/Doc/howto/regex.rst +++ b/Doc/howto/regex.rst @@ -245,7 +245,7 @@ You can omit either *m* or *n*; in that case, a reasonable value is assumed for the missing value. Omitting *m* is interpreted as a lower limit of 0, while omitting *n* results in an upper bound of infinity. -The simplest case ``{m}`` matches the preceding item exactly **m** times. +The simplest case ``{m}`` matches the preceding item exactly *m* times. For example, ``a/{2}b`` will only match ``'a//b'``. Readers of a reductionist bent may notice that the three other quantifiers can From f446df741f58488da198a265bc807712b3a66a98 Mon Sep 17 00:00:00 2001 From: Furkan Onder Date: Mon, 23 Oct 2023 12:49:44 +0300 Subject: [PATCH 520/632] [3.11] gh-67565: Add tests for C-contiguity checks (GH-110951) (GH-111199) (cherry picked from commit 9376728ce45191fcc0b908c7487ad7985454537e) --- Lib/test/test_binascii.py | 6 ++++++ Lib/test/test_capi/test_getargs.py | 19 +++++++++++++++++++ Lib/test/test_ssl.py | 4 ++++ 3 files changed, 29 insertions(+) diff --git a/Lib/test/test_binascii.py b/Lib/test/test_binascii.py index 7087d7a471d3f4..6fb57c3166d2b6 100644 --- a/Lib/test/test_binascii.py +++ b/Lib/test/test_binascii.py @@ -428,6 +428,12 @@ def test_b2a_base64_newline(self): self.assertEqual(binascii.b2a_base64(b, newline=False), b'aGVsbG8=') + def test_c_contiguity(self): + m = memoryview(bytearray(b'noncontig')) + noncontig_writable = m[::-2] + with self.assertRaises(BufferError): + binascii.b2a_hex(noncontig_writable) + class ArrayBinASCIITest(BinASCIITest): def type2test(self, s): diff --git a/Lib/test/test_capi/test_getargs.py b/Lib/test/test_capi/test_getargs.py index 552fa213524723..27675533d1bb4b 100644 --- a/Lib/test/test_capi/test_getargs.py +++ b/Lib/test/test_capi/test_getargs.py @@ -152,6 +152,8 @@ class TupleSubclass(tuple): class DictSubclass(dict): pass +NONCONTIG_WRITABLE = memoryview(bytearray(b'noncontig'))[::-2] +NONCONTIG_READONLY = memoryview(b'noncontig')[::-2] class Unsigned_TestCase(unittest.TestCase): def test_b(self): @@ -836,6 +838,8 @@ def test_y_star(self): self.assertEqual(getargs_y_star(bytearray(b'bytearray')), b'bytearray') self.assertEqual(getargs_y_star(memoryview(b'memoryview')), b'memoryview') self.assertRaises(TypeError, getargs_y_star, None) + self.assertRaises(BufferError, getargs_y_star, NONCONTIG_WRITABLE) + self.assertRaises(BufferError, getargs_y_star, NONCONTIG_READONLY) def test_y_hash(self): from _testcapi import getargs_y_hash @@ -845,6 +849,9 @@ def test_y_hash(self): self.assertRaises(TypeError, getargs_y_hash, bytearray(b'bytearray')) self.assertRaises(TypeError, getargs_y_hash, memoryview(b'memoryview')) self.assertRaises(TypeError, getargs_y_hash, None) + # TypeError: must be read-only bytes-like object, not memoryview + self.assertRaises(TypeError, getargs_y_hash, NONCONTIG_WRITABLE) + self.assertRaises(TypeError, getargs_y_hash, NONCONTIG_READONLY) def test_w_star(self): # getargs_w_star() modifies first and last byte @@ -860,6 +867,8 @@ def test_w_star(self): self.assertEqual(getargs_w_star(memoryview(buf)), b'[emoryvie]') self.assertEqual(buf, bytearray(b'[emoryvie]')) self.assertRaises(TypeError, getargs_w_star, None) + self.assertRaises(TypeError, getargs_w_star, NONCONTIG_WRITABLE) + self.assertRaises(TypeError, getargs_w_star, NONCONTIG_READONLY) class String_TestCase(unittest.TestCase): @@ -892,6 +901,8 @@ def test_s_star(self): self.assertEqual(getargs_s_star(bytearray(b'bytearray')), b'bytearray') self.assertEqual(getargs_s_star(memoryview(b'memoryview')), b'memoryview') self.assertRaises(TypeError, getargs_s_star, None) + self.assertRaises(BufferError, getargs_s_star, NONCONTIG_WRITABLE) + self.assertRaises(BufferError, getargs_s_star, NONCONTIG_READONLY) def test_s_hash(self): from _testcapi import getargs_s_hash @@ -901,6 +912,9 @@ def test_s_hash(self): self.assertRaises(TypeError, getargs_s_hash, bytearray(b'bytearray')) self.assertRaises(TypeError, getargs_s_hash, memoryview(b'memoryview')) self.assertRaises(TypeError, getargs_s_hash, None) + # TypeError: must be read-only bytes-like object, not memoryview + self.assertRaises(TypeError, getargs_s_hash, NONCONTIG_WRITABLE) + self.assertRaises(TypeError, getargs_s_hash, NONCONTIG_READONLY) def test_s_hash_int(self): # "s#" without PY_SSIZE_T_CLEAN defined. @@ -936,6 +950,8 @@ def test_z_star(self): self.assertEqual(getargs_z_star(bytearray(b'bytearray')), b'bytearray') self.assertEqual(getargs_z_star(memoryview(b'memoryview')), b'memoryview') self.assertIsNone(getargs_z_star(None)) + self.assertRaises(BufferError, getargs_z_star, NONCONTIG_WRITABLE) + self.assertRaises(BufferError, getargs_z_star, NONCONTIG_READONLY) def test_z_hash(self): from _testcapi import getargs_z_hash @@ -945,6 +961,9 @@ def test_z_hash(self): self.assertRaises(TypeError, getargs_z_hash, bytearray(b'bytearray')) self.assertRaises(TypeError, getargs_z_hash, memoryview(b'memoryview')) self.assertIsNone(getargs_z_hash(None)) + # TypeError: must be read-only bytes-like object, not memoryview + self.assertRaises(TypeError, getargs_z_hash, NONCONTIG_WRITABLE) + self.assertRaises(TypeError, getargs_z_hash, NONCONTIG_READONLY) def test_es(self): from _testcapi import getargs_es diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index c98f7679a9f06e..b353f3265cbb9e 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -1952,6 +1952,10 @@ def test_buffer_types(self): self.assertEqual(bio.read(), b'bar') bio.write(memoryview(b'baz')) self.assertEqual(bio.read(), b'baz') + m = memoryview(bytearray(b'noncontig')) + noncontig_writable = m[::-2] + with self.assertRaises(BufferError): + bio.write(memoryview(noncontig_writable)) def test_error_types(self): bio = ssl.MemoryBIO() From 135d5c58405fd4b4d6310206f2b63e3da7b1e69c Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 23 Oct 2023 17:49:17 +0200 Subject: [PATCH 521/632] [3.11] gh-106310 - document the __signature__ attribute (GH-106311) (#111146) Co-authored-by: Gouvernathor <44340603+Gouvernathor@users.noreply.github.com> Co-authored-by: Alex Waygood --- Doc/library/inspect.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index 22637a051d9fa6..e95196376bb24b 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -619,6 +619,9 @@ function. Accepts a wide range of Python callables, from plain functions and classes to :func:`functools.partial` objects. + If the passed object has a ``__signature__`` attribute, this function + returns it without further computations. + For objects defined in modules using stringized annotations (``from __future__ import annotations``), :func:`signature` will attempt to automatically un-stringize the annotations using @@ -738,6 +741,8 @@ function. sig = MySignature.from_callable(min) assert isinstance(sig, MySignature) + Its behavior is otherwise identical to that of :func:`signature`. + .. versionadded:: 3.5 .. versionadded:: 3.10 From a449a70bcbd9fd0eeccfad1ff852ede03978d691 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 23 Oct 2023 18:04:52 +0200 Subject: [PATCH 522/632] [3.11] typo: missing line of output in pull parser example (GH-111068) (#111218) Co-authored-by: Don Patterson <37046246+don-patterson@users.noreply.github.com> --- Doc/library/xml.etree.elementtree.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Doc/library/xml.etree.elementtree.rst b/Doc/library/xml.etree.elementtree.rst index 76c5b89ac66316..7fd4c7a4a5da2b 100644 --- a/Doc/library/xml.etree.elementtree.rst +++ b/Doc/library/xml.etree.elementtree.rst @@ -154,6 +154,7 @@ elements, call :meth:`XMLPullParser.read_events`. Here is an example:: ... print(elem.tag, 'text=', elem.text) ... end + mytag text= sometext more text The obvious use case is applications that operate in a non-blocking fashion where the XML data is being received from a socket or read incrementally from From 09bd752d9461b9104870ccc843b6e07b2f5a1467 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 23 Oct 2023 18:09:56 +0200 Subject: [PATCH 523/632] [3.11] Add a version added note for PY_VECTORCALL_ARGUMENTS_OFFSET (GH-110963) (#111220) Co-authored-by: Anthony Shaw --- Doc/c-api/call.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Doc/c-api/call.rst b/Doc/c-api/call.rst index 838af604dca020..df17c2207e94fd 100644 --- a/Doc/c-api/call.rst +++ b/Doc/c-api/call.rst @@ -99,6 +99,8 @@ This is a pointer to a function with the following signature: Doing so will allow callables such as bound methods to make their onward calls (which include a prepended *self* argument) very efficiently. + .. versionadded:: 3.8 + To call an object that implements vectorcall, use a :ref:`call API ` function as with any other callable. :c:func:`PyObject_Vectorcall` will usually be most efficient. From bf41dcda703210c1590bca60032636d526487a95 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 23 Oct 2023 21:18:00 +0200 Subject: [PATCH 524/632] [3.11] gh-101100: Fix Sphinx warnings for `fileno` (GH-111118) (#111227) Co-authored-by: Hugo van Kemenade --- Doc/library/bz2.rst | 48 +++++++++++++++++++++++++++++---- Doc/library/mmap.rst | 2 +- Doc/library/multiprocessing.rst | 2 +- Doc/library/selectors.rst | 2 +- Doc/library/socket.rst | 2 +- Doc/library/tempfile.rst | 2 +- Doc/tools/.nitignore | 2 -- Doc/whatsnew/2.5.rst | 2 +- 8 files changed, 49 insertions(+), 13 deletions(-) diff --git a/Doc/library/bz2.rst b/Doc/library/bz2.rst index d03f2c4f0ca97a..51b1da89683ead 100644 --- a/Doc/library/bz2.rst +++ b/Doc/library/bz2.rst @@ -91,7 +91,7 @@ The :mod:`bz2` module contains: and :meth:`~io.IOBase.truncate`. Iteration and the :keyword:`with` statement are supported. - :class:`BZ2File` also provides the following method: + :class:`BZ2File` also provides the following methods: .. method:: peek([n]) @@ -106,14 +106,52 @@ The :mod:`bz2` module contains: .. versionadded:: 3.3 + .. method:: fileno() + + Return the file descriptor for the underlying file. + + .. versionadded:: 3.3 + + .. method:: readable() + + Return whether the file was opened for reading. + + .. versionadded:: 3.3 + + .. method:: seekable() + + Return whether the file supports seeking. + + .. versionadded:: 3.3 + + .. method:: writable() + + Return whether the file was opened for writing. + + .. versionadded:: 3.3 + + .. method:: read1(size=-1) + + Read up to *size* uncompressed bytes, while trying to avoid + making multiple reads from the underlying stream. Reads up to a + buffer's worth of data if size is negative. + + Returns ``b''`` if the file is at EOF. + + .. versionadded:: 3.3 + + .. method:: readinto(b) + + Read bytes into *b*. + + Returns the number of bytes read (0 for EOF). + + .. versionadded:: 3.3 + .. versionchanged:: 3.1 Support for the :keyword:`with` statement was added. - .. versionchanged:: 3.3 - The :meth:`fileno`, :meth:`readable`, :meth:`seekable`, :meth:`writable`, - :meth:`read1` and :meth:`readinto` methods were added. - .. versionchanged:: 3.3 Support was added for *filename* being a :term:`file object` instead of an actual filename. diff --git a/Doc/library/mmap.rst b/Doc/library/mmap.rst index c4f8781f2ac993..21fa97a2a3cef8 100644 --- a/Doc/library/mmap.rst +++ b/Doc/library/mmap.rst @@ -19,7 +19,7 @@ the current file position, and :meth:`seek` through the file to different positi A memory-mapped file is created by the :class:`~mmap.mmap` constructor, which is different on Unix and on Windows. In either case you must provide a file descriptor for a file opened for update. If you wish to map an existing Python -file object, use its :meth:`fileno` method to obtain the correct value for the +file object, use its :meth:`~io.IOBase.fileno` method to obtain the correct value for the *fileno* parameter. Otherwise, you can open the file using the :func:`os.open` function, which returns a file descriptor directly (the file still needs to be closed when done). diff --git a/Doc/library/multiprocessing.rst b/Doc/library/multiprocessing.rst index 2162328be9adb2..99dda5b5768fbd 100644 --- a/Doc/library/multiprocessing.rst +++ b/Doc/library/multiprocessing.rst @@ -2540,7 +2540,7 @@ multiple connections at the same time. **Windows**: An item in *object_list* must either be an integer handle which is waitable (according to the definition used by the documentation of the Win32 function ``WaitForMultipleObjects()``) - or it can be an object with a :meth:`fileno` method which returns a + or it can be an object with a :meth:`~io.IOBase.fileno` method which returns a socket handle or pipe handle. (Note that pipe handles and socket handles are **not** waitable handles.) diff --git a/Doc/library/selectors.rst b/Doc/library/selectors.rst index dd50bac37e49b8..76cbf91412f763 100644 --- a/Doc/library/selectors.rst +++ b/Doc/library/selectors.rst @@ -21,7 +21,7 @@ It defines a :class:`BaseSelector` abstract base class, along with several concrete implementations (:class:`KqueueSelector`, :class:`EpollSelector`...), that can be used to wait for I/O readiness notification on multiple file objects. In the following, "file object" refers to any object with a -:meth:`fileno()` method, or a raw file descriptor. See :term:`file object`. +:meth:`~io.IOBase.fileno` method, or a raw file descriptor. See :term:`file object`. :class:`DefaultSelector` is an alias to the most efficient implementation available on the current platform: this should be the default choice for most diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index c7274f59f43d1a..7193713984f169 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -759,7 +759,7 @@ The following functions all create :ref:`socket objects `. .. function:: fromfd(fd, family, type, proto=0) Duplicate the file descriptor *fd* (an integer as returned by a file object's - :meth:`fileno` method) and build a socket object from the result. Address + :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 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. diff --git a/Doc/library/tempfile.rst b/Doc/library/tempfile.rst index 84a6f8887954cc..54096f083fd98b 100644 --- a/Doc/library/tempfile.rst +++ b/Doc/library/tempfile.rst @@ -103,7 +103,7 @@ The module defines the following user-callable items: This class operates exactly as :func:`TemporaryFile` does, except that data is spooled in memory until the file size exceeds *max_size*, or - until the file's :func:`fileno` method is called, at which point the + until the file's :func:`~io.IOBase.fileno` method is called, at which point the contents are written to disk and operation proceeds as with :func:`TemporaryFile`. diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 8fd7bcab8b2dff..5dc8bdc61f6ce5 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -39,7 +39,6 @@ Doc/library/asyncio-subprocess.rst Doc/library/asyncio-task.rst Doc/library/bdb.rst Doc/library/bisect.rst -Doc/library/bz2.rst Doc/library/calendar.rst Doc/library/cmd.rst Doc/library/collections.abc.rst @@ -104,7 +103,6 @@ Doc/library/reprlib.rst Doc/library/resource.rst Doc/library/rlcompleter.rst Doc/library/select.rst -Doc/library/selectors.rst Doc/library/shelve.rst Doc/library/signal.rst Doc/library/smtplib.rst diff --git a/Doc/whatsnew/2.5.rst b/Doc/whatsnew/2.5.rst index ad0931ecbed060..3608153db073a6 100644 --- a/Doc/whatsnew/2.5.rst +++ b/Doc/whatsnew/2.5.rst @@ -1347,7 +1347,7 @@ complete list of changes, or look through the SVN logs for all the details. :func:`input` function to allow opening files in binary or :term:`universal newlines` mode. Another new parameter, *openhook*, lets you use a function other than :func:`open` to open the input files. Once you're iterating over - the set of files, the :class:`FileInput` object's new :meth:`fileno` returns + the set of files, the :class:`FileInput` object's new :meth:`~fileinput.fileno` returns the file descriptor for the currently opened file. (Contributed by Georg Brandl.) From e35393fde91fba1157b57df0c0e0501b72705085 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 24 Oct 2023 08:35:49 +0200 Subject: [PATCH 525/632] [3.11] Fix a code snippet typo in asyncio docs (GH-108427) (#111244) Co-authored-by: A <5249513+Dumeng@users.noreply.github.com> --- Doc/library/asyncio-task.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst index 0944aef0ab619d..858c2996b87695 100644 --- a/Doc/library/asyncio-task.rst +++ b/Doc/library/asyncio-task.rst @@ -539,7 +539,7 @@ Shielding From Cancellation is equivalent to:: - res = await something() + res = await shield(something()) *except* that if the coroutine containing it is cancelled, the Task running in ``something()`` is not cancelled. From the point From 10376a164f31409e8cedb140178d4670fb44f8c1 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 24 Oct 2023 10:15:03 +0200 Subject: [PATCH 526/632] [3.11] Fix typo in sys docs (GH-111196) (#111249) Co-authored-by: James Tocknell Co-authored-by: Hugo van Kemenade --- Doc/library/sys.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 65c20748f862b5..064a9c47137b30 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -1714,7 +1714,7 @@ always available. However, if you are writing a library (and do not control in which context its code will be executed), be aware that the standard streams may be replaced with file-like objects like :class:`io.StringIO` which - do not support the :attr:!buffer` attribute. + do not support the :attr:`!buffer` attribute. .. data:: __stdin__ From c905fab33871df2c250f1e37760f6d541672a566 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 24 Oct 2023 13:53:38 +0200 Subject: [PATCH 527/632] [3.11] gh-75666: Tkinter: add tests for binding (GH-111202) (GH-111256) (cherry picked from commit 9bb202a1a90ef0edce20c495c9426d9766df11bb) Co-authored-by: Serhiy Storchaka --- Lib/tkinter/test/test_tkinter/test_misc.py | 307 +++++++++++++++++++++ 1 file changed, 307 insertions(+) diff --git a/Lib/tkinter/test/test_tkinter/test_misc.py b/Lib/tkinter/test/test_tkinter/test_misc.py index 620b6ed638c25a..c772bb804639b8 100644 --- a/Lib/tkinter/test/test_tkinter/test_misc.py +++ b/Lib/tkinter/test/test_tkinter/test_misc.py @@ -371,6 +371,309 @@ def test_info_patchlevel(self): self.assertTrue(str(vi).startswith(f'{vi.major}.{vi.minor}')) +class BindTest(AbstractTkTest, unittest.TestCase): + + def setUp(self): + super().setUp() + root = self.root + self.frame = tkinter.Frame(self.root, class_='Test', + width=150, height=100) + self.frame.pack() + + def assertCommandExist(self, funcid): + self.assertEqual(_info_commands(self.root, funcid), (funcid,)) + + def assertCommandNotExist(self, funcid): + self.assertEqual(_info_commands(self.root, funcid), ()) + + def test_bind(self): + event = '' + f = self.frame + self.assertEqual(f.bind(), ()) + self.assertEqual(f.bind(event), '') + def test1(e): pass + def test2(e): pass + + funcid = f.bind(event, test1) + self.assertEqual(f.bind(), (event,)) + script = f.bind(event) + self.assertIn(funcid, script) + self.assertCommandExist(funcid) + + funcid2 = f.bind(event, test2, add=True) + script = f.bind(event) + self.assertIn(funcid, script) + self.assertIn(funcid2, script) + self.assertCommandExist(funcid) + self.assertCommandExist(funcid2) + + def test_unbind(self): + event = '' + f = self.frame + self.assertEqual(f.bind(), ()) + self.assertEqual(f.bind(event), '') + def test1(e): pass + def test2(e): pass + + funcid = f.bind(event, test1) + funcid2 = f.bind(event, test2, add=True) + + self.assertRaises(TypeError, f.unbind) + f.unbind(event) + self.assertEqual(f.bind(event), '') + self.assertEqual(f.bind(), ()) + + def test_unbind2(self): + f = self.frame + event = '' + self.assertEqual(f.bind(), ()) + self.assertEqual(f.bind(event), '') + def test1(e): pass + def test2(e): pass + + funcid = f.bind(event, test1) + funcid2 = f.bind(event, test2, add=True) + + f.unbind(event, funcid) + script = f.bind(event) + self.assertNotIn(funcid, script) + self.assertCommandNotExist(funcid) + self.assertCommandExist(funcid2) + + f.unbind(event, funcid2) + self.assertEqual(f.bind(event), '') + self.assertEqual(f.bind(), ()) + self.assertCommandNotExist(funcid) + self.assertCommandNotExist(funcid2) + + # non-idempotent + self.assertRaises(tkinter.TclError, f.unbind, event, funcid2) + + def test_bind_rebind(self): + event = '' + f = self.frame + self.assertEqual(f.bind(), ()) + self.assertEqual(f.bind(event), '') + def test1(e): pass + def test2(e): pass + def test3(e): pass + + funcid = f.bind(event, test1) + funcid2 = f.bind(event, test2, add=True) + script = f.bind(event) + self.assertIn(funcid2, script) + self.assertIn(funcid, script) + self.assertCommandExist(funcid) + self.assertCommandExist(funcid2) + + funcid3 = f.bind(event, test3) + script = f.bind(event) + self.assertNotIn(funcid, script) + self.assertNotIn(funcid2, script) + self.assertIn(funcid3, script) + self.assertCommandExist(funcid3) + + def test_bind_class(self): + event = '' + bind_class = self.root.bind_class + unbind_class = self.root.unbind_class + self.assertRaises(TypeError, bind_class) + self.assertEqual(bind_class('Test'), ()) + self.assertEqual(bind_class('Test', event), '') + self.addCleanup(unbind_class, 'Test', event) + def test1(e): pass + def test2(e): pass + + funcid = bind_class('Test', event, test1) + self.assertEqual(bind_class('Test'), (event,)) + script = bind_class('Test', event) + self.assertIn(funcid, script) + self.assertCommandExist(funcid) + + funcid2 = bind_class('Test', event, test2, add=True) + script = bind_class('Test', event) + self.assertIn(funcid, script) + self.assertIn(funcid2, script) + self.assertCommandExist(funcid) + self.assertCommandExist(funcid2) + + def test_unbind_class(self): + event = '' + bind_class = self.root.bind_class + unbind_class = self.root.unbind_class + self.assertEqual(bind_class('Test'), ()) + self.assertEqual(bind_class('Test', event), '') + self.addCleanup(unbind_class, 'Test', event) + def test1(e): pass + def test2(e): pass + + funcid = bind_class('Test', event, test1) + funcid2 = bind_class('Test', event, test2, add=True) + + self.assertRaises(TypeError, unbind_class) + self.assertRaises(TypeError, unbind_class, 'Test') + unbind_class('Test', event) + self.assertEqual(bind_class('Test', event), '') + self.assertEqual(bind_class('Test'), ()) + self.assertCommandExist(funcid) + self.assertCommandExist(funcid2) + + unbind_class('Test', event) # idempotent + + def test_bind_class_rebind(self): + event = '' + bind_class = self.root.bind_class + unbind_class = self.root.unbind_class + self.assertEqual(bind_class('Test'), ()) + self.assertEqual(bind_class('Test', event), '') + self.addCleanup(unbind_class, 'Test', event) + def test1(e): pass + def test2(e): pass + def test3(e): pass + + funcid = bind_class('Test', event, test1) + funcid2 = bind_class('Test', event, test2, add=True) + script = bind_class('Test', event) + self.assertIn(funcid2, script) + self.assertIn(funcid, script) + self.assertCommandExist(funcid) + self.assertCommandExist(funcid2) + + funcid3 = bind_class('Test', event, test3) + script = bind_class('Test', event) + self.assertNotIn(funcid, script) + self.assertNotIn(funcid2, script) + self.assertIn(funcid3, script) + self.assertCommandExist(funcid) + self.assertCommandExist(funcid2) + self.assertCommandExist(funcid3) + + def test_bind_all(self): + event = '' + bind_all = self.root.bind_all + unbind_all = self.root.unbind_all + self.assertNotIn(event, bind_all()) + self.assertEqual(bind_all(event), '') + self.addCleanup(unbind_all, event) + def test1(e): pass + def test2(e): pass + + funcid = bind_all(event, test1) + self.assertIn(event, bind_all()) + script = bind_all(event) + self.assertIn(funcid, script) + self.assertCommandExist(funcid) + + funcid2 = bind_all(event, test2, add=True) + script = bind_all(event) + self.assertIn(funcid, script) + self.assertIn(funcid2, script) + self.assertCommandExist(funcid) + self.assertCommandExist(funcid2) + + def test_unbind_all(self): + event = '' + bind_all = self.root.bind_all + unbind_all = self.root.unbind_all + self.assertNotIn(event, bind_all()) + self.assertEqual(bind_all(event), '') + self.addCleanup(unbind_all, event) + def test1(e): pass + def test2(e): pass + + funcid = bind_all(event, test1) + funcid2 = bind_all(event, test2, add=True) + + unbind_all(event) + self.assertEqual(bind_all(event), '') + self.assertNotIn(event, bind_all()) + self.assertCommandExist(funcid) + self.assertCommandExist(funcid2) + + unbind_all(event) # idempotent + + def test_bind_all_rebind(self): + event = '' + bind_all = self.root.bind_all + unbind_all = self.root.unbind_all + self.assertNotIn(event, bind_all()) + self.assertEqual(bind_all(event), '') + self.addCleanup(unbind_all, event) + def test1(e): pass + def test2(e): pass + def test3(e): pass + + funcid = bind_all(event, test1) + funcid2 = bind_all(event, test2, add=True) + script = bind_all(event) + self.assertIn(funcid2, script) + self.assertIn(funcid, script) + self.assertCommandExist(funcid) + self.assertCommandExist(funcid2) + + funcid3 = bind_all(event, test3) + script = bind_all(event) + self.assertNotIn(funcid, script) + self.assertNotIn(funcid2, script) + self.assertIn(funcid3, script) + self.assertCommandExist(funcid) + self.assertCommandExist(funcid2) + self.assertCommandExist(funcid3) + + def test_bindtags(self): + f = self.frame + self.assertEqual(self.root.bindtags(), ('.', 'Tk', 'all')) + self.assertEqual(f.bindtags(), (str(f), 'Test', '.', 'all')) + f.bindtags(('a', 'b c')) + self.assertEqual(f.bindtags(), ('a', 'b c')) + + def test_bind_events(self): + event = '' + root = self.root + t = tkinter.Toplevel(root) + f = tkinter.Frame(t, class_='Test', width=150, height=100) + f.pack() + root.wait_visibility() # needed on Windows + root.update_idletasks() + self.addCleanup(root.unbind_class, 'Test', event) + self.addCleanup(root.unbind_class, 'Toplevel', event) + self.addCleanup(root.unbind_class, 'tag', event) + self.addCleanup(root.unbind_class, 'tag2', event) + self.addCleanup(root.unbind_all, event) + def test(what): + return lambda e: events.append((what, e.widget)) + + root.bind_all(event, test('all')) + root.bind_class('Test', event, test('frame class')) + root.bind_class('Toplevel', event, test('toplevel class')) + root.bind_class('tag', event, test('tag')) + root.bind_class('tag2', event, test('tag2')) + f.bind(event, test('frame')) + t.bind(event, test('toplevel')) + + events = [] + f.event_generate(event) + self.assertEqual(events, [ + ('frame', f), + ('frame class', f), + ('toplevel', f), + ('all', f), + ]) + + events = [] + t.event_generate(event) + self.assertEqual(events, [ + ('toplevel', t), + ('toplevel class', t), + ('all', t), + ]) + + f.bindtags(('tag', 'tag3')) + events = [] + f.event_generate(event) + self.assertEqual(events, [('tag', f)]) + + class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): def test_default_root(self): @@ -426,5 +729,9 @@ def test_mainloop(self): self.assertRaises(RuntimeError, tkinter.mainloop) +def _info_commands(widget, pattern=None): + return widget.tk.splitlist(widget.tk.call('info', 'commands', pattern)) + + if __name__ == "__main__": unittest.main() From 575bff3732130e7470b0d005f36c904f8f7db92d Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Tue, 24 Oct 2023 08:34:46 -0700 Subject: [PATCH 528/632] [3.11] gh-111151: Convert monospaced directives to :ref: (GH-111152) (#111270) (cherry picked from commit 1198076447f35b19a9173866ccb9839f3bcf3f17) Co-authored-by: InSync <122007197+InSyncWithFoo@users.noreply.github.com> --- Doc/library/asyncio-eventloop.rst | 6 ++++++ Doc/library/asyncio.rst | 6 +++--- Doc/library/typing.rst | 14 ++++++++++---- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index aeebc3bae85e13..5e40b50056d65a 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -644,6 +644,8 @@ Opening network connections Creating network servers ^^^^^^^^^^^^^^^^^^^^^^^^ +.. _loop_create_server: + .. coroutinemethod:: loop.create_server(protocol_factory, \ host=None, port=None, *, \ family=socket.AF_UNSPEC, \ @@ -1174,6 +1176,8 @@ Working with pipes Unix signals ^^^^^^^^^^^^ +.. _loop_add_signal_handler: + .. method:: loop.add_signal_handler(signum, callback, *args) Set *callback* as the handler for the *signum* signal. @@ -1393,6 +1397,8 @@ async/await code consider using the high-level :ref:`Subprocess Support on Windows ` for details. +.. _loop_subprocess_exec: + .. coroutinemethod:: loop.subprocess_exec(protocol_factory, *args, \ stdin=subprocess.PIPE, stdout=subprocess.PIPE, \ stderr=subprocess.PIPE, **kwargs) diff --git a/Doc/library/asyncio.rst b/Doc/library/asyncio.rst index c75ab47404c1e4..5f33c6813e74c0 100644 --- a/Doc/library/asyncio.rst +++ b/Doc/library/asyncio.rst @@ -46,9 +46,9 @@ Additionally, there are **low-level** APIs for *library and framework developers* to: * create and manage :ref:`event loops `, which - provide asynchronous APIs for :meth:`networking `, - running :meth:`subprocesses `, - handling :meth:`OS signals `, etc; + provide asynchronous APIs for :ref:`networking `, + running :ref:`subprocesses `, + handling :ref:`OS signals `, etc; * implement efficient protocols using :ref:`transports `; diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 57384b05fe23c9..0decbaf39749c0 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -290,7 +290,7 @@ a callable with any arbitrary parameter list would be acceptable: x = concat # Also OK ``Callable`` cannot express complex signatures such as functions that take a -variadic number of arguments, :func:`overloaded functions `, or +variadic number of arguments, :ref:`overloaded functions `, or functions that have keyword-only parameters. However, these signatures can be expressed by defining a :class:`Protocol` class with a :meth:`~object.__call__` method: @@ -1424,7 +1424,7 @@ These can be used as types in annotations. They all support subscription using Typing operator to conceptually mark an object as having been unpacked. For example, using the unpack operator ``*`` on a - :class:`type variable tuple ` is equivalent to using ``Unpack`` + :ref:`type variable tuple ` is equivalent to using ``Unpack`` to mark the type variable tuple as having been unpacked:: Ts = TypeVarTuple('Ts') @@ -1479,6 +1479,8 @@ for creating generic types. except KeyError: return default +.. _typevar: + .. class:: TypeVar(name, *constraints, bound=None, covariant=False, contravariant=False) Type variable. @@ -1573,9 +1575,11 @@ for creating generic types. A tuple containing the constraints of the type variable, if any. +.. _typevartuple: + .. class:: TypeVarTuple(name) - Type variable tuple. A specialized form of :class:`type variable ` + Type variable tuple. A specialized form of :ref:`type variable ` that enables *variadic* generics. Usage:: @@ -1686,7 +1690,7 @@ for creating generic types. .. class:: ParamSpec(name, *, bound=None, covariant=False, contravariant=False) Parameter specification variable. A specialized version of - :class:`type variables `. + :ref:`type variables `. Usage:: @@ -2483,6 +2487,8 @@ Functions and decorators .. versionadded:: 3.11 +.. _overload: + .. decorator:: overload Decorator for creating overloaded functions and methods. From ff7dc61643ce1640befdff451b4a05e0eae02c31 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 24 Oct 2023 18:26:57 +0200 Subject: [PATCH 529/632] [3.11] Revert "Fix a code snippet typo in asyncio docs (GH-108427)" (GH-111271) (GH-111273) Revert "Fix a code snippet typo in asyncio docs (GH-108427)" (GH-111271) This reverts commit 7f316763402a7d5556deecc3acd06cb719e189b3. The change resulted in a tautology and should not have been made. There may be an opportunity for additional clarity in this section, but this change wasn't it :) (cherry picked from commit c7d68f907ad3e3aa17546df92a32bddb145a69bf) Ref: https://github.com/python/cpython/pull/108427#-issuecomment-1777525740 Co-authored-by: Zachary Ware --- Doc/library/asyncio-task.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst index 858c2996b87695..0944aef0ab619d 100644 --- a/Doc/library/asyncio-task.rst +++ b/Doc/library/asyncio-task.rst @@ -539,7 +539,7 @@ Shielding From Cancellation is equivalent to:: - res = await shield(something()) + res = await something() *except* that if the coroutine containing it is cancelled, the Task running in ``something()`` is not cancelled. From the point From 652d3f33fb7c9f8cf5fe20bb27230afac8a0d5aa Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 24 Oct 2023 22:56:02 +0200 Subject: [PATCH 530/632] [3.11] gh-109017: Use non alternate name for Kyiv (GH-109251) (GH-111279) tzdata provides Kiev as an alternative to Kyiv: https://sources.debian.org/src/tzdata/2023c-10/backward/?hl=314GH-L314 But Debian moved it to the tzdata-legacy package breaking the test: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1050530 This patch switches to the name provided by tzdata. Also check that the new name is actually available. (cherry picked from commit 46407fe79ca78051cbf6c80e8b8e70a228f9fa50) Co-authored-by: Jochen Sprickerhof --- Lib/test/test_email/test_utils.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_email/test_utils.py b/Lib/test/test_email/test_utils.py index 78afb358035e81..81109a7508ac0d 100644 --- a/Lib/test/test_email/test_utils.py +++ b/Lib/test/test_email/test_utils.py @@ -5,6 +5,7 @@ import unittest import sys import os.path +import zoneinfo class DateTimeTests(unittest.TestCase): @@ -142,13 +143,9 @@ def test_localtime_epoch_notz_daylight_false(self): t2 = utils.localtime(t0.replace(tzinfo=None)) self.assertEqual(t1, t2) - # XXX: Need a more robust test for Olson's tzdata - @unittest.skipIf(sys.platform.startswith('win'), - "Windows does not use Olson's TZ database") - @unittest.skipUnless(os.path.exists('/usr/share/zoneinfo') or - os.path.exists('/usr/lib/zoneinfo'), - "Can't find the Olson's TZ database") - @test.support.run_with_tz('Europe/Kiev') + @unittest.skipUnless("Europe/Kyiv" in zoneinfo.available_timezones(), + "Can't find a Kyiv timezone database") + @test.support.run_with_tz('Europe/Kyiv') def test_variable_tzname(self): t0 = datetime.datetime(1984, 1, 1, tzinfo=datetime.timezone.utc) t1 = utils.localtime(t0) From d38843a7b6825530a6d705132aabfb5325c498d4 Mon Sep 17 00:00:00 2001 From: Artyom Romanov <92092049+art3xa@users.noreply.github.com> Date: Wed, 25 Oct 2023 15:33:09 +0500 Subject: [PATCH 531/632] [3.11] Bump `ruff` to the latest version (GH-111288) (#111310) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fd2692e490bec3..e80c5075401a99 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.0 + rev: v0.1.2 hooks: - id: ruff name: Run Ruff on Lib/test/ From bb92fdabc7ab9d77453452d5e61f8cc4ca167616 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 25 Oct 2023 13:18:30 +0200 Subject: [PATCH 532/632] [3.11] gh-111174: Fix crash in getbuffer() called repeatedly for empty BytesIO (GH-111210) (GH-111315) (cherry picked from commit 9da98c0d9a7cc55c67fb0bd3fa162fd3b2c2629b) Co-authored-by: Serhiy Storchaka --- Lib/test/test_memoryio.py | 14 ++++++++++++++ .../2023-10-23-13-53-58.gh-issue-111174.Oohmzd.rst | 2 ++ Modules/_io/bytesio.c | 7 ++++--- 3 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-10-23-13-53-58.gh-issue-111174.Oohmzd.rst diff --git a/Lib/test/test_memoryio.py b/Lib/test/test_memoryio.py index cd2faba1791c77..731299294e6877 100644 --- a/Lib/test/test_memoryio.py +++ b/Lib/test/test_memoryio.py @@ -463,6 +463,20 @@ def test_getbuffer(self): memio.close() self.assertRaises(ValueError, memio.getbuffer) + def test_getbuffer_empty(self): + memio = self.ioclass() + buf = memio.getbuffer() + self.assertEqual(bytes(buf), b"") + # Trying to change the size of the BytesIO while a buffer is exported + # raises a BufferError. + self.assertRaises(BufferError, memio.write, b'x') + buf2 = memio.getbuffer() + self.assertRaises(BufferError, memio.write, b'x') + buf.release() + self.assertRaises(BufferError, memio.write, b'x') + buf2.release() + memio.write(b'x') + def test_read1(self): buf = self.buftype("1234567890") self.assertEqual(self.ioclass(buf).read1(), buf) 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 new file mode 100644 index 00000000000000..95c315404d0ee6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-10-23-13-53-58.gh-issue-111174.Oohmzd.rst @@ -0,0 +1,2 @@ +Fix crash in :meth:`io.BytesIO.getbuffer` called repeatedly for empty +BytesIO. diff --git a/Modules/_io/bytesio.c b/Modules/_io/bytesio.c index 930ef7e29dbcf6..e84eef4c75961e 100644 --- a/Modules/_io/bytesio.c +++ b/Modules/_io/bytesio.c @@ -124,12 +124,13 @@ unshare_buffer(bytesio *self, size_t size) static int resize_buffer(bytesio *self, size_t size) { + assert(self->buf != NULL); + assert(self->exports == 0); + /* Here, unsigned types are used to avoid dealing with signed integer overflow, which is undefined in C. */ size_t alloc = PyBytes_GET_SIZE(self->buf); - assert(self->buf != NULL); - /* For simplicity, stay in the range of the signed type. Anyway, Python doesn't allow strings to be longer than this. */ if (size > PY_SSIZE_T_MAX) @@ -1083,7 +1084,7 @@ bytesiobuf_getbuffer(bytesiobuf *obj, Py_buffer *view, int flags) "bytesiobuf_getbuffer: view==NULL argument is obsolete"); return -1; } - if (SHARED_BUF(b)) { + if (b->exports == 0 && SHARED_BUF(b)) { if (unshare_buffer(b, b->string_size) < 0) return -1; } From 14167031eb035afddd82a114ec46487f0e142c65 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 25 Oct 2023 14:57:17 +0300 Subject: [PATCH 533/632] [3.11] gh-111309: Use unittest to collect and run distutils tests (GH-111311) * use unittest.main() instead of run_unittest(test_suite()) to run tests from modules via the CLI * add explicit load_tests() to load doctests * use test.support.load_package_tests() to load tests in submodules of distutils.tests * removes no longer needed test_suite() functions --- Lib/distutils/tests/__init__.py | 36 +++++++------------ Lib/distutils/tests/test_archive_util.py | 7 ++-- Lib/distutils/tests/test_bdist.py | 7 +--- Lib/distutils/tests/test_bdist_dumb.py | 6 +--- Lib/distutils/tests/test_bdist_rpm.py | 7 ++-- Lib/distutils/tests/test_build.py | 6 +--- Lib/distutils/tests/test_build_clib.py | 7 ++-- Lib/distutils/tests/test_build_ext.py | 8 +---- Lib/distutils/tests/test_build_py.py | 7 ++-- Lib/distutils/tests/test_build_scripts.py | 6 +--- Lib/distutils/tests/test_check.py | 6 +--- Lib/distutils/tests/test_clean.py | 6 +--- Lib/distutils/tests/test_cmd.py | 7 ++-- Lib/distutils/tests/test_config.py | 6 +--- Lib/distutils/tests/test_config_cmd.py | 7 ++-- Lib/distutils/tests/test_core.py | 7 ++-- Lib/distutils/tests/test_cygwinccompiler.py | 6 +--- Lib/distutils/tests/test_dep_util.py | 6 +--- Lib/distutils/tests/test_dir_util.py | 7 ++-- Lib/distutils/tests/test_dist.py | 10 ++---- Lib/distutils/tests/test_extension.py | 6 +--- Lib/distutils/tests/test_file_util.py | 6 +--- Lib/distutils/tests/test_filelist.py | 11 ++---- Lib/distutils/tests/test_install.py | 7 ++-- Lib/distutils/tests/test_install_data.py | 6 +--- Lib/distutils/tests/test_install_headers.py | 6 +--- Lib/distutils/tests/test_install_lib.py | 7 ++-- Lib/distutils/tests/test_install_scripts.py | 6 +--- Lib/distutils/tests/test_log.py | 7 ++-- Lib/distutils/tests/test_msvc9compiler.py | 6 +--- Lib/distutils/tests/test_msvccompiler.py | 6 +--- Lib/distutils/tests/test_register.py | 6 +--- Lib/distutils/tests/test_sdist.py | 7 ++-- Lib/distutils/tests/test_spawn.py | 7 ++-- Lib/distutils/tests/test_sysconfig.py | 10 ++---- Lib/distutils/tests/test_text_file.py | 6 +--- Lib/distutils/tests/test_unixccompiler.py | 6 +--- Lib/distutils/tests/test_upload.py | 6 +--- Lib/distutils/tests/test_util.py | 6 +--- Lib/distutils/tests/test_version.py | 6 +--- Lib/distutils/tests/test_versionpredicate.py | 8 ++--- Lib/test/test_distutils.py | 12 ++----- ...-10-25-13-13-30.gh-issue-111309.Re7orL.rst | 1 + 43 files changed, 75 insertions(+), 245 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2023-10-25-13-13-30.gh-issue-111309.Re7orL.rst diff --git a/Lib/distutils/tests/__init__.py b/Lib/distutils/tests/__init__.py index 16d011fd9ee6e7..d7922ffa011393 100644 --- a/Lib/distutils/tests/__init__.py +++ b/Lib/distutils/tests/__init__.py @@ -1,9 +1,7 @@ """Test suite for distutils. This test suite consists of a collection of test modules in the -distutils.tests package. Each test module has a name starting with -'test' and contains a function test_suite(). The function is expected -to return an initialized unittest.TestSuite instance. +distutils.tests package. Tests for the command classes in the distutils.command package are included in distutils.tests as well, instead of using a separate @@ -13,29 +11,21 @@ """ import os -import sys import unittest -from test.support import run_unittest from test.support.warnings_helper import save_restore_warnings_filters +from test.support import warnings_helper +from test.support import load_package_tests -here = os.path.dirname(__file__) or os.curdir - - -def test_suite(): - suite = unittest.TestSuite() - for fn in os.listdir(here): - if fn.startswith("test") and fn.endswith(".py"): - modname = "distutils.tests." + fn[:-3] - # bpo-40055: Save/restore warnings filters to leave them unchanged. - # Importing tests imports docutils which imports pkg_resources - # which adds a warnings filter. - with save_restore_warnings_filters(): - __import__(modname) - module = sys.modules[modname] - suite.addTest(module.test_suite()) - return suite - +def load_tests(*args): + # bpo-40055: Save/restore warnings filters to leave them unchanged. + # Importing tests imports docutils which imports pkg_resources + # which adds a warnings filter. + with (save_restore_warnings_filters(), + warnings_helper.check_warnings( + ("The distutils.sysconfig module is deprecated", DeprecationWarning), + quiet=True)): + return load_package_tests(os.path.dirname(__file__), *args) if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_archive_util.py b/Lib/distutils/tests/test_archive_util.py index 8aec84078ed48f..66aee1b44b2032 100644 --- a/Lib/distutils/tests/test_archive_util.py +++ b/Lib/distutils/tests/test_archive_util.py @@ -13,7 +13,7 @@ ARCHIVE_FORMATS) from distutils.spawn import find_executable, spawn from distutils.tests import support -from test.support import run_unittest, patch +from test.support import patch from test.support.os_helper import change_cwd from test.support.warnings_helper import check_warnings @@ -389,8 +389,5 @@ def test_tarfile_root_owner(self): finally: archive.close() -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(ArchiveUtilTestCase) - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_bdist.py b/Lib/distutils/tests/test_bdist.py index 241fc9ad75f34b..c53f0ccb17239b 100644 --- a/Lib/distutils/tests/test_bdist.py +++ b/Lib/distutils/tests/test_bdist.py @@ -1,7 +1,6 @@ """Tests for distutils.command.bdist.""" import os import unittest -from test.support import run_unittest import warnings with warnings.catch_warnings(): @@ -44,9 +43,5 @@ def test_skip_build(self): '%s should take --skip-build from bdist' % name) -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(BuildTestCase) - - if __name__ == '__main__': - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_bdist_dumb.py b/Lib/distutils/tests/test_bdist_dumb.py index bb860c8ac70345..b41812bccf692a 100644 --- a/Lib/distutils/tests/test_bdist_dumb.py +++ b/Lib/distutils/tests/test_bdist_dumb.py @@ -4,7 +4,6 @@ import sys import zipfile import unittest -from test.support import run_unittest from distutils.core import Distribution from distutils.command.bdist_dumb import bdist_dumb @@ -90,8 +89,5 @@ def test_simple_built(self): wanted.append('foo.%s.pyc' % sys.implementation.cache_tag) self.assertEqual(contents, sorted(wanted)) -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(BuildDumbTestCase) - if __name__ == '__main__': - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_bdist_rpm.py b/Lib/distutils/tests/test_bdist_rpm.py index 7eefa7b9cad84f..ea9a0bc33623e8 100644 --- a/Lib/distutils/tests/test_bdist_rpm.py +++ b/Lib/distutils/tests/test_bdist_rpm.py @@ -3,7 +3,7 @@ import unittest import sys import os -from test.support import run_unittest, requires_zlib +from test.support import requires_zlib from distutils.core import Distribution from distutils.command.bdist_rpm import bdist_rpm @@ -134,8 +134,5 @@ def test_no_optimize_flag(self): os.remove(os.path.join(pkg_dir, 'dist', 'foo-0.1-1.noarch.rpm')) -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(BuildRpmTestCase) - if __name__ == '__main__': - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_build.py b/Lib/distutils/tests/test_build.py index 71b5e164bae14a..c7c564d04774db 100644 --- a/Lib/distutils/tests/test_build.py +++ b/Lib/distutils/tests/test_build.py @@ -2,7 +2,6 @@ import unittest import os import sys -from test.support import run_unittest from distutils.command.build import build from distutils.tests import support @@ -50,8 +49,5 @@ def test_finalize_options(self): # executable is os.path.normpath(sys.executable) self.assertEqual(cmd.executable, os.path.normpath(sys.executable)) -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(BuildTestCase) - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_build_clib.py b/Lib/distutils/tests/test_build_clib.py index 95f928288e0048..0c3bc49e8e9760 100644 --- a/Lib/distutils/tests/test_build_clib.py +++ b/Lib/distutils/tests/test_build_clib.py @@ -5,7 +5,7 @@ import sysconfig from test.support import ( - run_unittest, missing_compiler_executable, requires_subprocess + missing_compiler_executable, requires_subprocess ) from distutils.command.build_clib import build_clib @@ -140,8 +140,5 @@ def test_run(self): # let's check the result self.assertIn('libfoo.a', os.listdir(build_temp)) -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(BuildCLibTestCase) - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_build_ext.py b/Lib/distutils/tests/test_build_ext.py index 4ebeafecef03c3..e89dc50665be47 100644 --- a/Lib/distutils/tests/test_build_ext.py +++ b/Lib/distutils/tests/test_build_ext.py @@ -545,11 +545,5 @@ def build_ext(self, *args, **kwargs): return build_ext -def test_suite(): - suite = unittest.TestSuite() - suite.addTest(unittest.TestLoader().loadTestsFromTestCase(BuildExtTestCase)) - suite.addTest(unittest.TestLoader().loadTestsFromTestCase(ParallelBuildExtTestCase)) - return suite - if __name__ == '__main__': - support.run_unittest(__name__) + unittest.main() diff --git a/Lib/distutils/tests/test_build_py.py b/Lib/distutils/tests/test_build_py.py index 44a06cc963aa3c..a7035e5a3fd9dd 100644 --- a/Lib/distutils/tests/test_build_py.py +++ b/Lib/distutils/tests/test_build_py.py @@ -9,7 +9,7 @@ from distutils.errors import DistutilsFileError from distutils.tests import support -from test.support import run_unittest, requires_subprocess +from test.support import requires_subprocess class BuildPyTestCase(support.TempdirManager, @@ -174,8 +174,5 @@ def test_dont_write_bytecode(self): self.logs[0][1] % self.logs[0][2]) -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(BuildPyTestCase) - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_build_scripts.py b/Lib/distutils/tests/test_build_scripts.py index f299e51ef79fac..485f4f050a5dba 100644 --- a/Lib/distutils/tests/test_build_scripts.py +++ b/Lib/distutils/tests/test_build_scripts.py @@ -8,7 +8,6 @@ from distutils import sysconfig from distutils.tests import support -from test.support import run_unittest class BuildScriptsTestCase(support.TempdirManager, @@ -105,8 +104,5 @@ def test_version_int(self): for name in expected: self.assertIn(name, built) -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(BuildScriptsTestCase) - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_check.py b/Lib/distutils/tests/test_check.py index 91bcdceb43bc69..1e86b948d22449 100644 --- a/Lib/distutils/tests/test_check.py +++ b/Lib/distutils/tests/test_check.py @@ -2,7 +2,6 @@ import os import textwrap import unittest -from test.support import run_unittest from distutils.command.check import check, HAS_DOCUTILS from distutils.tests import support @@ -156,8 +155,5 @@ def test_check_all(self): {}, **{'strict': 1, 'restructuredtext': 1}) -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(CheckTestCase) - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_clean.py b/Lib/distutils/tests/test_clean.py index 92367499cefc04..ccbb01e167e1cd 100644 --- a/Lib/distutils/tests/test_clean.py +++ b/Lib/distutils/tests/test_clean.py @@ -4,7 +4,6 @@ from distutils.command.clean import clean from distutils.tests import support -from test.support import run_unittest class cleanTestCase(support.TempdirManager, support.LoggingSilencer, @@ -42,8 +41,5 @@ def test_simple_run(self): cmd.ensure_finalized() cmd.run() -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(cleanTestCase) - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_cmd.py b/Lib/distutils/tests/test_cmd.py index 2319214a9e332b..90310078b1c29a 100644 --- a/Lib/distutils/tests/test_cmd.py +++ b/Lib/distutils/tests/test_cmd.py @@ -1,7 +1,7 @@ """Tests for distutils.cmd.""" import unittest import os -from test.support import captured_stdout, run_unittest +from test.support import captured_stdout from distutils.cmd import Command from distutils.dist import Distribution @@ -119,8 +119,5 @@ def test_debug_print(self): finally: debug.DEBUG = False -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(CommandTestCase) - if __name__ == '__main__': - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_config.py b/Lib/distutils/tests/test_config.py index 8ab70efb161cbd..cb8e7237abd9e1 100644 --- a/Lib/distutils/tests/test_config.py +++ b/Lib/distutils/tests/test_config.py @@ -8,7 +8,6 @@ from distutils.log import WARN from distutils.tests import support -from test.support import run_unittest PYPIRC = """\ [distutils] @@ -134,8 +133,5 @@ def test_config_interpolation(self): self.assertEqual(config, waited) -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(PyPIRCCommandTestCase) - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_config_cmd.py b/Lib/distutils/tests/test_config_cmd.py index c79db68aae115d..4f132ce3d185cc 100644 --- a/Lib/distutils/tests/test_config_cmd.py +++ b/Lib/distutils/tests/test_config_cmd.py @@ -4,7 +4,7 @@ import sys import sysconfig from test.support import ( - run_unittest, missing_compiler_executable, requires_subprocess + missing_compiler_executable, requires_subprocess ) from distutils.command.config import dump_file, config @@ -96,8 +96,5 @@ def test_clean(self): for f in (f1, f2): self.assertFalse(os.path.exists(f)) -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(ConfigTestCase) - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_core.py b/Lib/distutils/tests/test_core.py index 700a22da045d45..59fb16bdec28cb 100644 --- a/Lib/distutils/tests/test_core.py +++ b/Lib/distutils/tests/test_core.py @@ -5,7 +5,7 @@ import os import shutil import sys -from test.support import captured_stdout, run_unittest +from test.support import captured_stdout from test.support import os_helper import unittest from distutils.tests import support @@ -133,8 +133,5 @@ def test_debug_mode(self): wanted = "options (after parsing config files):\n" self.assertEqual(stdout.readlines()[0], wanted) -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(CoreTestCase) - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_cygwinccompiler.py b/Lib/distutils/tests/test_cygwinccompiler.py index 0912ffd15c8ee9..633d3041a1bee4 100644 --- a/Lib/distutils/tests/test_cygwinccompiler.py +++ b/Lib/distutils/tests/test_cygwinccompiler.py @@ -3,7 +3,6 @@ import sys import os from io import BytesIO -from test.support import run_unittest from distutils import cygwinccompiler from distutils.cygwinccompiler import (check_config_h, @@ -147,8 +146,5 @@ def test_get_msvcr(self): '[MSC v.1999 32 bits (Intel)]') self.assertRaises(ValueError, get_msvcr) -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(CygwinCCompilerTestCase) - if __name__ == '__main__': - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_dep_util.py b/Lib/distutils/tests/test_dep_util.py index 0d52740a9edda3..ef52900b91af37 100644 --- a/Lib/distutils/tests/test_dep_util.py +++ b/Lib/distutils/tests/test_dep_util.py @@ -5,7 +5,6 @@ from distutils.dep_util import newer, newer_pairwise, newer_group from distutils.errors import DistutilsFileError from distutils.tests import support -from test.support import run_unittest class DepUtilTestCase(support.TempdirManager, unittest.TestCase): @@ -73,8 +72,5 @@ def test_newer_group(self): missing='newer')) -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(DepUtilTestCase) - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_dir_util.py b/Lib/distutils/tests/test_dir_util.py index ebd89f320dca89..bae9c3ed56c830 100644 --- a/Lib/distutils/tests/test_dir_util.py +++ b/Lib/distutils/tests/test_dir_util.py @@ -11,7 +11,7 @@ from distutils import log from distutils.tests import support -from test.support import run_unittest, is_emscripten, is_wasi +from test.support import is_emscripten, is_wasi class DirUtilTestCase(support.TempdirManager, unittest.TestCase): @@ -136,8 +136,5 @@ def test_copy_tree_exception_in_listdir(self): dir_util.copy_tree(src, None) -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(DirUtilTestCase) - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_dist.py b/Lib/distutils/tests/test_dist.py index 2ef70d987f36bb..8ab24516e7073a 100644 --- a/Lib/distutils/tests/test_dist.py +++ b/Lib/distutils/tests/test_dist.py @@ -12,7 +12,7 @@ from distutils.cmd import Command from test.support import ( - captured_stdout, captured_stderr, run_unittest + captured_stdout, captured_stderr ) from test.support.os_helper import TESTFN from distutils.tests import support @@ -519,11 +519,5 @@ def test_read_metadata(self): self.assertEqual(metadata.obsoletes, None) self.assertEqual(metadata.requires, ['foo']) -def test_suite(): - suite = unittest.TestSuite() - suite.addTest(unittest.TestLoader().loadTestsFromTestCase(DistributionTestCase)) - suite.addTest(unittest.TestLoader().loadTestsFromTestCase(MetadataTestCase)) - return suite - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_extension.py b/Lib/distutils/tests/test_extension.py index 2b08930eafb10a..f9cdef25416549 100644 --- a/Lib/distutils/tests/test_extension.py +++ b/Lib/distutils/tests/test_extension.py @@ -3,7 +3,6 @@ import os import warnings -from test.support import run_unittest from test.support.warnings_helper import check_warnings from distutils.extension import read_setup_file, Extension @@ -63,8 +62,5 @@ def test_extension_init(self): self.assertEqual(str(w.warnings[0].message), "Unknown Extension options: 'chic'") -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(ExtensionTestCase) - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_file_util.py b/Lib/distutils/tests/test_file_util.py index 551151b0143661..76ef6e9743d381 100644 --- a/Lib/distutils/tests/test_file_util.py +++ b/Lib/distutils/tests/test_file_util.py @@ -8,7 +8,6 @@ from distutils import log from distutils.tests import support from distutils.errors import DistutilsFileError -from test.support import run_unittest from test.support.os_helper import unlink @@ -119,8 +118,5 @@ def test_copy_file_hard_link_failure(self): self.assertEqual(f.read(), 'some content') -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(FileUtilTestCase) - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_filelist.py b/Lib/distutils/tests/test_filelist.py index 98c97e49f80db5..216cf27f493e02 100644 --- a/Lib/distutils/tests/test_filelist.py +++ b/Lib/distutils/tests/test_filelist.py @@ -9,7 +9,7 @@ from distutils import filelist from test.support import os_helper -from test.support import captured_stdout, run_unittest +from test.support import captured_stdout from distutils.tests import support MANIFEST_IN = """\ @@ -329,12 +329,5 @@ def test_non_local_discovery(self): self.assertEqual(filelist.findall(temp_dir), expected) -def test_suite(): - return unittest.TestSuite([ - unittest.TestLoader().loadTestsFromTestCase(FileListTestCase), - unittest.TestLoader().loadTestsFromTestCase(FindAllTestCase), - ]) - - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_install.py b/Lib/distutils/tests/test_install.py index c38f98b8b2c294..c30414d5cb24fa 100644 --- a/Lib/distutils/tests/test_install.py +++ b/Lib/distutils/tests/test_install.py @@ -5,7 +5,7 @@ import unittest import site -from test.support import captured_stdout, run_unittest, requires_subprocess +from test.support import captured_stdout, requires_subprocess from distutils import sysconfig from distutils.command.install import install, HAS_USER_SITE @@ -254,8 +254,5 @@ def test_debug_mode(self): self.assertGreater(len(self.logs), old_logs_len) -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(InstallTestCase) - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_install_data.py b/Lib/distutils/tests/test_install_data.py index 6191d2fa6eefab..c5c04a36e12401 100644 --- a/Lib/distutils/tests/test_install_data.py +++ b/Lib/distutils/tests/test_install_data.py @@ -4,7 +4,6 @@ from distutils.command.install_data import install_data from distutils.tests import support -from test.support import run_unittest class InstallDataTestCase(support.TempdirManager, support.LoggingSilencer, @@ -68,8 +67,5 @@ def test_simple_run(self): self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) self.assertTrue(os.path.exists(os.path.join(inst, rone))) -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(InstallDataTestCase) - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_install_headers.py b/Lib/distutils/tests/test_install_headers.py index 1aa4d09cdef731..f8f513217fc6a9 100644 --- a/Lib/distutils/tests/test_install_headers.py +++ b/Lib/distutils/tests/test_install_headers.py @@ -4,7 +4,6 @@ from distutils.command.install_headers import install_headers from distutils.tests import support -from test.support import run_unittest class InstallHeadersTestCase(support.TempdirManager, support.LoggingSilencer, @@ -32,8 +31,5 @@ def test_simple_run(self): # let's check the results self.assertEqual(len(cmd.get_outputs()), 2) -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(InstallHeadersTestCase) - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_install_lib.py b/Lib/distutils/tests/test_install_lib.py index f840d1a94665ed..08bc9c84269376 100644 --- a/Lib/distutils/tests/test_install_lib.py +++ b/Lib/distutils/tests/test_install_lib.py @@ -8,7 +8,7 @@ from distutils.extension import Extension from distutils.tests import support from distutils.errors import DistutilsOptionError -from test.support import run_unittest, requires_subprocess +from test.support import requires_subprocess class InstallLibTestCase(support.TempdirManager, @@ -110,8 +110,5 @@ def test_dont_write_bytecode(self): self.logs[0][1] % self.logs[0][2]) -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(InstallLibTestCase) - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_install_scripts.py b/Lib/distutils/tests/test_install_scripts.py index 648db3b11da745..b4272d6f90faea 100644 --- a/Lib/distutils/tests/test_install_scripts.py +++ b/Lib/distutils/tests/test_install_scripts.py @@ -7,7 +7,6 @@ from distutils.core import Distribution from distutils.tests import support -from test.support import run_unittest class InstallScriptsTestCase(support.TempdirManager, @@ -75,8 +74,5 @@ def write_script(name, text): self.assertIn(name, installed) -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(InstallScriptsTestCase) - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_log.py b/Lib/distutils/tests/test_log.py index ec2ae028de8777..0f3250313fdead 100644 --- a/Lib/distutils/tests/test_log.py +++ b/Lib/distutils/tests/test_log.py @@ -3,7 +3,7 @@ import io import sys import unittest -from test.support import swap_attr, run_unittest +from test.support import swap_attr from distutils import log @@ -39,8 +39,5 @@ def test_non_ascii(self): 'Fαtal\trrr' if errors == 'ignore' else 'Fαtal\t\\xc8rr\\u014dr') -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(TestLog) - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_msvc9compiler.py b/Lib/distutils/tests/test_msvc9compiler.py index 6235405e31201b..60b99e25f8a02c 100644 --- a/Lib/distutils/tests/test_msvc9compiler.py +++ b/Lib/distutils/tests/test_msvc9compiler.py @@ -5,7 +5,6 @@ from distutils.errors import DistutilsPlatformError from distutils.tests import support -from test.support import run_unittest # A manifest with the only assembly reference being the msvcrt assembly, so # should have the assembly completely stripped. Note that although the @@ -177,8 +176,5 @@ def test_remove_entire_manifest(self): self.assertIsNone(got) -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(msvc9compilerTestCase) - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_msvccompiler.py b/Lib/distutils/tests/test_msvccompiler.py index dd67c3eb6d519d..7b3a6bbe7fda9d 100644 --- a/Lib/distutils/tests/test_msvccompiler.py +++ b/Lib/distutils/tests/test_msvccompiler.py @@ -5,7 +5,6 @@ from distutils.errors import DistutilsPlatformError from distutils.tests import support -from test.support import run_unittest SKIP_MESSAGE = (None if sys.platform == "win32" else @@ -74,8 +73,5 @@ def test_get_vc2015(self): else: raise unittest.SkipTest("VS 2015 is not installed") -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(msvccompilerTestCase) - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_register.py b/Lib/distutils/tests/test_register.py index 7805c6d3c9f7b9..33f9443358ff27 100644 --- a/Lib/distutils/tests/test_register.py +++ b/Lib/distutils/tests/test_register.py @@ -5,7 +5,6 @@ import urllib import warnings -from test.support import run_unittest from test.support.warnings_helper import check_warnings from distutils.command import register as register_module @@ -317,8 +316,5 @@ def test_show_response(self): self.assertEqual(results[3], 75 * '-' + '\nxxx\n' + 75 * '-') -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(RegisterTestCase) - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_sdist.py b/Lib/distutils/tests/test_sdist.py index 46b3a13e470c4e..58497fe0875672 100644 --- a/Lib/distutils/tests/test_sdist.py +++ b/Lib/distutils/tests/test_sdist.py @@ -6,7 +6,7 @@ import zipfile from os.path import join from textwrap import dedent -from test.support import captured_stdout, run_unittest +from test.support import captured_stdout from test.support.warnings_helper import check_warnings try: @@ -486,8 +486,5 @@ def test_make_distribution_owner_group(self): finally: archive.close() -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(SDistTestCase) - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_spawn.py b/Lib/distutils/tests/test_spawn.py index a0a1145da5df8e..30bbe2a283ba20 100644 --- a/Lib/distutils/tests/test_spawn.py +++ b/Lib/distutils/tests/test_spawn.py @@ -3,7 +3,7 @@ import stat import sys import unittest.mock -from test.support import run_unittest, unix_shell, requires_subprocess +from test.support import unix_shell, requires_subprocess from test.support import os_helper from distutils.spawn import find_executable @@ -132,8 +132,5 @@ def test_spawn_missing_exe(self): self.assertIn("command 'does-not-exist' failed", str(ctx.exception)) -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(SpawnTestCase) - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_sysconfig.py b/Lib/distutils/tests/test_sysconfig.py index 6833d22af5fe2a..363834fe8b3a34 100644 --- a/Lib/distutils/tests/test_sysconfig.py +++ b/Lib/distutils/tests/test_sysconfig.py @@ -10,7 +10,7 @@ from distutils import sysconfig from distutils.ccompiler import get_default_compiler from distutils.tests import support -from test.support import run_unittest, swap_item, requires_subprocess, is_wasi +from test.support import swap_item, requires_subprocess, is_wasi from test.support.os_helper import TESTFN from test.support.warnings_helper import check_warnings @@ -254,11 +254,5 @@ def test_customize_compiler_before_get_config_vars(self): self.assertEqual(0, p.returncode, "Subprocess failed: " + outs) -def test_suite(): - suite = unittest.TestSuite() - suite.addTest(unittest.TestLoader().loadTestsFromTestCase(SysconfigTestCase)) - return suite - - if __name__ == '__main__': - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_text_file.py b/Lib/distutils/tests/test_text_file.py index ebac3d52f9097c..fbf48515a1da70 100644 --- a/Lib/distutils/tests/test_text_file.py +++ b/Lib/distutils/tests/test_text_file.py @@ -3,7 +3,6 @@ import unittest from distutils.text_file import TextFile from distutils.tests import support -from test.support import run_unittest TEST_DATA = """# test file @@ -100,8 +99,5 @@ def test_input(count, description, file, expected_result): finally: in_file.close() -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(TextFileTestCase) - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_unixccompiler.py b/Lib/distutils/tests/test_unixccompiler.py index a3484d4f94cd92..6a6592527c7659 100644 --- a/Lib/distutils/tests/test_unixccompiler.py +++ b/Lib/distutils/tests/test_unixccompiler.py @@ -1,7 +1,6 @@ """Tests for distutils.unixccompiler.""" import sys import unittest -from test.support import run_unittest from test.support.os_helper import EnvironmentVarGuard from distutils import sysconfig @@ -138,8 +137,5 @@ def gcv(v): self.assertEqual(self.cc.linker_so[0], 'my_ld') -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(UnixCCompilerTestCase) - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_upload.py b/Lib/distutils/tests/test_upload.py index d6797414883060..9aae88bed582da 100644 --- a/Lib/distutils/tests/test_upload.py +++ b/Lib/distutils/tests/test_upload.py @@ -4,7 +4,6 @@ import unittest.mock as mock from urllib.error import HTTPError -from test.support import run_unittest from distutils.command import upload as upload_mod from distutils.command.upload import upload @@ -216,8 +215,5 @@ def test_wrong_exception_order(self): self.clear_logs() -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(uploadTestCase) - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_util.py b/Lib/distutils/tests/test_util.py index f9c223f06ec68d..1b002cb35be1d0 100644 --- a/Lib/distutils/tests/test_util.py +++ b/Lib/distutils/tests/test_util.py @@ -3,7 +3,6 @@ import sys import unittest from copy import copy -from test.support import run_unittest from unittest import mock from distutils.errors import DistutilsPlatformError, DistutilsByteCompileError @@ -306,8 +305,5 @@ def test_grok_environment_error(self): self.assertEqual(msg, "error: Unable to find batch file") -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(UtilTestCase) - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_version.py b/Lib/distutils/tests/test_version.py index 1563e0227b60dd..f102f2953ff50f 100644 --- a/Lib/distutils/tests/test_version.py +++ b/Lib/distutils/tests/test_version.py @@ -2,7 +2,6 @@ import unittest from distutils.version import LooseVersion from distutils.version import StrictVersion -from test.support import run_unittest class VersionTestCase(unittest.TestCase): @@ -80,8 +79,5 @@ def test_cmp(self): 'cmp(%s, %s) should be NotImplemented, got %s' % (v1, v2, res)) -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(VersionTestCase) - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/distutils/tests/test_versionpredicate.py b/Lib/distutils/tests/test_versionpredicate.py index 28ae09dc2058da..b0e9ab2cf59b59 100644 --- a/Lib/distutils/tests/test_versionpredicate.py +++ b/Lib/distutils/tests/test_versionpredicate.py @@ -4,10 +4,10 @@ import distutils.versionpredicate import doctest -from test.support import run_unittest -def test_suite(): - return doctest.DocTestSuite(distutils.versionpredicate) +def load_tests(loader, tests, pattern): + tests.addTest(doctest.DocTestSuite(distutils.versionpredicate)) + return tests if __name__ == '__main__': - run_unittest(test_suite()) + unittest.main() diff --git a/Lib/test/test_distutils.py b/Lib/test/test_distutils.py index 28320fb5c0bfd1..12d1472b975c78 100644 --- a/Lib/test/test_distutils.py +++ b/Lib/test/test_distutils.py @@ -1,8 +1,6 @@ """Tests for distutils. -The tests for distutils are defined in the distutils.tests package; -the test_suite() function there returns a test suite that's ready to -be run. +The tests for distutils are defined in the distutils.tests package. """ import unittest @@ -12,13 +10,7 @@ with warnings_helper.check_warnings( ("The distutils package is deprecated", DeprecationWarning), quiet=True): - import distutils.tests - - -def load_tests(*_): - # used by unittest - return distutils.tests.test_suite() - + from distutils.tests import load_tests def tearDownModule(): support.reap_children() diff --git a/Misc/NEWS.d/next/Tests/2023-10-25-13-13-30.gh-issue-111309.Re7orL.rst b/Misc/NEWS.d/next/Tests/2023-10-25-13-13-30.gh-issue-111309.Re7orL.rst new file mode 100644 index 00000000000000..ed0d3947ad3494 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-10-25-13-13-30.gh-issue-111309.Re7orL.rst @@ -0,0 +1 @@ +:mod:`distutils` tests can now be run via :mod:`unittest`. From fc9a5ef1a8894b2c61dbc762be6a8bf90906b621 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 25 Oct 2023 15:37:19 +0300 Subject: [PATCH 534/632] [3.11] [3.12] gh-111165: Move test running code from test.support to libregrtest (GH-111166) (GH-111316) (GH-111318) Remove no longer used functions run_unittest() and run_doctest() from the test.support module. (cherry picked from commit f6a45a03d0e0ef6b00c45a0de9a606b1d23cbd2f) (cherry picked from commit 5c4f9a1c7ed9fddd8c3ce4c1793af07f66f36a6b) --- Doc/library/test.rst | 35 ---- Lib/test/libregrtest/filter.py | 72 +++++++ Lib/test/libregrtest/findtests.py | 5 +- Lib/test/libregrtest/result.py | 26 ++- Lib/test/libregrtest/results.py | 3 +- Lib/test/libregrtest/setup.py | 5 +- Lib/test/libregrtest/single.py | 47 ++++- .../{support => libregrtest}/testresult.py | 0 Lib/test/support/__init__.py | 193 ------------------ Lib/test/test_regrtest.py | 118 ++++++++++- Lib/test/test_support.py | 115 ----------- ...-10-21-19-27-36.gh-issue-111165.FU6mUk.rst | 2 + 12 files changed, 266 insertions(+), 355 deletions(-) create mode 100644 Lib/test/libregrtest/filter.py rename Lib/test/{support => libregrtest}/testresult.py (100%) create mode 100644 Misc/NEWS.d/next/Tests/2023-10-21-19-27-36.gh-issue-111165.FU6mUk.rst diff --git a/Doc/library/test.rst b/Doc/library/test.rst index aa67a9480ddfcb..a27e88fabe2939 100644 --- a/Doc/library/test.rst +++ b/Doc/library/test.rst @@ -499,34 +499,6 @@ The :mod:`test.support` module defines the following functions: Define match patterns on test filenames and test method names for filtering tests. -.. function:: run_unittest(*classes) - - Execute :class:`unittest.TestCase` subclasses passed to the function. The - function scans the classes for methods starting with the prefix ``test_`` - and executes the tests individually. - - It is also legal to pass strings as parameters; these should be keys in - ``sys.modules``. Each associated module will be scanned by - ``unittest.TestLoader.loadTestsFromModule()``. This is usually seen in the - following :func:`test_main` function:: - - def test_main(): - support.run_unittest(__name__) - - This will run all tests defined in the named module. - - -.. function:: run_doctest(module, verbosity=None, optionflags=0) - - Run :func:`doctest.testmod` on the given *module*. Return - ``(failure_count, test_count)``. - - If *verbosity* is ``None``, :func:`doctest.testmod` is run with verbosity - set to :data:`verbose`. Otherwise, it is run with verbosity set to - ``None``. *optionflags* is passed as ``optionflags`` to - :func:`doctest.testmod`. - - .. function:: setswitchinterval(interval) Set the :func:`sys.setswitchinterval` to the given *interval*. Defines @@ -1052,13 +1024,6 @@ The :mod:`test.support` module defines the following classes: Try to match a single stored value (*dv*) with a supplied value (*v*). -.. class:: BasicTestRunner() - - .. method:: run(test) - - Run *test* and return the result. - - :mod:`test.support.socket_helper` --- Utilities for socket tests ================================================================ diff --git a/Lib/test/libregrtest/filter.py b/Lib/test/libregrtest/filter.py new file mode 100644 index 00000000000000..817624d79e9263 --- /dev/null +++ b/Lib/test/libregrtest/filter.py @@ -0,0 +1,72 @@ +import itertools +import operator +import re + + +# By default, don't filter tests +_test_matchers = () +_test_patterns = () + + +def match_test(test): + # Function used by support.run_unittest() and regrtest --list-cases + result = False + for matcher, result in reversed(_test_matchers): + if matcher(test.id()): + return result + return not result + + +def _is_full_match_test(pattern): + # If a pattern contains at least one dot, it's considered + # as a full test identifier. + # Example: 'test.test_os.FileTests.test_access'. + # + # ignore patterns which contain fnmatch patterns: '*', '?', '[...]' + # or '[!...]'. For example, ignore 'test_access*'. + return ('.' in pattern) and (not re.search(r'[?*\[\]]', pattern)) + + +def set_match_tests(patterns): + global _test_matchers, _test_patterns + + if not patterns: + _test_matchers = () + _test_patterns = () + else: + itemgetter = operator.itemgetter + patterns = tuple(patterns) + if patterns != _test_patterns: + _test_matchers = [ + (_compile_match_function(map(itemgetter(0), it)), result) + for result, it in itertools.groupby(patterns, itemgetter(1)) + ] + _test_patterns = patterns + + +def _compile_match_function(patterns): + patterns = list(patterns) + + if all(map(_is_full_match_test, patterns)): + # Simple case: all patterns are full test identifier. + # The test.bisect_cmd utility only uses such full test identifiers. + return set(patterns).__contains__ + else: + import fnmatch + regex = '|'.join(map(fnmatch.translate, patterns)) + # The search *is* case sensitive on purpose: + # don't use flags=re.IGNORECASE + regex_match = re.compile(regex).match + + def match_test_regex(test_id, regex_match=regex_match): + if regex_match(test_id): + # The regex matches the whole identifier, for example + # 'test.test_os.FileTests.test_access'. + return True + else: + # Try to match parts of the test identifier. + # For example, split 'test.test_os.FileTests.test_access' + # into: 'test', 'test_os', 'FileTests' and 'test_access'. + return any(map(regex_match, test_id.split("."))) + + return match_test_regex diff --git a/Lib/test/libregrtest/findtests.py b/Lib/test/libregrtest/findtests.py index dd39fe1ce20a77..a4235b52eca0af 100644 --- a/Lib/test/libregrtest/findtests.py +++ b/Lib/test/libregrtest/findtests.py @@ -4,6 +4,7 @@ from test import support +from .filter import match_test, set_match_tests from .utils import ( StrPath, TestName, TestTuple, TestList, TestFilter, abs_module_name, count, printlist) @@ -78,14 +79,14 @@ def _list_cases(suite): if isinstance(test, unittest.TestSuite): _list_cases(test) elif isinstance(test, unittest.TestCase): - if support.match_test(test): + if match_test(test): print(test.id()) def list_cases(tests: TestTuple, *, match_tests: TestFilter | None = None, test_dir: StrPath | None = None): support.verbose = False - support.set_match_tests(match_tests) + set_match_tests(match_tests) skipped = [] for test_name in tests: diff --git a/Lib/test/libregrtest/result.py b/Lib/test/libregrtest/result.py index d6b0d5ad383a5b..8bfd3665ac93d5 100644 --- a/Lib/test/libregrtest/result.py +++ b/Lib/test/libregrtest/result.py @@ -2,13 +2,35 @@ import json from typing import Any -from test.support import TestStats - from .utils import ( StrJSON, TestName, FilterTuple, format_duration, normalize_test_name, print_warning) +@dataclasses.dataclass(slots=True) +class TestStats: + tests_run: int = 0 + failures: int = 0 + skipped: int = 0 + + @staticmethod + def from_unittest(result): + return TestStats(result.testsRun, + len(result.failures), + len(result.skipped)) + + @staticmethod + def from_doctest(results): + return TestStats(results.attempted, + results.failed, + results.skipped) + + def accumulate(self, stats): + self.tests_run += stats.tests_run + self.failures += stats.failures + self.skipped += stats.skipped + + # Avoid enum.Enum to reduce the number of imports when tests are run class State: PASSED = "PASSED" diff --git a/Lib/test/libregrtest/results.py b/Lib/test/libregrtest/results.py index 3708078ff0bf3a..1feb43f8c074db 100644 --- a/Lib/test/libregrtest/results.py +++ b/Lib/test/libregrtest/results.py @@ -1,8 +1,7 @@ import sys -from test.support import TestStats from .runtests import RunTests -from .result import State, TestResult +from .result import State, TestResult, TestStats from .utils import ( StrPath, TestName, TestTuple, TestList, FilterDict, printlist, count, format_duration) diff --git a/Lib/test/libregrtest/setup.py b/Lib/test/libregrtest/setup.py index 6a96b051394d20..97edba9f87d7f9 100644 --- a/Lib/test/libregrtest/setup.py +++ b/Lib/test/libregrtest/setup.py @@ -8,6 +8,7 @@ from test import support from test.support.os_helper import TESTFN_UNDECODABLE, FS_NONASCII +from .filter import set_match_tests from .runtests import RunTests from .utils import ( setup_unraisable_hook, setup_threading_excepthook, fix_umask, @@ -92,11 +93,11 @@ def setup_tests(runtests: RunTests): support.PGO = runtests.pgo support.PGO_EXTENDED = runtests.pgo_extended - support.set_match_tests(runtests.match_tests) + set_match_tests(runtests.match_tests) if runtests.use_junit: support.junit_xml_list = [] - from test.support.testresult import RegressionTestResult + from .testresult import RegressionTestResult RegressionTestResult.USE_XML = True else: support.junit_xml_list = None diff --git a/Lib/test/libregrtest/single.py b/Lib/test/libregrtest/single.py index 0304f858edf42c..b4ae29905721dc 100644 --- a/Lib/test/libregrtest/single.py +++ b/Lib/test/libregrtest/single.py @@ -9,13 +9,14 @@ import unittest from test import support -from test.support import TestStats from test.support import threading_helper -from .result import State, TestResult +from .filter import match_test +from .result import State, TestResult, TestStats from .runtests import RunTests from .save_env import saved_test_environment from .setup import setup_tests +from .testresult import get_test_runner from .utils import ( TestName, clear_caches, remove_testfn, abs_module_name, print_warning) @@ -33,7 +34,47 @@ def run_unittest(test_mod): print(error, file=sys.stderr) if loader.errors: raise Exception("errors while loading tests") - return support.run_unittest(tests) + _filter_suite(tests, match_test) + return _run_suite(tests) + +def _filter_suite(suite, pred): + """Recursively filter test cases in a suite based on a predicate.""" + newtests = [] + for test in suite._tests: + if isinstance(test, unittest.TestSuite): + _filter_suite(test, pred) + newtests.append(test) + else: + if pred(test): + newtests.append(test) + suite._tests = newtests + +def _run_suite(suite): + """Run tests from a unittest.TestSuite-derived class.""" + runner = get_test_runner(sys.stdout, + verbosity=support.verbose, + capture_output=(support.junit_xml_list is not None)) + + result = runner.run(suite) + + if support.junit_xml_list is not None: + support.junit_xml_list.append(result.get_xml_element()) + + if not result.testsRun and not result.skipped and not result.errors: + raise support.TestDidNotRun + if not result.wasSuccessful(): + stats = TestStats.from_unittest(result) + if len(result.errors) == 1 and not result.failures: + err = result.errors[0][1] + elif len(result.failures) == 1 and not result.errors: + err = result.failures[0][1] + else: + err = "multiple errors occurred" + if not verbose: err += "; run in verbose mode for details" + errors = [(str(tc), exc_str) for tc, exc_str in result.errors] + failures = [(str(tc), exc_str) for tc, exc_str in result.failures] + raise support.TestFailedWithDetails(err, errors, failures, stats=stats) + return result def regrtest_runner(result: TestResult, test_func, runtests: RunTests) -> None: diff --git a/Lib/test/support/testresult.py b/Lib/test/libregrtest/testresult.py similarity index 100% rename from Lib/test/support/testresult.py rename to Lib/test/libregrtest/testresult.py diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 2d53f635cef7d3..7ebc42e5bdd01c 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -6,9 +6,7 @@ import contextlib import dataclasses import functools -import itertools import getpass -import operator import os import re import stat @@ -19,8 +17,6 @@ import unittest import warnings -from .testresult import get_test_runner - try: from _testcapi import unicode_legacy_string @@ -39,7 +35,6 @@ "is_resource_enabled", "requires", "requires_freebsd_version", "requires_linux_version", "requires_mac_ver", "check_syntax_error", - "BasicTestRunner", "run_unittest", "run_doctest", "requires_gzip", "requires_bz2", "requires_lzma", "bigmemtest", "bigaddrspacetest", "cpython_only", "get_attribute", "requires_IEEE_754", "requires_zlib", @@ -1026,12 +1021,6 @@ def wrapper(self): #======================================================================= # unittest integration. -class BasicTestRunner: - def run(self, test): - result = unittest.TestResult() - test(result) - return result - def _id(obj): return obj @@ -1110,156 +1099,6 @@ def refcount_test(test): return no_tracing(cpython_only(test)) -def _filter_suite(suite, pred): - """Recursively filter test cases in a suite based on a predicate.""" - newtests = [] - for test in suite._tests: - if isinstance(test, unittest.TestSuite): - _filter_suite(test, pred) - newtests.append(test) - else: - if pred(test): - newtests.append(test) - suite._tests = newtests - -@dataclasses.dataclass(slots=True) -class TestStats: - tests_run: int = 0 - failures: int = 0 - skipped: int = 0 - - @staticmethod - def from_unittest(result): - return TestStats(result.testsRun, - len(result.failures), - len(result.skipped)) - - @staticmethod - def from_doctest(results): - return TestStats(results.attempted, - results.failed) - - def accumulate(self, stats): - self.tests_run += stats.tests_run - self.failures += stats.failures - self.skipped += stats.skipped - - -def _run_suite(suite): - """Run tests from a unittest.TestSuite-derived class.""" - runner = get_test_runner(sys.stdout, - verbosity=verbose, - capture_output=(junit_xml_list is not None)) - - result = runner.run(suite) - - if junit_xml_list is not None: - junit_xml_list.append(result.get_xml_element()) - - if not result.testsRun and not result.skipped and not result.errors: - raise TestDidNotRun - if not result.wasSuccessful(): - stats = TestStats.from_unittest(result) - if len(result.errors) == 1 and not result.failures: - err = result.errors[0][1] - elif len(result.failures) == 1 and not result.errors: - err = result.failures[0][1] - else: - err = "multiple errors occurred" - if not verbose: err += "; run in verbose mode for details" - errors = [(str(tc), exc_str) for tc, exc_str in result.errors] - failures = [(str(tc), exc_str) for tc, exc_str in result.failures] - raise TestFailedWithDetails(err, errors, failures, stats=stats) - return result - - -# By default, don't filter tests -_test_matchers = () -_test_patterns = () - - -def match_test(test): - # Function used by support.run_unittest() and regrtest --list-cases - result = False - for matcher, result in reversed(_test_matchers): - if matcher(test.id()): - return result - return not result - - -def _is_full_match_test(pattern): - # If a pattern contains at least one dot, it's considered - # as a full test identifier. - # Example: 'test.test_os.FileTests.test_access'. - # - # ignore patterns which contain fnmatch patterns: '*', '?', '[...]' - # or '[!...]'. For example, ignore 'test_access*'. - return ('.' in pattern) and (not re.search(r'[?*\[\]]', pattern)) - - -def set_match_tests(patterns): - global _test_matchers, _test_patterns - - if not patterns: - _test_matchers = () - _test_patterns = () - else: - itemgetter = operator.itemgetter - patterns = tuple(patterns) - if patterns != _test_patterns: - _test_matchers = [ - (_compile_match_function(map(itemgetter(0), it)), result) - for result, it in itertools.groupby(patterns, itemgetter(1)) - ] - _test_patterns = patterns - - -def _compile_match_function(patterns): - patterns = list(patterns) - - if all(map(_is_full_match_test, patterns)): - # Simple case: all patterns are full test identifier. - # The test.bisect_cmd utility only uses such full test identifiers. - return set(patterns).__contains__ - else: - import fnmatch - regex = '|'.join(map(fnmatch.translate, patterns)) - # The search *is* case sensitive on purpose: - # don't use flags=re.IGNORECASE - regex_match = re.compile(regex).match - - def match_test_regex(test_id, regex_match=regex_match): - if regex_match(test_id): - # The regex matches the whole identifier, for example - # 'test.test_os.FileTests.test_access'. - return True - else: - # Try to match parts of the test identifier. - # For example, split 'test.test_os.FileTests.test_access' - # into: 'test', 'test_os', 'FileTests' and 'test_access'. - return any(map(regex_match, test_id.split("."))) - - return match_test_regex - - -def run_unittest(*classes): - """Run tests from unittest.TestCase-derived classes.""" - valid_types = (unittest.TestSuite, unittest.TestCase) - loader = unittest.TestLoader() - suite = unittest.TestSuite() - for cls in classes: - if isinstance(cls, str): - if cls in sys.modules: - suite.addTest(loader.loadTestsFromModule(sys.modules[cls])) - else: - raise ValueError("str arguments must be keys in sys.modules") - elif isinstance(cls, valid_types): - suite.addTest(cls) - else: - suite.addTest(loader.loadTestsFromTestCase(cls)) - _filter_suite(suite, match_test) - return _run_suite(suite) - #======================================================================= # Check for the presence of docstrings. @@ -1280,38 +1119,6 @@ def _check_docstrings(): "test requires docstrings") -#======================================================================= -# doctest driver. - -def run_doctest(module, verbosity=None, optionflags=0): - """Run doctest on the given module. Return (#failures, #tests). - - If optional argument verbosity is not specified (or is None), pass - support's belief about verbosity on to doctest. Else doctest's - usual behavior is used (it searches sys.argv for -v). - """ - - import doctest - - if verbosity is None: - verbosity = verbose - else: - verbosity = None - - results = doctest.testmod(module, - verbose=verbosity, - optionflags=optionflags) - if results.failed: - stats = TestStats.from_doctest(results) - raise TestFailed(f"{results.failed} of {results.attempted} " - f"doctests failed", - stats=stats) - if verbose: - print('doctest (%s) ... %d tests with zero failures' % - (module.__name__, results.attempted)) - return results - - #======================================================================= # Support for saving and restoring the imported modules. diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py index 0c12b0efff88e5..aa3f65487a288e 100644 --- a/Lib/test/test_regrtest.py +++ b/Lib/test/test_regrtest.py @@ -22,11 +22,13 @@ import textwrap import unittest from test import support -from test.support import os_helper, TestStats +from test.support import os_helper from test.libregrtest import cmdline from test.libregrtest import main from test.libregrtest import setup from test.libregrtest import utils +from test.libregrtest.filter import set_match_tests, match_test +from test.libregrtest.result import TestStats from test.libregrtest.utils import normalize_test_name if not support.has_subprocess_support: @@ -2182,6 +2184,120 @@ def test_format_resources(self): format_resources((*ALL_RESOURCES, "tzdata")), 'resources: all,tzdata') + def test_match_test(self): + class Test: + def __init__(self, test_id): + self.test_id = test_id + + def id(self): + return self.test_id + + test_access = Test('test.test_os.FileTests.test_access') + test_chdir = Test('test.test_os.Win32ErrorTests.test_chdir') + test_copy = Test('test.test_shutil.TestCopy.test_copy') + + # Test acceptance + with support.swap_attr(support, '_test_matchers', ()): + # match all + set_match_tests([]) + self.assertTrue(match_test(test_access)) + self.assertTrue(match_test(test_chdir)) + + # match all using None + set_match_tests(None) + self.assertTrue(match_test(test_access)) + self.assertTrue(match_test(test_chdir)) + + # match the full test identifier + set_match_tests([(test_access.id(), True)]) + self.assertTrue(match_test(test_access)) + self.assertFalse(match_test(test_chdir)) + + # match the module name + set_match_tests([('test_os', True)]) + self.assertTrue(match_test(test_access)) + self.assertTrue(match_test(test_chdir)) + self.assertFalse(match_test(test_copy)) + + # Test '*' pattern + set_match_tests([('test_*', True)]) + self.assertTrue(match_test(test_access)) + self.assertTrue(match_test(test_chdir)) + + # Test case sensitivity + set_match_tests([('filetests', True)]) + self.assertFalse(match_test(test_access)) + set_match_tests([('FileTests', True)]) + self.assertTrue(match_test(test_access)) + + # Test pattern containing '.' and a '*' metacharacter + set_match_tests([('*test_os.*.test_*', True)]) + self.assertTrue(match_test(test_access)) + self.assertTrue(match_test(test_chdir)) + self.assertFalse(match_test(test_copy)) + + # Multiple patterns + set_match_tests([(test_access.id(), True), (test_chdir.id(), True)]) + self.assertTrue(match_test(test_access)) + self.assertTrue(match_test(test_chdir)) + self.assertFalse(match_test(test_copy)) + + set_match_tests([('test_access', True), ('DONTMATCH', True)]) + self.assertTrue(match_test(test_access)) + self.assertFalse(match_test(test_chdir)) + + # Test rejection + with support.swap_attr(support, '_test_matchers', ()): + # match the full test identifier + set_match_tests([(test_access.id(), False)]) + self.assertFalse(match_test(test_access)) + self.assertTrue(match_test(test_chdir)) + + # match the module name + set_match_tests([('test_os', False)]) + self.assertFalse(match_test(test_access)) + self.assertFalse(match_test(test_chdir)) + self.assertTrue(match_test(test_copy)) + + # Test '*' pattern + set_match_tests([('test_*', False)]) + self.assertFalse(match_test(test_access)) + self.assertFalse(match_test(test_chdir)) + + # Test case sensitivity + set_match_tests([('filetests', False)]) + self.assertTrue(match_test(test_access)) + set_match_tests([('FileTests', False)]) + self.assertFalse(match_test(test_access)) + + # Test pattern containing '.' and a '*' metacharacter + set_match_tests([('*test_os.*.test_*', False)]) + self.assertFalse(match_test(test_access)) + self.assertFalse(match_test(test_chdir)) + self.assertTrue(match_test(test_copy)) + + # Multiple patterns + set_match_tests([(test_access.id(), False), (test_chdir.id(), False)]) + self.assertFalse(match_test(test_access)) + self.assertFalse(match_test(test_chdir)) + self.assertTrue(match_test(test_copy)) + + set_match_tests([('test_access', False), ('DONTMATCH', False)]) + self.assertFalse(match_test(test_access)) + self.assertTrue(match_test(test_chdir)) + + # Test mixed filters + with support.swap_attr(support, '_test_matchers', ()): + set_match_tests([('*test_os', False), ('test_access', True)]) + self.assertTrue(match_test(test_access)) + self.assertFalse(match_test(test_chdir)) + self.assertTrue(match_test(test_copy)) + + set_match_tests([('*test_os', True), ('test_access', False)]) + self.assertFalse(match_test(test_access)) + self.assertTrue(match_test(test_chdir)) + self.assertFalse(match_test(test_copy)) + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index 3f9159642ba178..147ae577175129 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -550,120 +550,6 @@ def test_optim_args_from_interpreter_flags(self): with self.subTest(opts=opts): self.check_options(opts, 'optim_args_from_interpreter_flags') - def test_match_test(self): - class Test: - def __init__(self, test_id): - self.test_id = test_id - - def id(self): - return self.test_id - - test_access = Test('test.test_os.FileTests.test_access') - test_chdir = Test('test.test_os.Win32ErrorTests.test_chdir') - test_copy = Test('test.test_shutil.TestCopy.test_copy') - - # Test acceptance - with support.swap_attr(support, '_test_matchers', ()): - # match all - support.set_match_tests([]) - self.assertTrue(support.match_test(test_access)) - self.assertTrue(support.match_test(test_chdir)) - - # match all using None - support.set_match_tests(None) - self.assertTrue(support.match_test(test_access)) - self.assertTrue(support.match_test(test_chdir)) - - # match the full test identifier - support.set_match_tests([(test_access.id(), True)]) - self.assertTrue(support.match_test(test_access)) - self.assertFalse(support.match_test(test_chdir)) - - # match the module name - support.set_match_tests([('test_os', True)]) - self.assertTrue(support.match_test(test_access)) - self.assertTrue(support.match_test(test_chdir)) - self.assertFalse(support.match_test(test_copy)) - - # Test '*' pattern - support.set_match_tests([('test_*', True)]) - self.assertTrue(support.match_test(test_access)) - self.assertTrue(support.match_test(test_chdir)) - - # Test case sensitivity - support.set_match_tests([('filetests', True)]) - self.assertFalse(support.match_test(test_access)) - support.set_match_tests([('FileTests', True)]) - self.assertTrue(support.match_test(test_access)) - - # Test pattern containing '.' and a '*' metacharacter - support.set_match_tests([('*test_os.*.test_*', True)]) - self.assertTrue(support.match_test(test_access)) - self.assertTrue(support.match_test(test_chdir)) - self.assertFalse(support.match_test(test_copy)) - - # Multiple patterns - support.set_match_tests([(test_access.id(), True), (test_chdir.id(), True)]) - self.assertTrue(support.match_test(test_access)) - self.assertTrue(support.match_test(test_chdir)) - self.assertFalse(support.match_test(test_copy)) - - support.set_match_tests([('test_access', True), ('DONTMATCH', True)]) - self.assertTrue(support.match_test(test_access)) - self.assertFalse(support.match_test(test_chdir)) - - # Test rejection - with support.swap_attr(support, '_test_matchers', ()): - # match the full test identifier - support.set_match_tests([(test_access.id(), False)]) - self.assertFalse(support.match_test(test_access)) - self.assertTrue(support.match_test(test_chdir)) - - # match the module name - support.set_match_tests([('test_os', False)]) - self.assertFalse(support.match_test(test_access)) - self.assertFalse(support.match_test(test_chdir)) - self.assertTrue(support.match_test(test_copy)) - - # Test '*' pattern - support.set_match_tests([('test_*', False)]) - self.assertFalse(support.match_test(test_access)) - self.assertFalse(support.match_test(test_chdir)) - - # Test case sensitivity - support.set_match_tests([('filetests', False)]) - self.assertTrue(support.match_test(test_access)) - support.set_match_tests([('FileTests', False)]) - self.assertFalse(support.match_test(test_access)) - - # Test pattern containing '.' and a '*' metacharacter - support.set_match_tests([('*test_os.*.test_*', False)]) - self.assertFalse(support.match_test(test_access)) - self.assertFalse(support.match_test(test_chdir)) - self.assertTrue(support.match_test(test_copy)) - - # Multiple patterns - support.set_match_tests([(test_access.id(), False), (test_chdir.id(), False)]) - self.assertFalse(support.match_test(test_access)) - self.assertFalse(support.match_test(test_chdir)) - self.assertTrue(support.match_test(test_copy)) - - support.set_match_tests([('test_access', False), ('DONTMATCH', False)]) - self.assertFalse(support.match_test(test_access)) - self.assertTrue(support.match_test(test_chdir)) - - # Test mixed filters - with support.swap_attr(support, '_test_matchers', ()): - support.set_match_tests([('*test_os', False), ('test_access', True)]) - self.assertTrue(support.match_test(test_access)) - self.assertFalse(support.match_test(test_chdir)) - self.assertTrue(support.match_test(test_copy)) - - support.set_match_tests([('*test_os', True), ('test_access', False)]) - self.assertFalse(support.match_test(test_access)) - self.assertTrue(support.match_test(test_chdir)) - self.assertFalse(support.match_test(test_copy)) - @unittest.skipIf(support.is_emscripten, "Unstable in Emscripten") @unittest.skipIf(support.is_wasi, "Unavailable on WASI") def test_fd_count(self): @@ -875,7 +761,6 @@ def test_copy_python_src_ignore(self): # precisionbigmemtest # bigaddrspacetest # requires_resource - # run_doctest # threading_cleanup # reap_threads # can_symlink 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 new file mode 100644 index 00000000000000..753a3c00029ceb --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-10-21-19-27-36.gh-issue-111165.FU6mUk.rst @@ -0,0 +1,2 @@ +Remove no longer used functions ``run_unittest()`` and ``run_doctest()`` and +class ``BasicTestRunner`` from the :mod:`test.support` module. From 07664c9ddb5b5ee3371912fdc47da24bbbc7a6b3 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 25 Oct 2023 16:07:44 +0200 Subject: [PATCH 535/632] [3.11] gh-108590: Improve sqlite3 docs on encoding issues and how to handle those (GH-108699) (#111325) Add a guide for how to handle non-UTF-8 text encodings. Link to that guide from the 'text_factory' docs. (cherry picked from commit 1262e41842957c3b402fc0cf0a6eb2ea230c828f) Co-authored-by: Erlend E. Aasland Co-authored-by: Alex Waygood Co-authored-by: C.A.M. Gerlach Co-authored-by: Corvin Co-authored-by: Ezio Melotti Co-authored-by: Serhiy Storchaka --- Doc/library/sqlite3.rst | 83 +++++++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 33 deletions(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index ef92bad596ef15..3e9b7a270ad5ef 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -1029,6 +1029,10 @@ Connection objects f.write('%s\n' % line) con.close() + .. seealso:: + + :ref:`sqlite3-howto-encoding` + .. method:: backup(target, *, pages=-1, progress=None, name="main", sleep=0.250) @@ -1095,6 +1099,10 @@ Connection objects .. versionadded:: 3.7 + .. seealso:: + + :ref:`sqlite3-howto-encoding` + .. method:: getlimit(category, /) Get a connection runtime limit. @@ -1253,39 +1261,8 @@ Connection objects and returns a text representation of it. The callable is invoked for SQLite values with the ``TEXT`` data type. By default, this attribute is set to :class:`str`. - If you want to return ``bytes`` instead, set *text_factory* to ``bytes``. - Example: - - .. testcode:: - - con = sqlite3.connect(":memory:") - cur = con.cursor() - - AUSTRIA = "Österreich" - - # by default, rows are returned as str - cur.execute("SELECT ?", (AUSTRIA,)) - row = cur.fetchone() - assert row[0] == AUSTRIA - - # but we can make sqlite3 always return bytestrings ... - con.text_factory = bytes - cur.execute("SELECT ?", (AUSTRIA,)) - row = cur.fetchone() - assert type(row[0]) is bytes - # the bytestrings will be encoded in UTF-8, unless you stored garbage in the - # database ... - assert row[0] == AUSTRIA.encode("utf-8") - - # we can also implement a custom text_factory ... - # here we implement one that appends "foo" to all strings - con.text_factory = lambda x: x.decode("utf-8") + "foo" - cur.execute("SELECT ?", ("bar",)) - row = cur.fetchone() - assert row[0] == "barfoo" - - con.close() + See :ref:`sqlite3-howto-encoding` for more details. .. attribute:: total_changes @@ -1423,7 +1400,6 @@ Cursor objects COMMIT; """) - .. method:: fetchone() If :attr:`~Cursor.row_factory` is ``None``, @@ -2369,6 +2345,47 @@ With some adjustments, the above recipe can be adapted to use a instead of a :class:`~collections.namedtuple`. +.. _sqlite3-howto-encoding: + +How to handle non-UTF-8 text encodings +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +By default, :mod:`!sqlite3` uses :class:`str` to adapt SQLite values +with the ``TEXT`` data type. +This works well for UTF-8 encoded text, but it might fail for other encodings +and invalid UTF-8. +You can use a custom :attr:`~Connection.text_factory` to handle such cases. + +Because of SQLite's `flexible typing`_, it is not uncommon to encounter table +columns with the ``TEXT`` data type containing non-UTF-8 encodings, +or even arbitrary data. +To demonstrate, let's assume we have a database with ISO-8859-2 (Latin-2) +encoded text, for example a table of Czech-English dictionary entries. +Assuming we now have a :class:`Connection` instance :py:data:`!con` +connected to this database, +we can decode the Latin-2 encoded text using this :attr:`~Connection.text_factory`: + +.. testcode:: + + con.text_factory = lambda data: str(data, encoding="latin2") + +For invalid UTF-8 or arbitrary data in stored in ``TEXT`` table columns, +you can use the following technique, borrowed from the :ref:`unicode-howto`: + +.. testcode:: + + con.text_factory = lambda data: str(data, errors="surrogateescape") + +.. note:: + + The :mod:`!sqlite3` module API does not support strings + containing surrogates. + +.. seealso:: + + :ref:`unicode-howto` + + .. _sqlite3-explanation: Explanation From 6fea61a9e02260648fbec204e9caac6d5176cc7b Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 25 Oct 2023 17:15:50 +0200 Subject: [PATCH 536/632] [3.11] gh-111165: Add missed "support." prefix for "verbose" (GH-111327) (GH-111329) (cherry picked from commit a4981921aa2c00f3883ef593fde1dbc034e3c304) Co-authored-by: Serhiy Storchaka --- Lib/test/libregrtest/single.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/libregrtest/single.py b/Lib/test/libregrtest/single.py index b4ae29905721dc..ad75ef54a8c3f8 100644 --- a/Lib/test/libregrtest/single.py +++ b/Lib/test/libregrtest/single.py @@ -70,7 +70,7 @@ def _run_suite(suite): err = result.failures[0][1] else: err = "multiple errors occurred" - if not verbose: err += "; run in verbose mode for details" + if not support.verbose: err += "; run in verbose mode for details" errors = [(str(tc), exc_str) for tc, exc_str in result.errors] failures = [(str(tc), exc_str) for tc, exc_str in result.failures] raise support.TestFailedWithDetails(err, errors, failures, stats=stats) From 12c7e5071cb6013df69586ccc26835742c4d86b9 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Wed, 25 Oct 2023 13:47:16 -0700 Subject: [PATCH 537/632] [3.11] GH-94438: Restore ability to jump over None tests (GH-111338) (cherry picked from commit 6640f1d) --- Lib/test/test_sys_settrace.py | 34 +++++++++++++++++++ Misc/ACKS | 1 + ...3-10-23-22-11-09.gh-issue-94438.y2pITu.rst | 1 + Objects/frameobject.c | 12 +++++-- 4 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-23-22-11-09.gh-issue-94438.y2pITu.rst diff --git a/Lib/test/test_sys_settrace.py b/Lib/test/test_sys_settrace.py index 3540192b5bb5bb..e97e122534a2f3 100644 --- a/Lib/test/test_sys_settrace.py +++ b/Lib/test/test_sys_settrace.py @@ -1952,6 +1952,40 @@ def test_jump_simple_backwards(output): output.append(1) output.append(2) + @jump_test(1, 4, [5]) + def test_jump_is_none_forwards(output): + x = None + if x is None: + output.append(3) + else: + output.append(5) + + @jump_test(6, 5, [3, 5, 6]) + def test_jump_is_none_backwards(output): + x = None + if x is None: + output.append(3) + else: + output.append(5) + output.append(6) + + @jump_test(1, 4, [5]) + def test_jump_is_not_none_forwards(output): + x = None + if x is not None: + output.append(3) + else: + output.append(5) + + @jump_test(6, 5, [5, 5, 6]) + def test_jump_is_not_none_backwards(output): + x = None + if x is not None: + output.append(3) + else: + output.append(5) + output.append(6) + @jump_test(3, 5, [2, 5]) def test_jump_out_of_block_forwards(output): for i in 1, 2: diff --git a/Misc/ACKS b/Misc/ACKS index 6eff2cc1f19e28..7efa01db59e7ea 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1324,6 +1324,7 @@ Michele Orrù Tomáš Orsava Oleg Oshmyan Denis Osipov +Savannah Ostrowski Denis S. Otkidach Peter Otten Michael Otteneder 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 new file mode 100644 index 00000000000000..b6e147a48a8cd8 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-10-23-22-11-09.gh-issue-94438.y2pITu.rst @@ -0,0 +1 @@ +Fix a regression that prevented jumping across ``is None`` and ``is not None`` when debugging. Patch by Savannah Ostrowski. diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 425749d14cf014..c95e8711880517 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -315,19 +315,27 @@ mark_stacks(PyCodeObject *code_obj, int len) case POP_JUMP_BACKWARD_IF_FALSE: case POP_JUMP_FORWARD_IF_TRUE: case POP_JUMP_BACKWARD_IF_TRUE: + case POP_JUMP_FORWARD_IF_NONE: + case POP_JUMP_BACKWARD_IF_NONE: + case POP_JUMP_FORWARD_IF_NOT_NONE: + case POP_JUMP_BACKWARD_IF_NOT_NONE: { int64_t target_stack; int j = get_arg(code, i); if (opcode == POP_JUMP_FORWARD_IF_FALSE || opcode == POP_JUMP_FORWARD_IF_TRUE || opcode == JUMP_IF_FALSE_OR_POP || - opcode == JUMP_IF_TRUE_OR_POP) + opcode == JUMP_IF_TRUE_OR_POP || + opcode == POP_JUMP_FORWARD_IF_NONE || + opcode == POP_JUMP_FORWARD_IF_NOT_NONE) { j += i + 1; } else { assert(opcode == POP_JUMP_BACKWARD_IF_FALSE || - opcode == POP_JUMP_BACKWARD_IF_TRUE); + opcode == POP_JUMP_BACKWARD_IF_TRUE || + opcode == POP_JUMP_BACKWARD_IF_NONE || + opcode == POP_JUMP_BACKWARD_IF_NOT_NONE); j = i + 1 - j; } assert(j < len); From 762aba72eb35a5696048ed89df35385c8d784c52 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 26 Oct 2023 15:12:27 +0200 Subject: [PATCH 538/632] [3.11] gh-111348: Fix direct invocation of `test_doctest`; remove `test_doctest.test_coverage` (GH-111349) (#111360) gh-111348: Fix direct invocation of `test_doctest`; remove `test_doctest.test_coverage` (GH-111349) (cherry picked from commit 31c05b72c15885ad5ff298de39456d8baed28448) Co-authored-by: Nikita Sobolev Co-authored-by: Sergey B Kirpichev --- Lib/test/test_doctest.py | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py index fbeed96e5bee36..98bda46d312909 100644 --- a/Lib/test/test_doctest.py +++ b/Lib/test/test_doctest.py @@ -3318,19 +3318,5 @@ def load_tests(loader, tests, pattern): return tests -def test_coverage(coverdir): - trace = import_helper.import_module('trace') - tracer = trace.Trace(ignoredirs=[sys.base_prefix, sys.base_exec_prefix,], - trace=0, count=1) - tracer.run('test_main()') - r = tracer.results() - print('Writing coverage results...') - r.write_results(show_missing=True, summary=True, - coverdir=coverdir) - - if __name__ == '__main__': - if '-c' in sys.argv: - test_coverage('/tmp/doctest.cover') - else: - unittest.main() + unittest.main(module='test.test_doctest') From 22cde39fbf65df8790d1a93f199affa8a0361c4b Mon Sep 17 00:00:00 2001 From: Pablo Galindo Salgado Date: Fri, 27 Oct 2023 09:46:20 +0900 Subject: [PATCH 539/632] [3.11] bpo-43950: handle wide unicode characters in tracebacks (GH-28150) (#111373) --- Lib/test/test_traceback.py | 57 ++++++++++++++++++- Lib/traceback.py | 53 +++++++++++++---- ...3-10-26-15-34-11.gh-issue-88116.W9-vaQ.rst | 3 + Parser/pegen.c | 55 ++++++++++++++++++ Parser/pegen.h | 1 + Python/traceback.c | 35 +++++++++++- 6 files changed, 189 insertions(+), 15 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-26-15-34-11.gh-issue-88116.W9-vaQ.rst diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index ccc59870c2a952..2881a3489084a0 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -893,7 +893,62 @@ def f(): f" callable()", f" File \"{__file__}\", line {f.__code__.co_firstlineno + 4}, in f", f" print(1, www(", - f" ^^^^", + f" ^^^^^^^", + ] + self.assertEqual(actual, expected) + + def test_byte_offset_with_wide_characters_term_highlight(self): + def f(): + 说明说明 = 1 + şçöğıĤellö = 0 # not wide but still non-ascii + return 说明说明 / şçöğıĤellö + + actual = self.get_exception(f) + expected = [ + f"Traceback (most recent call last):", + f" File \"{__file__}\", line {self.callable_line}, in get_exception", + f" callable()", + f" File \"{__file__}\", line {f.__code__.co_firstlineno + 3}, in f", + f" return 说明说明 / şçöğıĤellö", + f" ~~~~~~~~~^~~~~~~~~~~~", + ] + self.assertEqual(actual, expected) + + def test_byte_offset_with_emojis_term_highlight(self): + def f(): + return "✨🐍" + func_说明说明("📗🚛", + "📗🚛") + "🐍" + + actual = self.get_exception(f) + expected = [ + f"Traceback (most recent call last):", + f" File \"{__file__}\", line {self.callable_line}, in get_exception", + f" callable()", + f" File \"{__file__}\", line {f.__code__.co_firstlineno + 1}, in f", + f' return "✨🐍" + func_说明说明("📗🚛",', + f" ^^^^^^^^^^^^^", + ] + self.assertEqual(actual, expected) + + def test_byte_offset_wide_chars_subscript(self): + def f(): + my_dct = { + "✨🚛✨": { + "说明": { + "🐍🐍🐍": None + } + } + } + return my_dct["✨🚛✨"]["说明"]["🐍"]["说明"]["🐍🐍"] + + actual = self.get_exception(f) + expected = [ + f"Traceback (most recent call last):", + f" File \"{__file__}\", line {self.callable_line}, in get_exception", + f" callable()", + f" File \"{__file__}\", line {f.__code__.co_firstlineno + 8}, in f", + f' return my_dct["✨🚛✨"]["说明"]["🐍"]["说明"]["🐍🐍"]', + f" ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^", ] self.assertEqual(actual, expected) diff --git a/Lib/traceback.py b/Lib/traceback.py index 0e229553cb5d25..ea045e27610d4d 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -465,7 +465,8 @@ def format_frame_summary(self, frame_summary): stripped_line = frame_summary.line.strip() row.append(' {}\n'.format(stripped_line)) - orig_line_len = len(frame_summary._original_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 ( @@ -473,31 +474,40 @@ def format_frame_summary(self, frame_summary): and frame_summary.end_colno is not None ): start_offset = _byte_offset_to_character_offset( - frame_summary._original_line, frame_summary.colno) + 1 + line, frame_summary.colno) end_offset = _byte_offset_to_character_offset( - frame_summary._original_line, frame_summary.end_colno) + 1 + line, frame_summary.end_colno) + code_segment = line[start_offset:end_offset] anchors = None if frame_summary.lineno == frame_summary.end_lineno: with suppress(Exception): - anchors = _extract_caret_anchors_from_line_segment( - frame_summary._original_line[start_offset - 1:end_offset - 1] - ) + anchors = _extract_caret_anchors_from_line_segment(code_segment) else: - end_offset = stripped_characters + len(stripped_line) + # 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(' ' * (start_offset - stripped_characters)) + row.append(' ' * (dp_start_offset - stripped_characters)) if anchors: - row.append(anchors.primary_char * (anchors.left_end_offset)) - row.append(anchors.secondary_char * (anchors.right_start_offset - anchors.left_end_offset)) - row.append(anchors.primary_char * (end_offset - start_offset - anchors.right_start_offset)) + 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('^' * (end_offset - start_offset)) + row.append('^' * (dp_end_offset - dp_start_offset)) row.append('\n') @@ -618,6 +628,25 @@ def _extract_caret_anchors_from_line_segment(segment): return None +_WIDE_CHAR_SPECIFIERS = "WF" + +def _display_width(line, offset): + """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.""" + + # Fast track for ASCII-only strings + if line.isascii(): + return offset + + import unicodedata + + return sum( + 2 if unicodedata.east_asian_width(char) in _WIDE_CHAR_SPECIFIERS else 1 + for char in line[:offset] + ) + + class _ExceptionPrintContext: def __init__(self): diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-26-15-34-11.gh-issue-88116.W9-vaQ.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-26-15-34-11.gh-issue-88116.W9-vaQ.rst new file mode 100644 index 00000000000000..12257ef2b0b9b0 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-10-26-15-34-11.gh-issue-88116.W9-vaQ.rst @@ -0,0 +1,3 @@ +Traceback location ranges involving wide unicode characters (like emoji and +asian characters) now are properly highlighted. Patch by Batuhan Taskaya and +Pablo Galindo. diff --git a/Parser/pegen.c b/Parser/pegen.c index 87b47bacec553f..3b85b095beb235 100644 --- a/Parser/pegen.c +++ b/Parser/pegen.c @@ -38,6 +38,61 @@ _PyPegen_byte_offset_to_character_offset(PyObject *line, Py_ssize_t col_offset) return size; } +// 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. +Py_ssize_t +_PyPegen_calculate_display_width(PyObject *line, Py_ssize_t character_offset) +{ + PyObject *segment = PyUnicode_Substring(line, 0, character_offset); + if (!segment) { + return -1; + } + + // Fast track for ascii strings + if (PyUnicode_IS_ASCII(segment)) { + Py_DECREF(segment); + return character_offset; + } + + PyObject *width_fn = _PyImport_GetModuleAttrString("unicodedata", "east_asian_width"); + if (!width_fn) { + return -1; + } + + Py_ssize_t width = 0; + Py_ssize_t len = PyUnicode_GET_LENGTH(segment); + for (Py_ssize_t i = 0; i < len; i++) { + PyObject *chr = PyUnicode_Substring(segment, i, i + 1); + if (!chr) { + Py_DECREF(segment); + Py_DECREF(width_fn); + return -1; + } + + PyObject *width_specifier = PyObject_CallOneArg(width_fn, chr); + Py_DECREF(chr); + if (!width_specifier) { + Py_DECREF(segment); + Py_DECREF(width_fn); + return -1; + } + + if (_PyUnicode_EqualToASCIIString(width_specifier, "W") || + _PyUnicode_EqualToASCIIString(width_specifier, "F")) { + width += 2; + } + else { + width += 1; + } + Py_DECREF(width_specifier); + } + + Py_DECREF(segment); + Py_DECREF(width_fn); + return width; +} + // Here, mark is the start of the node, while p->mark is the end. // If node==NULL, they should be the same. int diff --git a/Parser/pegen.h b/Parser/pegen.h index fe0c327b875566..2c4b2c3dfc65c6 100644 --- a/Parser/pegen.h +++ b/Parser/pegen.h @@ -143,6 +143,7 @@ expr_ty _PyPegen_name_token(Parser *p); expr_ty _PyPegen_number_token(Parser *p); void *_PyPegen_string_token(Parser *p); Py_ssize_t _PyPegen_byte_offset_to_character_offset(PyObject *line, Py_ssize_t col_offset); +Py_ssize_t _PyPegen_calculate_display_width(PyObject *segment, Py_ssize_t character_offset); // Error handling functions and APIs typedef enum { diff --git a/Python/traceback.c b/Python/traceback.c index c4f5ec877bba5d..130f945c290234 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -907,8 +907,39 @@ tb_displayline(PyTracebackObject* tb, PyObject *f, PyObject *filename, int linen goto done; } - if (print_error_location_carets(f, truncation, start_offset, end_offset, - right_start_offset, left_end_offset, + // Convert all offsets to display offsets (e.g. the space they would take up if printed + // on the screen). + Py_ssize_t dp_start = _PyPegen_calculate_display_width(source_line, start_offset); + if (dp_start < 0) { + err = ignore_source_errors() < 0; + goto done; + } + + Py_ssize_t dp_end = _PyPegen_calculate_display_width(source_line, end_offset); + if (dp_end < 0) { + err = ignore_source_errors() < 0; + goto done; + } + + Py_ssize_t dp_left_end = -1; + Py_ssize_t dp_right_start = -1; + if (has_secondary_ranges) { + dp_left_end = _PyPegen_calculate_display_width(source_line, left_end_offset); + if (dp_left_end < 0) { + err = ignore_source_errors() < 0; + goto done; + } + + dp_right_start = _PyPegen_calculate_display_width(source_line, right_start_offset); + if (dp_right_start < 0) { + err = ignore_source_errors() < 0; + goto done; + } + } + + + if (print_error_location_carets(f, truncation, dp_start, dp_end, + dp_right_start, dp_left_end, primary_error_char, secondary_error_char) < 0) { err = -1; goto done; From e7cdcccd2604ee26669173a6f690c0c2e2ef43bf Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 27 Oct 2023 16:08:53 +0300 Subject: [PATCH 540/632] [3.11] gh-111187: Postpone removal version for locale.getdefaultlocale() to 3.15 (GH-111188) (#111326) --- Doc/library/locale.rst | 2 +- Doc/whatsnew/3.11.rst | 2 +- Lib/locale.py | 9 +++++---- .../2023-10-22-21-28-05.gh-issue-111187._W11Ab.rst | 1 + 4 files changed, 8 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-10-22-21-28-05.gh-issue-111187._W11Ab.rst diff --git a/Doc/library/locale.rst b/Doc/library/locale.rst index 2948105d2925c4..6b9f1d3391997e 100644 --- a/Doc/library/locale.rst +++ b/Doc/library/locale.rst @@ -303,7 +303,7 @@ The :mod:`locale` module defines the following exception and functions: *language code* and *encoding* may be ``None`` if their values cannot be determined. - .. deprecated-removed:: 3.11 3.13 + .. deprecated-removed:: 3.11 3.15 .. function:: getlocale(category=LC_CTYPE) diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index fda4fbcb122dcf..698df284c9fd4a 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -1798,7 +1798,7 @@ Standard Library * :func:`importlib.resources.path` * The :func:`locale.getdefaultlocale` function is deprecated and will be - removed in Python 3.13. Use :func:`locale.setlocale`, + removed in Python 3.15. Use :func:`locale.setlocale`, :func:`locale.getpreferredencoding(False) ` and :func:`locale.getlocale` functions instead. (Contributed by Victor Stinner in :gh:`90817`.) diff --git a/Lib/locale.py b/Lib/locale.py index 7a7694e1bfb71c..f45841ed629e3a 100644 --- a/Lib/locale.py +++ b/Lib/locale.py @@ -556,10 +556,11 @@ def getdefaultlocale(envvars=('LC_ALL', 'LC_CTYPE', 'LANG', 'LANGUAGE')): """ import warnings - warnings.warn( - "Use setlocale(), getencoding() and getlocale() instead", - DeprecationWarning, stacklevel=2 - ) + warnings._deprecated( + "locale.getdefaultlocale", + "{name!r} is deprecated and slated for removal in Python {remove}. " + "Use setlocale(), getencoding() and getlocale() instead.", + remove=(3, 15)) try: # check if it's supported by the _locale module 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 new file mode 100644 index 00000000000000..dc2424370bb96c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-10-22-21-28-05.gh-issue-111187._W11Ab.rst @@ -0,0 +1 @@ +Postpone removal version for locale.getdefaultlocale() to Python 3.15. From a9e0455bd318cac76016a9272c89b4bb0ff9448b Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 27 Oct 2023 17:15:15 +0200 Subject: [PATCH 541/632] [3.11] gh-111276: Clarify docs and comments about the role of LC_CTYPE (GH-111319) (#111392) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix locale.LC_CTYPE documentation to no longer mention string.lower() et al. Those functions were removed in Python 3.0: https://docs.python.org/2/library/string.htmlGH-deprecated-string-functions Also, fix a comment in logging about locale-specific behavior of `str.lower()`. (cherry picked from commit 6d42759c5e47ab62d60a72b4ff15d29864554579) Co-authored-by: Łukasz Langa Co-authored-by: Hugo van Kemenade --- Doc/library/locale.rst | 15 ++++++++++----- Lib/logging/handlers.py | 6 ++---- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Doc/library/locale.rst b/Doc/library/locale.rst index 6b9f1d3391997e..5ea5957eb143cd 100644 --- a/Doc/library/locale.rst +++ b/Doc/library/locale.rst @@ -476,11 +476,16 @@ The :mod:`locale` module defines the following exception and functions: .. data:: LC_CTYPE - .. index:: pair: module; string - - Locale category for the character type functions. Depending on the settings of - this category, the functions of module :mod:`string` dealing with case change - their behaviour. + Locale category for the character type functions. Most importantly, this + category defines the text encoding, i.e. how bytes are interpreted as + Unicode codepoints. See :pep:`538` and :pep:`540` for how this variable + might be automatically coerced to ``C.UTF-8`` to avoid issues created by + invalid settings in containers or incompatible settings passed over remote + SSH connections. + + Python doesn't internally use locale-dependent character transformation functions + from ``ctype.h``. Instead, an internal ``pyctype.h`` provides locale-independent + equivalents like :c:macro:`!Py_TOLOWER`. .. data:: LC_COLLATE diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py index eadd1b6340bcb6..81041488c8b202 100644 --- a/Lib/logging/handlers.py +++ b/Lib/logging/handlers.py @@ -833,10 +833,8 @@ class SysLogHandler(logging.Handler): "local7": LOG_LOCAL7, } - #The map below appears to be trivially lowercasing the key. However, - #there's more to it than meets the eye - in some locales, lowercasing - #gives unexpected results. See SF #1524081: in the Turkish locale, - #"INFO".lower() != "info" + # Originally added to work around GH-43683. Unnecessary since GH-50043 but kept + # for backwards compatibility. priority_map = { "DEBUG" : "debug", "INFO" : "info", From e84b06c05a90c2f32a8e904f0a43ccc8c1cc88e3 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 27 Oct 2023 20:45:52 +0200 Subject: [PATCH 542/632] [3.11] gh-111406: Fix broken link to bpython's site (GH-111407) (#111409) gh-111406: Fix broken link to bpython's site (GH-111407) (cherry picked from commit 8a158a753c48d166ebceae0687e88ae0c0725c02) Co-authored-by: Zack Cerza --- Doc/tutorial/interactive.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/tutorial/interactive.rst b/Doc/tutorial/interactive.rst index 0d3896a4832b59..4e054c4e6c2c32 100644 --- a/Doc/tutorial/interactive.rst +++ b/Doc/tutorial/interactive.rst @@ -51,4 +51,4 @@ bpython_. .. _GNU Readline: https://tiswww.case.edu/php/chet/readline/rltop.html .. _IPython: https://ipython.org/ -.. _bpython: https://www.bpython-interpreter.org/ +.. _bpython: https://bpython-interpreter.org/ From 1a01ca44d61d6d90be159771576d6868515399dd Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 28 Oct 2023 01:36:24 +0200 Subject: [PATCH 543/632] [3.11] gh-110205: Fix asyncio ThreadedChildWatcher._join_threads() (GH-110884) (#111413) - `ThreadedChildWatcher.close()` is now *officially* a no-op; `_join_threads()` never did anything. - Threads created by that class are now named `asyncio-waitpid-NNN`. - `test.test_asyncio.utils.TestCase.close_loop()` now waits for the child watcher's threads, but not forever; if a thread hangs, it raises `RuntimeError`. (cherry picked from commit c3bb10c9303503e7b55a7bdf9acfa6b3bcb699c6) Co-authored-by: Guido van Rossum --- Lib/asyncio/unix_events.py | 11 ++--------- Lib/test/test_asyncio/utils.py | 11 ++++++++--- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/Lib/asyncio/unix_events.py b/Lib/asyncio/unix_events.py index 0d4ba72603e675..77d2670f833fbc 100644 --- a/Lib/asyncio/unix_events.py +++ b/Lib/asyncio/unix_events.py @@ -1363,14 +1363,7 @@ def is_active(self): return True def close(self): - self._join_threads() - - def _join_threads(self): - """Internal: Join all non-daemon threads""" - threads = [thread for thread in list(self._threads.values()) - if thread.is_alive() and not thread.daemon] - for thread in threads: - thread.join() + pass def __enter__(self): return self @@ -1389,7 +1382,7 @@ def __del__(self, _warn=warnings.warn): def add_child_handler(self, pid, callback, *args): loop = events.get_running_loop() thread = threading.Thread(target=self._do_waitpid, - name=f"waitpid-{next(self._pid_counter)}", + name=f"asyncio-waitpid-{next(self._pid_counter)}", args=(loop, pid, callback, args), daemon=True) self._threads[pid] = thread diff --git a/Lib/test/test_asyncio/utils.py b/Lib/test/test_asyncio/utils.py index 7940855b19efed..045e385511bbae 100644 --- a/Lib/test/test_asyncio/utils.py +++ b/Lib/test/test_asyncio/utils.py @@ -548,6 +548,7 @@ def close_loop(loop): else: loop._default_executor.shutdown(wait=True) loop.close() + policy = support.maybe_get_event_loop_policy() if policy is not None: try: @@ -557,9 +558,13 @@ def close_loop(loop): pass else: if isinstance(watcher, asyncio.ThreadedChildWatcher): - threads = list(watcher._threads.values()) - for thread in threads: - thread.join() + # Wait for subprocess to finish, but not forever + for thread in list(watcher._threads.values()): + thread.join(timeout=support.SHORT_TIMEOUT) + if thread.is_alive(): + raise RuntimeError(f"thread {thread} still alive: " + "subprocess still running") + def set_event_loop(self, loop, *, cleanup=True): if loop is None: From da1736b06a40dc2af4ecb587effd3a119cfcca80 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 28 Oct 2023 09:51:57 +0200 Subject: [PATCH 544/632] [3.11] CI: Include Python version in cache.config key (GH-111410) (#111422) CI: Include Python version in cache.config key (GH-111410) * Include Python version in cache.config key, after Python setup * Remove EOL 3.7 from branch triggers (cherry picked from commit 9d4a1a480b65196c3aabbcd2d165d1fb86d0c8e5) Co-authored-by: Hugo van Kemenade --- .github/workflows/build.yml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cebcc3ef182b8f..71901d4cab2ae5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,7 +12,6 @@ on: - '3.10' - '3.9' - '3.8' - - '3.7' pull_request: branches: - 'main' @@ -20,7 +19,6 @@ on: - '3.10' - '3.9' - '3.8' - - '3.7' permissions: contents: read @@ -144,14 +142,14 @@ jobs: if: needs.check_source.outputs.run_tests == 'true' steps: - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 + with: + python-version: '3.x' - name: Restore config.cache uses: actions/cache@v3 with: path: config.cache - key: ${{ github.job }}-${{ runner.os }}-${{ needs.check_source.outputs.config_hash }} - - uses: actions/setup-python@v4 - with: - python-version: '3.x' + key: ${{ github.job }}-${{ runner.os }}-${{ needs.check_source.outputs.config_hash }}-${{ env.pythonLocation }} - name: Install Dependencies run: sudo ./.github/workflows/posix-deps-apt.sh - name: Add ccache to PATH @@ -296,7 +294,7 @@ jobs: - uses: actions/checkout@v4 - name: Register gcc problem matcher run: echo "::add-matcher::.github/problem-matchers/gcc.json" - - name: Install Dependencies + - name: Install dependencies run: sudo ./.github/workflows/posix-deps-apt.sh - name: Configure OpenSSL env vars run: | From 68c03cef245e5baf43b86f77fa1313c17098dc0a Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 29 Oct 2023 01:23:38 +0200 Subject: [PATCH 545/632] [3.11] gh-111426: Remove `test_cmd.test_coverage` (GH-111427) (#111433) gh-111426: Remove `test_cmd.test_coverage` (GH-111427) (cherry picked from commit 66bea2555dc7b3dd18282cc699fe9a22dea50de3) Co-authored-by: Nikita Sobolev --- Lib/test/test_cmd.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/Lib/test/test_cmd.py b/Lib/test/test_cmd.py index 319801c71f776b..28f80766677e59 100644 --- a/Lib/test/test_cmd.py +++ b/Lib/test/test_cmd.py @@ -248,19 +248,9 @@ def load_tests(loader, tests, pattern): tests.addTest(doctest.DocTestSuite()) return tests -def test_coverage(coverdir): - trace = support.import_module('trace') - tracer=trace.Trace(ignoredirs=[sys.base_prefix, sys.base_exec_prefix,], - trace=0, count=1) - tracer.run('import importlib; importlib.reload(cmd); test_main()') - r=tracer.results() - print("Writing coverage results...") - r.write_results(show_missing=True, summary=True, coverdir=coverdir) if __name__ == "__main__": - if "-c" in sys.argv: - test_coverage('/tmp/cmd.cover') - elif "-i" in sys.argv: + if "-i" in sys.argv: samplecmdclass().cmdloop() else: unittest.main() From a10fc6670b439ffe74acf088caeea82ad4726e60 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 29 Oct 2023 20:39:25 +0100 Subject: [PATCH 546/632] [3.11] gh-101100: Fix sphinx warnings in `library/asyncio-eventloop.rst` (GH-111222) (#111470) gh-101100: Fix sphinx warnings in `library/asyncio-eventloop.rst` (GH-111222) * gh-101100: Fix sphinx warnings in `library/asyncio-eventloop.rst` * Update Doc/library/socket.rst * Update asyncio-eventloop.rst * Update socket.rst --------- (cherry picked from commit 46389c32750f79ab3f398a0132cd002e8a64f809) Co-authored-by: Nikita Sobolev Co-authored-by: Hugo van Kemenade --- Doc/library/asyncio-eventloop.rst | 14 ++++++++------ Doc/library/socket.rst | 7 +++++++ Doc/tools/.nitignore | 1 - 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index 5e40b50056d65a..32daaa7bca2123 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -495,7 +495,7 @@ Opening network connections .. versionchanged:: 3.6 - The socket option :py:const:`~socket.TCP_NODELAY` is set by default + The socket option :ref:`socket.TCP_NODELAY ` is set by default for all TCP connections. .. versionchanged:: 3.7 @@ -564,7 +564,7 @@ Opening network connections * *reuse_port* tells the kernel to allow this endpoint to be bound to the same port as other existing endpoints are bound to, so long as they all set this flag when being created. This option is not supported on Windows - and some Unixes. If the :py:const:`~socket.SO_REUSEPORT` constant is not + and some Unixes. If the :ref:`socket.SO_REUSEPORT ` constant is not defined then this capability is unsupported. * *allow_broadcast* tells the kernel to allow this endpoint to send @@ -590,7 +590,8 @@ Opening network connections .. versionchanged:: 3.8.1 The *reuse_address* parameter is no longer supported, as using - :py:const:`~sockets.SO_REUSEADDR` poses a significant security concern for + :ref:`socket.SO_REUSEADDR ` + poses a significant security concern for UDP. Explicitly passing ``reuse_address=True`` will raise an exception. When multiple processes with differing UIDs assign sockets to an @@ -599,7 +600,8 @@ Opening network connections For supported platforms, *reuse_port* can be used as a replacement for similar functionality. With *reuse_port*, - :py:const:`~sockets.SO_REUSEPORT` is used instead, which specifically + :ref:`socket.SO_REUSEPORT ` + is used instead, which specifically prevents processes with differing UIDs from assigning sockets to the same socket address. @@ -741,7 +743,7 @@ Creating network servers .. versionchanged:: 3.6 Added *ssl_handshake_timeout* and *start_serving* parameters. - The socket option :py:const:`~socket.TCP_NODELAY` is set by default + The socket option :ref:`socket.TCP_NODELAY ` is set by default for all TCP connections. .. versionchanged:: 3.11 @@ -1853,7 +1855,7 @@ Set signal handlers for SIGINT and SIGTERM (This ``signals`` example only works on Unix.) -Register handlers for signals :py:data:`SIGINT` and :py:data:`SIGTERM` +Register handlers for signals :const:`~signal.SIGINT` and :const:`~signal.SIGTERM` using the :meth:`loop.add_signal_handler` method:: import asyncio diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index 7193713984f169..b5c1edaf5ad8e0 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -326,6 +326,11 @@ Constants defined then this protocol is unsupported. More constants may be available depending on the system. +.. data:: AF_UNSPEC + + :const:`AF_UNSPEC` means that + :func:`getaddrinfo` should return socket addresses for any + address family (either IPv4, IPv6, or any other) that can be used. .. data:: SOCK_STREAM SOCK_DGRAM @@ -354,6 +359,8 @@ Constants .. versionadded:: 3.2 +.. _socket-unix-constants: + .. data:: SO_* SOMAXCONN MSG_* diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 5dc8bdc61f6ce5..e59d6198032314 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -31,7 +31,6 @@ Doc/howto/urllib2.rst Doc/library/__future__.rst Doc/library/abc.rst Doc/library/ast.rst -Doc/library/asyncio-eventloop.rst Doc/library/asyncio-extending.rst Doc/library/asyncio-policy.rst Doc/library/asyncio-stream.rst From 9a12c23fe417efc21b0c5412355bddbf0c3c6324 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 29 Oct 2023 21:45:51 +0200 Subject: [PATCH 547/632] [3.11] gh-111165: Remove documentation for moved functions (GH-111467) (GH-111472) (cherry picked from commit 4d6bdf8aabcc92303041420a96750fbc52c9f213) --- Doc/library/test.rst | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/Doc/library/test.rst b/Doc/library/test.rst index a27e88fabe2939..7fc2a7c8466f66 100644 --- a/Doc/library/test.rst +++ b/Doc/library/test.rst @@ -489,16 +489,6 @@ The :mod:`test.support` module defines the following functions: rather than looking directly in the path directories. -.. function:: match_test(test) - - Determine whether *test* matches the patterns set in :func:`set_match_tests`. - - -.. function:: set_match_tests(accept_patterns=None, ignore_patterns=None) - - Define match patterns on test filenames and test method names for filtering tests. - - .. function:: setswitchinterval(interval) Set the :func:`sys.setswitchinterval` to the given *interval*. Defines From 78150c6f7bf2b21f0bddaa87e6ab7b2fbaa4a6db Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 30 Oct 2023 18:44:27 +0100 Subject: [PATCH 548/632] [3.11] gh-111284: Make multiprocessing tests with threads faster and more reliable (GH-111285) (GH-111511) (cherry picked from commit 624ace5a2f02715d084c29eaf2211cd0dd550690) Co-authored-by: Serhiy Storchaka --- Lib/test/_test_multiprocessing.py | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 3bcb1d1d46991f..3d581d695bc06e 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -2437,8 +2437,11 @@ def test_namespace(self): # # -def sqr(x, wait=0.0): - time.sleep(wait) +def sqr(x, wait=0.0, event=None): + if event is None: + time.sleep(wait) + else: + event.wait(wait) return x*x def mul(x, y): @@ -2577,10 +2580,18 @@ def test_async(self): self.assertTimingAlmostEqual(get.elapsed, TIMEOUT1) def test_async_timeout(self): - res = self.pool.apply_async(sqr, (6, TIMEOUT2 + support.SHORT_TIMEOUT)) - get = TimingWrapper(res.get) - self.assertRaises(multiprocessing.TimeoutError, get, timeout=TIMEOUT2) - self.assertTimingAlmostEqual(get.elapsed, TIMEOUT2) + p = self.Pool(3) + try: + event = threading.Event() if self.TYPE == 'threads' else None + res = p.apply_async(sqr, (6, TIMEOUT2 + support.SHORT_TIMEOUT, event)) + get = TimingWrapper(res.get) + self.assertRaises(multiprocessing.TimeoutError, get, timeout=TIMEOUT2) + self.assertTimingAlmostEqual(get.elapsed, TIMEOUT2) + finally: + if event is not None: + event.set() + p.terminate() + p.join() def test_imap(self): it = self.pool.imap(sqr, list(range(10))) @@ -2682,10 +2693,11 @@ def test_make_pool(self): def test_terminate(self): # Simulate slow tasks which take "forever" to complete + p = self.Pool(3) args = [support.LONG_TIMEOUT for i in range(10_000)] - result = self.pool.map_async(time.sleep, args, chunksize=1) - self.pool.terminate() - self.pool.join() + result = p.map_async(time.sleep, args, chunksize=1) + p.terminate() + p.join() def test_empty_iterable(self): # See Issue 12157 From a9d5966bce9bb347002d4da7672bf49fe4274ea4 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 31 Oct 2023 02:14:30 +0100 Subject: [PATCH 549/632] [3.11] gh-111347: Remove wrong assertion in test_sendfile (GH-111377) (#111462) gh-111347: Remove wrong assertion in test_sendfile (GH-111377) Windows is different. (cherry picked from commit fa35b9e89b2e207fc8bae9eb0284260d0d922e7a) Co-authored-by: zcxsythenew <30565051+zcxsythenew@users.noreply.github.com> --- Lib/test/test_asyncio/test_sendfile.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_asyncio/test_sendfile.py b/Lib/test/test_asyncio/test_sendfile.py index 0198da21d77028..d33ff197bbfa1d 100644 --- a/Lib/test/test_asyncio/test_sendfile.py +++ b/Lib/test/test_asyncio/test_sendfile.py @@ -470,8 +470,11 @@ def test_sendfile_close_peer_in_the_middle_of_receiving(self): self.assertTrue(1024 <= srv_proto.nbytes < len(self.DATA), srv_proto.nbytes) - self.assertTrue(1024 <= self.file.tell() < len(self.DATA), - self.file.tell()) + if not (sys.platform == 'win32' + and isinstance(self.loop, asyncio.ProactorEventLoop)): + # On Windows, Proactor uses transmitFile, which does not update tell() + self.assertTrue(1024 <= self.file.tell() < len(self.DATA), + self.file.tell()) self.assertTrue(cli_proto.transport.is_closing()) def test_sendfile_fallback_close_peer_in_the_middle_of_receiving(self): From c5f6c6396d15549c1e68094ed9811ae4316863f5 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 31 Oct 2023 06:47:57 +0100 Subject: [PATCH 550/632] [3.11] Remove myself from typing CODEOWNERS (GH-111523) (#111526) Remove myself from typing CODEOWNERS (GH-111523) (cherry picked from commit 804a207c168b876112984709edc3a94afa433c69) Co-authored-by: Ken Jin --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 1b156c829cd9aa..6c29ba321b923f 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -137,7 +137,7 @@ Lib/ast.py @isidentical **/*idlelib* @terryjreedy -**/*typing* @gvanrossum @Fidget-Spinner @JelleZijlstra @AlexWaygood +**/*typing* @gvanrossum @JelleZijlstra @AlexWaygood **/*asyncore @giampaolo **/*asynchat @giampaolo From c66f0bedebeb0f63f02999fdf5c9ce9e045ea97d Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 31 Oct 2023 08:13:29 +0100 Subject: [PATCH 551/632] [3.11] gh-111531: Tkinter: fix reference leaks in bind_class() and bind_all() (GH-111533) (GH-111536) (cherry picked from commit e3353c498d79f0f3f108a9baf8807a12e77c2ebe) Co-authored-by: Serhiy Storchaka --- Lib/tkinter/__init__.py | 4 ++-- .../Library/2023-10-31-07-46-56.gh-issue-111531.6zUV_G.rst | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-10-31-07-46-56.gh-issue-111531.6zUV_G.rst diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index 6ae9839055382c..704e7b8364b8ea 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -1459,7 +1459,7 @@ def bind_all(self, sequence=None, func=None, add=None): An additional boolean parameter ADD specifies whether FUNC will be called additionally to the other bound function or whether it will replace the previous function. See bind for the return value.""" - return self._bind(('bind', 'all'), sequence, func, add, 0) + return self._root()._bind(('bind', 'all'), sequence, func, add, True) def unbind_all(self, sequence): """Unbind for all widgets for event SEQUENCE all functions.""" @@ -1473,7 +1473,7 @@ def bind_class(self, className, sequence=None, func=None, add=None): whether it will replace the previous function. See bind for the return value.""" - return self._bind(('bind', className), sequence, func, add, 0) + return self._root()._bind(('bind', className), sequence, func, add, True) def unbind_class(self, className, sequence): """Unbind for all widgets with bindtag CLASSNAME for event SEQUENCE 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 new file mode 100644 index 00000000000000..b722f0414184b1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-10-31-07-46-56.gh-issue-111531.6zUV_G.rst @@ -0,0 +1,2 @@ +Fix reference leaks in ``bind_class()`` and ``bind_all()`` methods of +:mod:`tkinter` widgets. From 08e4e11b758517ad614d71ff2377dd4057ffdcb1 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 31 Oct 2023 14:29:42 +0100 Subject: [PATCH 552/632] [3.11] gh-111380: Show SyntaxWarnings only once when parsing if invalid syntax is encouintered (GH-111381) (#111383) gh-111380: Show SyntaxWarnings only once when parsing if invalid syntax is encouintered (GH-111381) (cherry picked from commit 3d2f1f0b830d86f16f42c42b54d3ea4453dac318) Co-authored-by: Pablo Galindo Salgado --- Lib/test/test_string_literals.py | 12 ++++++++++++ .../2023-10-27-11-51-40.gh-issue-111380.vgSbir.rst | 2 ++ Parser/string_parser.c | 5 +++++ 3 files changed, 19 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-27-11-51-40.gh-issue-111380.vgSbir.rst diff --git a/Lib/test/test_string_literals.py b/Lib/test/test_string_literals.py index 7247b7e48bc2b6..aeec703d5f5abd 100644 --- a/Lib/test/test_string_literals.py +++ b/Lib/test/test_string_literals.py @@ -131,6 +131,18 @@ def test_eval_str_invalid_escape(self): self.assertEqual(exc.lineno, 1) self.assertEqual(exc.offset, 1) + # Check that the warning is raised ony once if there are syntax errors + + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter('always', category=DeprecationWarning) + with self.assertRaises(SyntaxError) as cm: + eval("'\\e' $") + exc = cm.exception + self.assertEqual(len(w), 1) + self.assertEqual(w[0].category, DeprecationWarning) + self.assertRegex(str(w[0].message), 'invalid escape sequence') + self.assertEqual(w[0].filename, '') + def test_eval_str_invalid_octal_escape(self): for i in range(0o400, 0o1000): with self.assertWarns(DeprecationWarning): 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 new file mode 100644 index 00000000000000..4ce6398dbfe3b1 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-10-27-11-51-40.gh-issue-111380.vgSbir.rst @@ -0,0 +1,2 @@ +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/Parser/string_parser.c b/Parser/string_parser.c index fb2b9808af15a2..7079b82d04f8ec 100644 --- a/Parser/string_parser.c +++ b/Parser/string_parser.c @@ -11,6 +11,11 @@ static int warn_invalid_escape_sequence(Parser *p, const char *first_invalid_escape, Token *t) { + if (p->call_invalid_rules) { + // Do not report warnings if we are in the second pass of the parser + // to avoid showing the warning twice. + return 0; + } unsigned char c = *first_invalid_escape; int octal = ('4' <= c && c <= '7'); PyObject *msg = From bc9f47097ef5c44c87cb91a441c19ee532907684 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Tue, 31 Oct 2023 17:00:39 +0300 Subject: [PATCH 553/632] [3.11] gh-108303: Move all inspect test files to `test_inspect/` (GH-109607) (#111543) --- Lib/test/libregrtest/findtests.py | 1 + Lib/test/test_import/__init__.py | 1 - Lib/test/test_inspect/__init__.py | 6 ++++++ Lib/test/{ => test_inspect}/inspect_fodder.py | 0 Lib/test/{ => test_inspect}/inspect_fodder2.py | 0 .../{ => test_inspect}/inspect_stock_annotations.py | 0 .../inspect_stringized_annotations.py | 0 .../inspect_stringized_annotations_2.py | 0 Lib/test/{ => test_inspect}/test_inspect.py | 11 ++++++----- Lib/test/test_tokenize.py | 2 +- Makefile.pre.in | 1 + 11 files changed, 15 insertions(+), 7 deletions(-) create mode 100644 Lib/test/test_inspect/__init__.py rename Lib/test/{ => test_inspect}/inspect_fodder.py (100%) rename Lib/test/{ => test_inspect}/inspect_fodder2.py (100%) rename Lib/test/{ => test_inspect}/inspect_stock_annotations.py (100%) rename Lib/test/{ => test_inspect}/inspect_stringized_annotations.py (100%) rename Lib/test/{ => test_inspect}/inspect_stringized_annotations_2.py (100%) rename Lib/test/{ => test_inspect}/test_inspect.py (99%) diff --git a/Lib/test/libregrtest/findtests.py b/Lib/test/libregrtest/findtests.py index a4235b52eca0af..78343775bc5b99 100644 --- a/Lib/test/libregrtest/findtests.py +++ b/Lib/test/libregrtest/findtests.py @@ -21,6 +21,7 @@ "test_concurrent_futures", "test_future_stmt", "test_gdb", + "test_inspect", "test_multiprocessing_fork", "test_multiprocessing_forkserver", "test_multiprocessing_spawn", diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index 131aebbd4e1f56..8632ac2e818fab 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -1,5 +1,4 @@ import builtins -import contextlib import errno import glob import importlib.util diff --git a/Lib/test/test_inspect/__init__.py b/Lib/test/test_inspect/__init__.py new file mode 100644 index 00000000000000..f2a39a3fe29c7f --- /dev/null +++ b/Lib/test/test_inspect/__init__.py @@ -0,0 +1,6 @@ +import os +from test import support + + +def load_tests(*args): + return support.load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/inspect_fodder.py b/Lib/test/test_inspect/inspect_fodder.py similarity index 100% rename from Lib/test/inspect_fodder.py rename to Lib/test/test_inspect/inspect_fodder.py diff --git a/Lib/test/inspect_fodder2.py b/Lib/test/test_inspect/inspect_fodder2.py similarity index 100% rename from Lib/test/inspect_fodder2.py rename to Lib/test/test_inspect/inspect_fodder2.py diff --git a/Lib/test/inspect_stock_annotations.py b/Lib/test/test_inspect/inspect_stock_annotations.py similarity index 100% rename from Lib/test/inspect_stock_annotations.py rename to Lib/test/test_inspect/inspect_stock_annotations.py diff --git a/Lib/test/inspect_stringized_annotations.py b/Lib/test/test_inspect/inspect_stringized_annotations.py similarity index 100% rename from Lib/test/inspect_stringized_annotations.py rename to Lib/test/test_inspect/inspect_stringized_annotations.py diff --git a/Lib/test/inspect_stringized_annotations_2.py b/Lib/test/test_inspect/inspect_stringized_annotations_2.py similarity index 100% rename from Lib/test/inspect_stringized_annotations_2.py rename to Lib/test/test_inspect/inspect_stringized_annotations_2.py diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect/test_inspect.py similarity index 99% rename from Lib/test/test_inspect.py rename to Lib/test/test_inspect/test_inspect.py index 650d56e39c415c..6d4050a5ded100 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -30,12 +30,13 @@ from test.support.import_helper import DirsOnSysPath, ready_to_import from test.support.os_helper import TESTFN from test.support.script_helper import assert_python_ok, assert_python_failure -from test import inspect_fodder as mod -from test import inspect_fodder2 as mod2 from test import support -from test import inspect_stock_annotations -from test import inspect_stringized_annotations -from test import inspect_stringized_annotations_2 + +from . import inspect_fodder as mod +from . import inspect_fodder2 as mod2 +from . import inspect_stock_annotations +from . import inspect_stringized_annotations +from . import inspect_stringized_annotations_2 # Functions tested in this suite: diff --git a/Lib/test/test_tokenize.py b/Lib/test/test_tokenize.py index 62f152fe431d5b..b5f57f693474d2 100644 --- a/Lib/test/test_tokenize.py +++ b/Lib/test/test_tokenize.py @@ -1624,7 +1624,7 @@ def test_random_files(self): # 7 more testfiles fail. Remove them also until the failure is diagnosed. testfiles.remove(os.path.join(tempdir, "test_unicode_identifiers.py")) - for f in ('buffer', 'builtin', 'fileio', 'inspect', 'os', 'platform', 'sys'): + for f in ('buffer', 'builtin', 'fileio', 'os', 'platform', 'sys'): testfiles.remove(os.path.join(tempdir, "test_%s.py") % f) if not support.is_resource_enabled("cpu"): diff --git a/Makefile.pre.in b/Makefile.pre.in index 49238902718eb8..ada866d83c0423 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1969,6 +1969,7 @@ TESTSUBDIRS= ctypes/test \ test/test_email/data \ test/test_future_stmt \ test/test_gdb \ + test/test_inspect \ test/test_import \ test/test_import/data \ test/test_import/data/circular_imports \ From 19a266ca8961d8dcb85799adb1025e4f4fb2f087 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Salgado Date: Tue, 31 Oct 2023 14:41:20 +0000 Subject: [PATCH 554/632] [3.11] gh-111366: Correctly show custom syntax error messages in the codeop module functions (GH-111384). (#111516) (cherry picked from commit cd6e0a04a16535d8bc727c84f73730c53267184e) --- Lib/codeop.py | 19 +++++++++++++------ Lib/test/test_codeop.py | 14 ++++++++++++++ ...-10-27-12-17-49.gh-issue-111366._TSknV.rst | 3 +++ 3 files changed, 30 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-27-12-17-49.gh-issue-111366._TSknV.rst diff --git a/Lib/codeop.py b/Lib/codeop.py index 2213b69f231f92..e64911ee5ad417 100644 --- a/Lib/codeop.py +++ b/Lib/codeop.py @@ -70,8 +70,7 @@ def _maybe_compile(compiler, source, filename, symbol): return None # fallthrough - return compiler(source, filename, symbol) - + return compiler(source, filename, symbol, incomplete_input=False) def _is_syntax_error(err1, err2): rep1 = repr(err1) @@ -82,8 +81,12 @@ def _is_syntax_error(err1, err2): return True return False -def _compile(source, filename, symbol): - return compile(source, filename, symbol, PyCF_DONT_IMPLY_DEDENT | PyCF_ALLOW_INCOMPLETE_INPUT) +def _compile(source, filename, symbol, incomplete_input=True): + flags = 0 + if incomplete_input: + flags |= PyCF_ALLOW_INCOMPLETE_INPUT + flags |= PyCF_DONT_IMPLY_DEDENT + return compile(source, filename, symbol, flags) def compile_command(source, filename="", symbol="single"): r"""Compile a command and determine whether it is incomplete. @@ -114,8 +117,12 @@ class Compile: def __init__(self): self.flags = PyCF_DONT_IMPLY_DEDENT | PyCF_ALLOW_INCOMPLETE_INPUT - def __call__(self, source, filename, symbol): - codeob = compile(source, filename, symbol, self.flags, True) + def __call__(self, source, filename, symbol, **kwargs): + flags = self.flags + if kwargs.get('incomplete_input', True) is False: + flags &= ~PyCF_DONT_IMPLY_DEDENT + flags &= ~PyCF_ALLOW_INCOMPLETE_INPUT + codeob = compile(source, filename, symbol, flags, True) for feature in _features: if codeob.co_flags & feature.compiler_flag: self.flags |= feature.compiler_flag diff --git a/Lib/test/test_codeop.py b/Lib/test/test_codeop.py index 133096d25a44bc..94ea06f79d3618 100644 --- a/Lib/test/test_codeop.py +++ b/Lib/test/test_codeop.py @@ -7,6 +7,7 @@ import warnings from test import support from test.support import warnings_helper +from textwrap import dedent from codeop import compile_command, PyCF_DONT_IMPLY_DEDENT import io @@ -341,6 +342,19 @@ def test_invalid_warning(self): self.assertRegex(str(w[0].message), 'invalid escape sequence') self.assertEqual(w[0].filename, '') + def assertSyntaxErrorMatches(self, code, message): + with self.subTest(code): + with self.assertRaisesRegex(SyntaxError, message): + compile_command(code, symbol='exec') + + def test_syntax_errors(self): + self.assertSyntaxErrorMatches( + dedent("""\ + def foo(x,x): + pass + """), "duplicate argument 'x' in function definition") + + if __name__ == "__main__": unittest.main() 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 new file mode 100644 index 00000000000000..7e76ce916ea714 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-10-27-12-17-49.gh-issue-111366._TSknV.rst @@ -0,0 +1,3 @@ +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 From a4aa213b65ab4c9327dc9099eb03c4ac92182884 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Salgado Date: Tue, 31 Oct 2023 15:59:31 +0000 Subject: [PATCH 555/632] [3.11] gh-109181: Speed up Traceback object creation by lazily compute the line number (GH-111548) (#111550) . (cherry picked from commit abb15420c11d9dda9c89f74eac8417240b321109) --- ...-10-31-14-25-21.gh-issue-109181.11h6Mc.rst | 2 ++ Python/traceback.c | 35 +++++++++++++++---- 2 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-31-14-25-21.gh-issue-109181.11h6Mc.rst 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 new file mode 100644 index 00000000000000..61a15b471cfb27 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-10-31-14-25-21.gh-issue-109181.11h6Mc.rst @@ -0,0 +1,2 @@ +Speed up :obj:`Traceback` object creation by lazily compute the line number. +Patch by Pablo Galindo diff --git a/Python/traceback.c b/Python/traceback.c index 130f945c290234..664a73fdfbaa31 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -111,6 +111,26 @@ tb_next_get(PyTracebackObject *self, void *Py_UNUSED(_)) return ret; } +static int +tb_get_lineno(PyTracebackObject* tb) { + PyFrameObject* frame = tb->tb_frame; + assert(frame != NULL); + return PyCode_Addr2Line(PyFrame_GetCode(frame), tb->tb_lasti); +} + +static PyObject * +tb_lineno_get(PyTracebackObject *self, void *Py_UNUSED(_)) +{ + int lineno = self->tb_lineno; + if (lineno == -1) { + lineno = tb_get_lineno(self); + if (lineno < 0) { + Py_RETURN_NONE; + } + } + return PyLong_FromLong(lineno); +} + static int tb_next_set(PyTracebackObject *self, PyObject *new_next, void *Py_UNUSED(_)) { @@ -157,12 +177,12 @@ static PyMethodDef tb_methods[] = { static PyMemberDef tb_memberlist[] = { {"tb_frame", T_OBJECT, OFF(tb_frame), READONLY|PY_AUDIT_READ}, {"tb_lasti", T_INT, OFF(tb_lasti), READONLY}, - {"tb_lineno", T_INT, OFF(tb_lineno), READONLY}, {NULL} /* Sentinel */ }; static PyGetSetDef tb_getsetters[] = { {"tb_next", (getter)tb_next_get, (setter)tb_next_set, NULL, NULL}, + {"tb_lineno", (getter)tb_lineno_get, NULL, NULL, NULL}, {NULL} /* Sentinel */ }; @@ -241,8 +261,7 @@ _PyTraceBack_FromFrame(PyObject *tb_next, PyFrameObject *frame) assert(tb_next == NULL || PyTraceBack_Check(tb_next)); assert(frame != NULL); int addr = _PyInterpreterFrame_LASTI(frame->f_frame) * sizeof(_Py_CODEUNIT); - return tb_create_raw((PyTracebackObject *)tb_next, frame, addr, - PyFrame_GetLineNumber(frame)); + return tb_create_raw((PyTracebackObject *)tb_next, frame, addr, -1); } @@ -990,9 +1009,13 @@ tb_printinternal(PyTracebackObject *tb, PyObject *f, long limit, } while (tb != NULL) { code = PyFrame_GetCode(tb->tb_frame); + int tb_lineno = tb->tb_lineno; + if (tb_lineno == -1) { + tb_lineno = tb_get_lineno(tb); + } if (last_file == NULL || code->co_filename != last_file || - last_line == -1 || tb->tb_lineno != last_line || + last_line == -1 || tb_lineno != last_line || last_name == NULL || code->co_name != last_name) { if (cnt > TB_RECURSIVE_CUTOFF) { if (tb_print_line_repeated(f, cnt) < 0) { @@ -1000,13 +1023,13 @@ tb_printinternal(PyTracebackObject *tb, PyObject *f, long limit, } } last_file = code->co_filename; - last_line = tb->tb_lineno; + last_line = tb_lineno; last_name = code->co_name; cnt = 0; } cnt++; if (cnt <= TB_RECURSIVE_CUTOFF) { - if (tb_displayline(tb, f, code->co_filename, tb->tb_lineno, + if (tb_displayline(tb, f, code->co_filename, tb_lineno, tb->tb_frame, code->co_name, indent, margin) < 0) { goto error; } From be21fb6369d0c1ab3f7642e734fa46594dcbbf8c Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 31 Oct 2023 17:24:17 +0100 Subject: [PATCH 556/632] [3.11] gh-106861: Docs: Add availability directives to all Unix-only modules (GH-108975) (#111554) Co-authored-by: xzmeng --- Doc/library/fcntl.rst | 2 +- Doc/library/grp.rst | 2 +- Doc/library/posix.rst | 2 ++ Doc/library/pty.rst | 2 ++ Doc/library/pwd.rst | 2 +- Doc/library/resource.rst | 2 +- Doc/library/syslog.rst | 4 ++-- Doc/library/termios.rst | 2 ++ Doc/library/tty.rst | 2 ++ 9 files changed, 14 insertions(+), 6 deletions(-) diff --git a/Doc/library/fcntl.rst b/Doc/library/fcntl.rst index 1951f80f91e663..c417a84e048f8a 100644 --- a/Doc/library/fcntl.rst +++ b/Doc/library/fcntl.rst @@ -18,7 +18,7 @@ interface to the :c:func:`fcntl` and :c:func:`ioctl` Unix routines. For a complete description of these calls, see :manpage:`fcntl(2)` and :manpage:`ioctl(2)` Unix manual pages. -.. include:: ../includes/wasm-notavail.rst +.. availability:: Unix, not Emscripten, not WASI. All functions in this module take a file descriptor *fd* as their first argument. This can be an integer file descriptor, such as returned by diff --git a/Doc/library/grp.rst b/Doc/library/grp.rst index 14af744e3ae8f4..ee55b12ea8643a 100644 --- a/Doc/library/grp.rst +++ b/Doc/library/grp.rst @@ -10,7 +10,7 @@ This module provides access to the Unix group database. It is available on all Unix versions. -.. include:: ../includes/wasm-notavail.rst +.. availability:: Unix, not Emscripten, not WASI. Group database entries are reported as a tuple-like object, whose attributes correspond to the members of the ``group`` structure (Attribute field below, see diff --git a/Doc/library/posix.rst b/Doc/library/posix.rst index 0413f9d02a8d57..5871574b442667 100644 --- a/Doc/library/posix.rst +++ b/Doc/library/posix.rst @@ -11,6 +11,8 @@ This module provides access to operating system functionality that is standardized by the C Standard and the POSIX standard (a thinly disguised Unix interface). +.. availability:: Unix. + .. index:: pair: module; os **Do not import this module directly.** Instead, import the module :mod:`os`, diff --git a/Doc/library/pty.rst b/Doc/library/pty.rst index ad4981c97119fa..af9378464edb9f 100644 --- a/Doc/library/pty.rst +++ b/Doc/library/pty.rst @@ -16,6 +16,8 @@ The :mod:`pty` module defines operations for handling the pseudo-terminal concept: starting another process and being able to write to and read from its controlling terminal programmatically. +.. availability:: Unix. + Pseudo-terminal handling is highly platform dependent. This code is mainly tested on Linux, FreeBSD, and macOS (it is supposed to work on other POSIX platforms but it's not been thoroughly tested). diff --git a/Doc/library/pwd.rst b/Doc/library/pwd.rst index 7cafc66fd7e93c..755f0d29ac7345 100644 --- a/Doc/library/pwd.rst +++ b/Doc/library/pwd.rst @@ -10,7 +10,7 @@ This module provides access to the Unix user account and password database. It is available on all Unix versions. -.. include:: ../includes/wasm-notavail.rst +.. availability:: Unix, not Emscripten, not WASI. Password database entries are reported as a tuple-like object, whose attributes correspond to the members of the ``passwd`` structure (Attribute field below, diff --git a/Doc/library/resource.rst b/Doc/library/resource.rst index a5324c82c63484..ef65674d1b0a78 100644 --- a/Doc/library/resource.rst +++ b/Doc/library/resource.rst @@ -13,7 +13,7 @@ This module provides basic mechanisms for measuring and controlling system resources utilized by a program. -.. include:: ../includes/wasm-notavail.rst +.. availability:: Unix, not Emscripten, not WASI. Symbolic constants are used to specify particular system resources and to request usage information about either the current process or its children. diff --git a/Doc/library/syslog.rst b/Doc/library/syslog.rst index 766ff57cc66d69..889bbb39d58232 100644 --- a/Doc/library/syslog.rst +++ b/Doc/library/syslog.rst @@ -11,12 +11,12 @@ This module provides an interface to the Unix ``syslog`` library routines. Refer to the Unix manual pages for a detailed description of the ``syslog`` facility. +.. availability:: Unix, not Emscripten, not WASI. + This module wraps the system ``syslog`` family of routines. A pure Python library that can speak to a syslog server is available in the :mod:`logging.handlers` module as :class:`SysLogHandler`. -.. include:: ../includes/wasm-notavail.rst - The module defines the following functions: diff --git a/Doc/library/termios.rst b/Doc/library/termios.rst index 03806178e9d3fb..57705ddc4e6470 100644 --- a/Doc/library/termios.rst +++ b/Doc/library/termios.rst @@ -16,6 +16,8 @@ complete description of these calls, see :manpage:`termios(3)` Unix manual page. It is only available for those Unix versions that support POSIX *termios* style tty I/O control configured during installation. +.. availability:: Unix. + All functions in this module take a file descriptor *fd* as their first argument. This can be an integer file descriptor, such as returned by ``sys.stdin.fileno()``, or a :term:`file object`, such as ``sys.stdin`` itself. diff --git a/Doc/library/tty.rst b/Doc/library/tty.rst index b30bc3c7ac42e9..75ba6c4523e5e7 100644 --- a/Doc/library/tty.rst +++ b/Doc/library/tty.rst @@ -15,6 +15,8 @@ The :mod:`tty` module defines functions for putting the tty into cbreak and raw modes. +.. availability:: Unix. + Because it requires the :mod:`termios` module, it will work only on Unix. The :mod:`tty` module defines the following functions: From 650eb29b4941efae6405478763b605c5852cdc61 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 31 Oct 2023 17:24:41 +0100 Subject: [PATCH 557/632] [3.11] gh-93607: document `root` attribute of `iterparse` (GH-99410) (#111556) Co-authored-by: Prometheus3375 <35541026+Prometheus3375@users.noreply.github.com> Co-authored-by: Stanley <46876382+slateny@users.noreply.github.com> Co-authored-by: Hugo van Kemenade --- Doc/library/xml.etree.elementtree.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Doc/library/xml.etree.elementtree.rst b/Doc/library/xml.etree.elementtree.rst index 7fd4c7a4a5da2b..31135a7e613c97 100644 --- a/Doc/library/xml.etree.elementtree.rst +++ b/Doc/library/xml.etree.elementtree.rst @@ -622,7 +622,9 @@ Functions *parser* is an optional parser instance. If not given, the standard :class:`XMLParser` parser is used. *parser* must be a subclass of :class:`XMLParser` and can only use the default :class:`TreeBuilder` as a - target. Returns an :term:`iterator` providing ``(event, elem)`` pairs. + target. Returns an :term:`iterator` providing ``(event, elem)`` pairs; + it has a ``root`` attribute that references the root element of the + resulting XML tree once *source* is fully read. Note that while :func:`iterparse` builds the tree incrementally, it issues blocking reads on *source* (or the file it names). As such, it's unsuitable From 2bb10acfdd9782e36534a1cb95bec520d0338ab6 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 31 Oct 2023 17:40:17 +0100 Subject: [PATCH 558/632] [3.11] gh-102249: Expand sys.call_tracing documentation (GH-102806) (#111558) Co-authored-by: Quentin Peter Co-authored-by: C.A.M. Gerlach --- Doc/library/sys.rst | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 064a9c47137b30..f5b94f567ff749 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -173,7 +173,11 @@ always available. Call ``func(*args)``, while tracing is enabled. The tracing state is saved, and restored afterwards. This is intended to be called from a debugger from - a checkpoint, to recursively debug some other code. + a checkpoint, to recursively debug or profile some other code. + + Tracing is suspended while calling a tracing function set by + :func:`settrace` or :func:`setprofile` to avoid infinite recursion. + :func:`!call_tracing` enables explicit recursion of the tracing function. .. data:: copyright @@ -1439,13 +1443,16 @@ always available. its return value is not used, so it can simply return ``None``. Error in the profile function will cause itself unset. + .. note:: + The same tracing mechanism is used for :func:`!setprofile` as :func:`settrace`. + To trace calls with :func:`!setprofile` inside a tracing function + (e.g. in a debugger breakpoint), see :func:`call_tracing`. + Profile functions should have three arguments: *frame*, *event*, and *arg*. *frame* is the current stack frame. *event* is a string: ``'call'``, ``'return'``, ``'c_call'``, ``'c_return'``, or ``'c_exception'``. *arg* depends on the event type. - .. audit-event:: sys.setprofile "" sys.setprofile - The events have the following meaning: ``'call'`` @@ -1467,6 +1474,9 @@ always available. ``'c_exception'`` A C function has raised an exception. *arg* is the C function object. + .. audit-event:: sys.setprofile "" sys.setprofile + + .. function:: setrecursionlimit(limit) Set the maximum depth of the Python interpreter stack to *limit*. This limit @@ -1526,6 +1536,10 @@ always available. If there is any error occurred in the trace function, it will be unset, just like ``settrace(None)`` is called. + .. note:: + Tracing is disabled while calling the trace function (e.g. a function set by + :func:`!settrace`). For recursive tracing see :func:`call_tracing`. + The events have the following meaning: ``'call'`` From 1d7ad7780a9365bddfa6ba9dbba2e390ca55fba7 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 1 Nov 2023 04:57:23 +0100 Subject: [PATCH 559/632] [3.11] gh-110367: Make regrtest --verbose3 compatible with --huntrleaks -jN (GH-111577) (#111590) gh-110367: Make regrtest --verbose3 compatible with --huntrleaks -jN (GH-111577) "./python -m test -j1 -R 3:3 --verbose3" now works as expected, since run_single_test() does not replace sys.stdout with StringIO in this case. (cherry picked from commit d9a5530d2327efa1fe66a04d31b5c67e42dbcd9c) Co-authored-by: Victor Stinner --- Lib/test/libregrtest/cmdline.py | 10 ++++++-- Lib/test/test_regrtest.py | 23 +++++++++++++++++++ ...-10-31-22-09-25.gh-issue-110367.UhQi44.rst | 3 +++ 3 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2023-10-31-22-09-25.gh-issue-110367.UhQi44.rst diff --git a/Lib/test/libregrtest/cmdline.py b/Lib/test/libregrtest/cmdline.py index 905b3f862281f1..9ba6ec63cad9f8 100644 --- a/Lib/test/libregrtest/cmdline.py +++ b/Lib/test/libregrtest/cmdline.py @@ -494,10 +494,16 @@ def _parse_args(args, **kwargs): ns.randomize = True if ns.verbose: ns.header = True - if ns.huntrleaks and ns.verbose3: + # When -jN option is used, a worker process does not use --verbose3 + # and so -R 3:3 -jN --verbose3 just works as expected: there is no false + # alarm about memory leak. + if ns.huntrleaks and ns.verbose3 and ns.use_mp is None: ns.verbose3 = False + # run_single_test() replaces sys.stdout with io.StringIO if verbose3 + # is true. In this case, huntrleaks sees an write into StringIO as + # a memory leak, whereas it is not (gh-71290). print("WARNING: Disable --verbose3 because it's incompatible with " - "--huntrleaks: see http://bugs.python.org/issue27103", + "--huntrleaks without -jN option", file=sys.stderr) if ns.forever: # --forever implies --failfast diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py index aa3f65487a288e..2f1bb03bc0ba4e 100644 --- a/Lib/test/test_regrtest.py +++ b/Lib/test/test_regrtest.py @@ -2120,6 +2120,29 @@ def test_crash(self): self.assertIn(f"Exit code {exitcode} (SIGSEGV)", output) self.check_line(output, "just before crash!", full=True, regex=False) + def test_verbose3(self): + code = textwrap.dedent(r""" + import unittest + from test import support + + class VerboseTests(unittest.TestCase): + def test_pass(self): + print("SPAM SPAM SPAM") + """) + testname = self.create_test(code=code) + + # Run sequentially + output = self.run_tests("--verbose3", testname) + self.check_executed_tests(output, testname, stats=1) + self.assertNotIn('SPAM SPAM SPAM', output) + + # -R option needs a debug build + if support.Py_DEBUG: + # Check for reference leaks, run in parallel + output = self.run_tests("-R", "3:3", "-j1", "--verbose3", testname) + self.check_executed_tests(output, testname, stats=1, parallel=True) + self.assertNotIn('SPAM SPAM SPAM', output) + class TestUtils(unittest.TestCase): def test_format_duration(self): 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 new file mode 100644 index 00000000000000..0254288d3626cc --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-10-31-22-09-25.gh-issue-110367.UhQi44.rst @@ -0,0 +1,3 @@ +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. From cf6145453ff3a7bdc0790ac6d95b5b097c489d45 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 1 Nov 2023 11:43:37 +0100 Subject: [PATCH 560/632] [3.11] gh-111576: Improve documention for tkinter.messagebox (GH-111578) (GH-111598) (cherry picked from commit eaf67e37a2da28c1241362e3b4ff1202945c83c5) Co-authored-by: Serhiy Storchaka --- Doc/library/tkinter.messagebox.rst | 175 +++++++++++++++++++++++++++-- 1 file changed, 165 insertions(+), 10 deletions(-) diff --git a/Doc/library/tkinter.messagebox.rst b/Doc/library/tkinter.messagebox.rst index 56c1d6c132afd2..56090a0a0e424b 100644 --- a/Doc/library/tkinter.messagebox.rst +++ b/Doc/library/tkinter.messagebox.rst @@ -11,7 +11,8 @@ The :mod:`tkinter.messagebox` module provides a template base class as well as a variety of convenience methods for commonly used configurations. The message -boxes are modal and will return a subset of (True, False, OK, None, Yes, No) based on +boxes are modal and will return a subset of (``True``, ``False``, ``None``, +:data:`OK`, :data:`CANCEL`, :data:`YES`, :data:`NO`) based on the user's selection. Common message box styles and layouts include but are not limited to: @@ -19,21 +20,175 @@ limited to: .. class:: Message(master=None, **options) - Create a default information message box. + Create a message window with an application-specified message, an icon + and a set of buttons. + Each of the buttons in the message window is identified by a unique symbolic name (see the *type* options). + + The following options are supported: + + *command* + Specifies the function to invoke when the user closes the dialog. + The name of the button clicked by the user to close the dialog is + passed as argument. + This is only available on macOS. + + *default* + Gives the :ref:`symbolic name ` of the default button + for this message window (:data:`OK`, :data:`CANCEL`, and so on). + If this option is not specified, the first button in the dialog will + be made the default. + + *detail* + Specifies an auxiliary message to the main message given by the + *message* option. + The message detail will be presented beneath the main message and, + where supported by the OS, in a less emphasized font than the main + message. + + *icon* + Specifies an :ref:`icon ` to display. + If this option is not specified, then the :data:`INFO` icon will be + displayed. + + *message* + Specifies the message to display in this message box. + The default value is an empty string. + + *parent* + Makes the specified window the logical parent of the message box. + The message box is displayed on top of its parent window. + + *title* + Specifies a string to display as the title of the message box. + This option is ignored on macOS, where platform guidelines forbid + the use of a title on this kind of dialog. + + *type* + Arranges for a :ref:`predefined set of buttons ` + to be displayed. + + .. method:: show(**options) + + Display a message window and wait for the user to select one of the buttons. Then return the symbolic name of the selected button. + Keyword arguments can override options specified in the constructor. + **Information message box** -.. method:: showinfo(title=None, message=None, **options) +.. function:: showinfo(title=None, message=None, **options) + + Creates and displays an information message box with the specified title + and message. **Warning message boxes** -.. method:: showwarning(title=None, message=None, **options) - showerror(title=None, message=None, **options) +.. function:: showwarning(title=None, message=None, **options) + + Creates and displays a warning message box with the specified title + and message. + +.. function:: showerror(title=None, message=None, **options) + + Creates and displays an error message box with the specified title + and message. **Question message boxes** -.. method:: askquestion(title=None, message=None, **options) - askokcancel(title=None, message=None, **options) - askretrycancel(title=None, message=None, **options) - askyesno(title=None, message=None, **options) - askyesnocancel(title=None, message=None, **options) +.. function:: askquestion(title=None, message=None, *, type=YESNO, **options) + + Ask a question. By default shows buttons :data:`YES` and :data:`NO`. + Returns the symbolic name of the selected button. + +.. function:: askokcancel(title=None, message=None, **options) + + Ask if operation should proceed. Shows buttons :data:`OK` and :data:`CANCEL`. + Returns ``True`` if the answer is ok and ``False`` otherwise. + +.. function:: askretrycancel(title=None, message=None, **options) + + Ask if operation should be retried. Shows buttons :data:`RETRY` and :data:`CANCEL`. + Return ``True`` if the answer is yes and ``False`` otherwise. + +.. function:: askyesno(title=None, message=None, **options) + + Ask a question. Shows buttons :data:`YES` and :data:`NO`. + Returns ``True`` if the answer is yes and ``False`` otherwise. + +.. function:: askyesnocancel(title=None, message=None, **options) + + Ask a question. Shows buttons :data:`YES`, :data:`NO` and :data:`CANCEL`. + Return ``True`` if the answer is yes, ``None`` if cancelled, and ``False`` + otherwise. + + +.. _messagebox-buttons: + +Symbolic names of buttons: + +.. data:: ABORT + :value: 'abort' +.. data:: RETRY + :value: 'retry' +.. data:: IGNORE + :value: 'ignore' +.. data:: OK + :value: 'ok' +.. data:: CANCEL + :value: 'cancel' +.. data:: YES + :value: 'yes' +.. data:: NO + :value: 'no' + +.. _messagebox-types: + +Predefined sets of buttons: + +.. data:: ABORTRETRYIGNORE + :value: 'abortretryignore' + + Displays three buttons whose symbolic names are :data:`ABORT`, + :data:`RETRY` and :data:`IGNORE`. + +.. data:: OK + :value: 'ok' + :noindex: + + Displays one button whose symbolic name is :data:`OK`. + +.. data:: OKCANCEL + :value: 'okcancel' + + Displays two buttons whose symbolic names are :data:`OK` and + :data:`CANCEL`. + +.. data:: RETRYCANCEL + :value: 'retrycancel' + + Displays two buttons whose symbolic names are :data:`RETRY` and + :data:`CANCEL`. + +.. data:: YESNO + :value: 'yesno' + + Displays two buttons whose symbolic names are :data:`YES` and + :data:`NO`. + +.. data:: YESNOCANCEL + :value: 'yesnocancel' + + Displays three buttons whose symbolic names are :data:`YES`, + :data:`NO` and :data:`CANCEL`. + +.. _messagebox-icons: + +Icon images: + +.. data:: ERROR + :value: 'error' +.. data:: INFO + :value: 'info' +.. data:: QUESTION + :value: 'question' +.. data:: WARNING + :value: 'warning' From cfdcc96aba90fca40acea63c3aac4dd4be8c8c1e Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 2 Nov 2023 12:36:35 +0100 Subject: [PATCH 561/632] [3.11] gh-111625: Fix link to Info-ZIP homepage (GH-111626) (#111640) Co-authored-by: partev --- Doc/tools/templates/download.html | 4 ++-- Lib/test/ziptestdata/README.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/tools/templates/download.html b/Doc/tools/templates/download.html index 7920e0619f9337..b5353d6fb77ab4 100644 --- a/Doc/tools/templates/download.html +++ b/Doc/tools/templates/download.html @@ -49,12 +49,12 @@

Unpacking

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

Windows users can use the ZIP archives since those are customary on that -platform. These are created on Unix using the InfoZIP zip program.

+platform. These are created on Unix using the Info-ZIP zip program.

Problems

diff --git a/Lib/test/ziptestdata/README.md b/Lib/test/ziptestdata/README.md index 6b9147db76e178..00d96d445bf543 100644 --- a/Lib/test/ziptestdata/README.md +++ b/Lib/test/ziptestdata/README.md @@ -1,7 +1,7 @@ # Test data for `test_zipfile` The test executables in this directory are created manually from header.sh and -the `testdata_module_inside_zip.py` file. You must have infozip's zip utility +the `testdata_module_inside_zip.py` file. You must have Info-ZIP's zip utility installed (`apt install zip` on Debian). ## Purpose @@ -25,7 +25,7 @@ rm zip2.zip ### Modern format (4.5) zip64 file -Redirecting from stdin forces infozip's zip tool to create a zip64. +Redirecting from stdin forces Info-ZIP's zip tool to create a zip64. ``` zip -0 zip64.zip From e61f2edfe8ebfea4de5f0b55f3b790fdf07998d7 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 3 Nov 2023 00:03:02 +0100 Subject: [PATCH 562/632] [3.11] Fix typo in documentation of `SysLogHandler.createSocket` (GH-111665) (#111670) (cherry picked from commit 489b80640ff9c4f10b25da6d562b06c62a10a76b) --- Doc/library/logging.handlers.rst | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Doc/library/logging.handlers.rst b/Doc/library/logging.handlers.rst index f9b3b9b9abe656..bb30f716dd3d39 100644 --- a/Doc/library/logging.handlers.rst +++ b/Doc/library/logging.handlers.rst @@ -656,9 +656,7 @@ supports sending logging messages to a remote or local Unix syslog. to the other end. This method is called during handler initialization, but it's not regarded as an error if the other end isn't listening at this point - the method will be called again when emitting an event, if - but it's not regarded as an error if the other end isn't listening yet - --- the method will be called again when emitting an event, - if there is no socket at that point. + there is no socket at that point. .. versionadded:: 3.11 From a106f61b728b61269ffff5d0909be044e7c8c41e Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 3 Nov 2023 07:28:53 +0100 Subject: [PATCH 563/632] [3.11] gh-54434: Make difflib.rst doctests pass. (GH-111677) (#111679) gh-54434: Make difflib.rst doctests pass. (GH-111677) (cherry picked from commit 0d3df272fbd131bff7f02d4d4279ad1e35081121) Co-authored-by: Terry Jan Reedy --- Doc/library/difflib.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Doc/library/difflib.rst b/Doc/library/difflib.rst index 431ee9c2c6ce17..83c669e50f1f85 100644 --- a/Doc/library/difflib.rst +++ b/Doc/library/difflib.rst @@ -173,9 +173,12 @@ diffs. For comparing directories and files, see also, the :mod:`filecmp` module. expressed in the ISO 8601 format. If not specified, the strings default to blanks. + >>> import sys + >>> from difflib import * >>> s1 = ['bacon\n', 'eggs\n', 'ham\n', 'guido\n'] >>> s2 = ['python\n', 'eggy\n', 'hamster\n', 'guido\n'] - >>> sys.stdout.writelines(context_diff(s1, s2, fromfile='before.py', tofile='after.py')) + >>> sys.stdout.writelines(context_diff(s1, s2, fromfile='before.py', + ... tofile='after.py')) *** before.py --- after.py *************** @@ -298,13 +301,12 @@ diffs. For comparing directories and files, see also, the :mod:`filecmp` module. For inputs that do not have trailing newlines, set the *lineterm* argument to ``""`` so that the output will be uniformly newline free. - The context diff format normally has a header for filenames and modification + The unified diff format normally has a header for filenames and modification times. Any or all of these may be specified using strings for *fromfile*, *tofile*, *fromfiledate*, and *tofiledate*. The modification times are normally expressed in the ISO 8601 format. If not specified, the strings default to blanks. - >>> s1 = ['bacon\n', 'eggs\n', 'ham\n', 'guido\n'] >>> s2 = ['python\n', 'eggy\n', 'hamster\n', 'guido\n'] >>> sys.stdout.writelines(unified_diff(s1, s2, fromfile='before.py', tofile='after.py')) From 4a6116967228f8399d4e5bc4dfed6f6da81202e3 Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Fri, 3 Nov 2023 16:51:56 -0700 Subject: [PATCH 564/632] [3.11] gh-111181: Fix enum doctests (GH-111180) (GH-111617) gh-111181: Fix enum doctests (GH-111180) Co-authored-by: Ethan Furman (cherry picked from commit c4dc5a6ae8aa13abb743182df088f1a3526d1bcd) Co-authored-by: Nikita Sobolev --- Doc/howto/enum.rst | 8 +++++--- Lib/enum.py | 11 +++++------ Lib/test/test_enum.py | 17 +++++++++++------ 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/Doc/howto/enum.rst b/Doc/howto/enum.rst index 58849429539cca..a714f6d9aa3c7e 100644 --- a/Doc/howto/enum.rst +++ b/Doc/howto/enum.rst @@ -494,7 +494,8 @@ It is possible to modify how enum members are pickled/unpickled by defining :meth:`__reduce_ex__` in the enumeration class. The default method is by-value, but enums with complicated values may want to use by-name:: - >>> class MyEnum(Enum): + >>> import enum + >>> class MyEnum(enum.Enum): ... __reduce_ex__ = enum.pickle_by_enum_name .. note:: @@ -736,7 +737,7 @@ be combined with them (but may lose :class:`IntFlag` membership:: >>> Perm.X | 4 - >>> Perm.X | 8 + >>> Perm.X + 8 9 .. note:: @@ -1398,8 +1399,9 @@ alias:: ... GRENE = 2 ... 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 155cb13022fa16..63833aaa717feb 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -1197,14 +1197,13 @@ def __str__(self): def __dir__(self): """ - Returns all members and all public methods + Returns public methods and other interesting attributes. """ - if self.__class__._member_type_ is object: - interesting = set(['__class__', '__doc__', '__eq__', '__hash__', '__module__', 'name', 'value']) - else: + interesting = set() + if self.__class__._member_type_ is not object: interesting = set(object.__dir__(self)) for name in getattr(self, '__dict__', []): - if name[0] != '_': + if name[0] != '_' and name not in self._member_map_: interesting.add(name) for cls in self.__class__.mro(): for name, obj in cls.__dict__.items(): @@ -1217,7 +1216,7 @@ def __dir__(self): else: # in case it was added by `dir(self)` interesting.discard(name) - else: + elif name not in self._member_map_: interesting.add(name) names = sorted( set(['__class__', '__doc__', '__eq__', '__hash__', '__module__']) diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index ed1c3a59ce479c..ab2b45853c2214 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -18,7 +18,7 @@ from io import StringIO from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL from test import support -from test.support import ALWAYS_EQ +from test.support import ALWAYS_EQ, REPO_ROOT from test.support import threading_helper from textwrap import dedent from datetime import timedelta @@ -27,14 +27,19 @@ def load_tests(loader, tests, ignore): tests.addTests(doctest.DocTestSuite(enum)) - if os.path.exists('Doc/library/enum.rst'): + + lib_tests = os.path.join(REPO_ROOT, 'Doc/library/enum.rst') + if os.path.exists(lib_tests): tests.addTests(doctest.DocFileSuite( - '../../Doc/library/enum.rst', + lib_tests, + module_relative=False, optionflags=doctest.ELLIPSIS|doctest.NORMALIZE_WHITESPACE, )) - if os.path.exists('Doc/howto/enum.rst'): + howto_tests = os.path.join(REPO_ROOT, 'Doc/howto/enum.rst') + if os.path.exists(howto_tests): tests.addTests(doctest.DocFileSuite( - '../../Doc/howto/enum.rst', + howto_tests, + module_relative=False, optionflags=doctest.ELLIPSIS|doctest.NORMALIZE_WHITESPACE, )) return tests @@ -4874,7 +4879,7 @@ def member_dir(member): allowed.add(name) else: allowed.discard(name) - else: + elif name not in member._member_map_: allowed.add(name) return sorted(allowed) From 89264a31eaeb71a1349edba96c090bcf026b498c Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 4 Nov 2023 01:43:53 +0100 Subject: [PATCH 565/632] [3.11] gh-111644: Fix support threading_cleanup() (GH-111714) (#111717) gh-111644: Fix support threading_cleanup() (GH-111714) Copy the list of dangling threads to make sure that the list of "Dangling thread" is complete. Previously, the list was incomplete if threads completed just before the list was displayed. Changes: * Rewrite the warning to make it easier to understand. * Use support.sleeping_retry(). * threading_cleanup() no longer copies threading._dangling, but only counts the number of dangling thread. * Remove support.gc_support() call. (cherry picked from commit f62c7ccf9abf6e0493978da9cf9ca43adcd403f9) Co-authored-by: Victor Stinner --- Lib/test/support/threading_helper.py | 53 +++++++++++++++------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/Lib/test/support/threading_helper.py b/Lib/test/support/threading_helper.py index b9973c8bf5c914..6815ab36ec7a9f 100644 --- a/Lib/test/support/threading_helper.py +++ b/Lib/test/support/threading_helper.py @@ -22,34 +22,37 @@ def threading_setup(): - return _thread._count(), threading._dangling.copy() + return _thread._count(), len(threading._dangling) def threading_cleanup(*original_values): - _MAX_COUNT = 100 - - for count in range(_MAX_COUNT): - values = _thread._count(), threading._dangling - if values == original_values: - break - - if not count: - # Display a warning at the first iteration - support.environment_altered = True - dangling_threads = values[1] - support.print_warning(f"threading_cleanup() failed to cleanup " - f"{values[0] - original_values[0]} threads " - f"(count: {values[0]}, " - f"dangling: {len(dangling_threads)})") - for thread in dangling_threads: - support.print_warning(f"Dangling thread: {thread!r}") - - # Don't hold references to threads - dangling_threads = None - values = None - - time.sleep(0.01) - support.gc_collect() + orig_count, orig_ndangling = original_values + + timeout = 1.0 + for _ in support.sleeping_retry(timeout, error=False): + # Copy the thread list to get a consistent output. threading._dangling + # is a WeakSet, its value changes when it's read. + dangling_threads = list(threading._dangling) + count = _thread._count() + + if count <= orig_count: + return + + # Timeout! + support.environment_altered = True + support.print_warning( + f"threading_cleanup() failed to clean up threads " + f"in {timeout:.1f} seconds\n" + f" before: thread count={orig_count}, dangling={orig_ndangling}\n" + f" after: thread count={count}, dangling={len(dangling_threads)}") + for thread in dangling_threads: + support.print_warning(f"Dangling thread: {thread!r}") + + # The warning happens when a test spawns threads and some of these threads + # are still running after the test completes. To fix this warning, join + # threads explicitly to wait until they complete. + # + # To make the warning more likely, reduce the timeout. def reap_threads(func): From f7ffe4a8eac64dedd2950e334f870a5c111e4be7 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 4 Nov 2023 02:23:01 +0100 Subject: [PATCH 566/632] [3.11] [3.12] GH-110894: Call loop exception handler for exceptions in client_connected_cb (GH-111601) (GH-111632) (#111634) * [3.12] GH-110894: Call loop exception handler for exceptions in client_connected_cb (GH-111601) (GH-111632) (cherry picked from commit 9aa88290d82e2808eed84e7a63d0bf9623f84f53) Co-authored-by: Kumar Aditya Call loop exception handler for exceptions in `client_connected_cb` of `asyncio.start_server` so that applications can handle it.. (cherry picked from commit 229f44d353c71185414a072017f46f125676bdd6) * gh-111644: Fix asyncio test_unhandled_exceptions() (#111713) Fix test_unhandled_exceptions() of test_asyncio.test_streams: break explicitly a reference cycle. Fix also StreamTests.tearDown(): the loop must not be closed explicitly, but using set_event_loop() which takes care of shutting down the executor with executor.shutdown(wait=True). BaseEventLoop.close() calls executor.shutdown(wait=False). (cherry picked from commit ac01e2243a1104b2154c0d1bdbc9f8d5b3ada778) --------- Co-authored-by: Kumar Aditya Co-authored-by: Victor Stinner --- Lib/asyncio/streams.py | 12 +++++++ Lib/test/test_asyncio/test_streams.py | 34 +++++++++++++++++-- ...-11-01-14-03-24.gh-issue-110894.7-wZxC.rst | 1 + 3 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-11-01-14-03-24.gh-issue-110894.7-wZxC.rst diff --git a/Lib/asyncio/streams.py b/Lib/asyncio/streams.py index 19d9154b66d027..7c407067e05a16 100644 --- a/Lib/asyncio/streams.py +++ b/Lib/asyncio/streams.py @@ -245,7 +245,19 @@ def connection_made(self, transport): res = self._client_connected_cb(reader, self._stream_writer) if coroutines.iscoroutine(res): + def callback(task): + exc = task.exception() + if exc is not None: + self._loop.call_exception_handler({ + 'message': 'Unhandled exception in client_connected_cb', + 'exception': exc, + 'transport': transport, + }) + transport.close() + self._task = self._loop.create_task(res) + self._task.add_done_callback(callback) + self._strong_reader = None def connection_lost(self, exc): diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py index 95fc7a159baee8..86354306f1fff3 100644 --- a/Lib/test/test_asyncio/test_streams.py +++ b/Lib/test/test_asyncio/test_streams.py @@ -36,8 +36,7 @@ def tearDown(self): # just in case if we have transport close callbacks test_utils.run_briefly(self.loop) - self.loop.close() - gc.collect() + # set_event_loop() takes care of closing self.loop in a safe way super().tearDown() def _basetest_open_connection(self, open_connection_fut): @@ -1068,6 +1067,37 @@ def test_eof_feed_when_closing_writer(self): self.assertEqual(messages, []) + def test_unhandled_exceptions(self) -> None: + port = socket_helper.find_unused_port() + + messages = [] + self.loop.set_exception_handler(lambda loop, ctx: messages.append(ctx)) + + async def client(): + rd, wr = await asyncio.open_connection('localhost', port) + wr.write(b'test msg') + await wr.drain() + wr.close() + await wr.wait_closed() + + async def main(): + async def handle_echo(reader, writer): + raise Exception('test') + + server = await asyncio.start_server( + handle_echo, 'localhost', port) + await server.start_serving() + await client() + server.close() + await server.wait_closed() + + self.loop.run_until_complete(main()) + + self.assertEqual(messages[0]['message'], + 'Unhandled exception in client_connected_cb') + # Break explicitly reference cycle + messages = None + if __name__ == '__main__': unittest.main() 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 new file mode 100644 index 00000000000000..c59fe6b9119eca --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-01-14-03-24.gh-issue-110894.7-wZxC.rst @@ -0,0 +1 @@ +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. From 425efc11828a8713057e75f04e8d7d889e2f20db Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 4 Nov 2023 11:12:15 +0100 Subject: [PATCH 567/632] [3.11] gh-111159: Fix `SyntaxError` doctests for non-builtin exception classes (GH-111541) (#111733) gh-111159: Fix `SyntaxError` doctests for non-builtin exception classes (GH-111541) (cherry picked from commit 18c954849bcdd5acb6ef91cd90d92f3b5c685134) Co-authored-by: Nikita Sobolev --- Lib/doctest.py | 6 +++++- Lib/test/test_doctest.py | 18 ++++++++++++++++++ ...3-11-04-10-24-25.gh-issue-111541.x0RBI1.rst | 1 + 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2023-11-04-10-24-25.gh-issue-111541.x0RBI1.rst diff --git a/Lib/doctest.py b/Lib/doctest.py index 783664a0d3554e..21abe475ef85f5 100644 --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -1376,10 +1376,14 @@ def __run(self, test, compileflags, out): # we don't care about the carets / suggestions / etc # We only care about the error message and notes. # They start with `SyntaxError:` (or any other class name) + exception_line_prefixes = ( + f"{exception[0].__qualname__}:", + f"{exception[0].__module__}.{exception[0].__qualname__}:", + ) exc_msg_index = next( index for index, line in enumerate(formatted_ex) - if line.startswith(f"{exception[0].__name__}:") + if line.startswith(exception_line_prefixes) ) formatted_ex = formatted_ex[exc_msg_index:] diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py index 98bda46d312909..617529dded3b73 100644 --- a/Lib/test/test_doctest.py +++ b/Lib/test/test_doctest.py @@ -3279,6 +3279,24 @@ def test_syntax_error_with_note(cls, multiline=False): raise exc +def test_syntax_error_subclass_from_stdlib(): + """ + `ParseError` is a subclass of `SyntaxError`, but it is not a builtin: + + >>> test_syntax_error_subclass_from_stdlib() + Traceback (most recent call last): + ... + xml.etree.ElementTree.ParseError: error + error + Note + Line + """ + from xml.etree.ElementTree import ParseError + exc = ParseError("error\nerror") + exc.add_note('Note\nLine') + raise exc + + def test_syntax_error_with_incorrect_expected_note(): """ >>> def f(x): 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 new file mode 100644 index 00000000000000..719b63dad36fb7 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-04-10-24-25.gh-issue-111541.x0RBI1.rst @@ -0,0 +1 @@ +Fix :mod:`doctest` for :exc:`SyntaxError` not-builtin subclasses. From cebf2d2a5d751052d16b6234e78e0e902b72b706 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 4 Nov 2023 20:56:08 +0100 Subject: [PATCH 568/632] [3.11] gh-111724: Fix doctest `ResourceWarning` in `howto/descriptor.rst` (GH-111725) (#111728) gh-111724: Fix doctest `ResourceWarning` in `howto/descriptor.rst` (GH-111725) Close database connection explicitly in test cleanup. (cherry picked from commit f48e669504ce53040a04e0181064c11741a87817) Co-authored-by: Nikita Sobolev --- Doc/howto/descriptor.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Doc/howto/descriptor.rst b/Doc/howto/descriptor.rst index 1d9424cb735a46..0a7263614670a1 100644 --- a/Doc/howto/descriptor.rst +++ b/Doc/howto/descriptor.rst @@ -943,6 +943,10 @@ it can be updated: >>> Movie('Star Wars').director 'J.J. Abrams' +.. testcleanup:: + + conn.close() + Pure Python Equivalents ^^^^^^^^^^^^^^^^^^^^^^^ From 9fe9eaec2d094a65eb106de447d2f5c9fd1ab267 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 5 Nov 2023 05:28:48 +0100 Subject: [PATCH 569/632] [3.11] gh-111747: DOC: fix moved link to Documentation Translations (GH-111748) (#111750) Update old link in bugs.rst to the table of doc translators and translation repositories at Github. (cherry picked from commit 72e27a67b97993f277e69c9dafb063007ba79adf) Co-authored-by: partev --- Doc/bugs.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/bugs.rst b/Doc/bugs.rst index d98192b369603e..908987cf41ff6e 100644 --- a/Doc/bugs.rst +++ b/Doc/bugs.rst @@ -38,7 +38,7 @@ though it may take a while to be processed. `Helping with Documentation `_ Comprehensive guide for individuals that are interested in contributing to Python documentation. - `Documentation Translations `_ + `Documentation Translations `_ A list of GitHub pages for documentation translation and their primary contacts. From bd4cc4715f425e557b2c707dca21dbf00897cef3 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Mon, 6 Nov 2023 20:29:59 +0900 Subject: [PATCH 570/632] =?UTF-8?q?[3.11]=20gh-101180:=20Fix=20a=20bug=20w?= =?UTF-8?q?here=20iso2022=5Fjp=5F3=20and=20iso2022=5Fjp=5F2004=20co?= =?UTF-8?q?=E2=80=A6=20(gh-111771)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [3.11] gh-101180: Fix a bug where iso2022_jp_3 and iso2022_jp_2004 codecs read out of bounds (gh-111695) (cherry picked from commit c8faa3568afd255708096f6aa8df0afa80cf7697) Co-authored-by: Masayuki Moriyama --- Lib/test/test_codecencodings_iso2022.py | 46 +++++++++++++++++++ ...-10-27-19-38-33.gh-issue-102388.vd5YUZ.rst | 1 + Modules/cjkcodecs/_codecs_iso2022.c | 9 ++-- 3 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-27-19-38-33.gh-issue-102388.vd5YUZ.rst diff --git a/Lib/test/test_codecencodings_iso2022.py b/Lib/test/test_codecencodings_iso2022.py index 00ea1c39dd6fb6..027dbecc6134df 100644 --- a/Lib/test/test_codecencodings_iso2022.py +++ b/Lib/test/test_codecencodings_iso2022.py @@ -24,6 +24,52 @@ class Test_ISO2022_JP2(multibytecodec_support.TestBase, unittest.TestCase): (b'ab\x1BNdef', 'replace', 'abdef'), ) +class Test_ISO2022_JP3(multibytecodec_support.TestBase, unittest.TestCase): + encoding = 'iso2022_jp_3' + tstring = multibytecodec_support.load_teststring('iso2022_jp') + codectests = COMMON_CODEC_TESTS + ( + (b'ab\x1BNdef', 'replace', 'ab\x1BNdef'), + (b'\x1B$(O\x2E\x23\x1B(B', 'strict', '\u3402' ), + (b'\x1B$(O\x2E\x22\x1B(B', 'strict', '\U0002000B' ), + (b'\x1B$(O\x24\x77\x1B(B', 'strict', '\u304B\u309A'), + (b'\x1B$(P\x21\x22\x1B(B', 'strict', '\u4E02' ), + (b'\x1B$(P\x7E\x76\x1B(B', 'strict', '\U0002A6B2' ), + ('\u3402', 'strict', b'\x1B$(O\x2E\x23\x1B(B'), + ('\U0002000B', 'strict', b'\x1B$(O\x2E\x22\x1B(B'), + ('\u304B\u309A', 'strict', b'\x1B$(O\x24\x77\x1B(B'), + ('\u4E02', 'strict', b'\x1B$(P\x21\x22\x1B(B'), + ('\U0002A6B2', 'strict', b'\x1B$(P\x7E\x76\x1B(B'), + (b'ab\x1B$(O\x2E\x21\x1B(Bdef', 'replace', 'ab\uFFFDdef'), + ('ab\u4FF1def', 'replace', b'ab?def'), + ) + xmlcharnametest = ( + '\xAB\u211C\xBB = \u2329\u1234\u232A', + b'\x1B$(O\x29\x28\x1B(Bℜ\x1B$(O\x29\x32\x1B(B = ⟨ሴ⟩' + ) + +class Test_ISO2022_JP2004(multibytecodec_support.TestBase, unittest.TestCase): + encoding = 'iso2022_jp_2004' + tstring = multibytecodec_support.load_teststring('iso2022_jp') + codectests = COMMON_CODEC_TESTS + ( + (b'ab\x1BNdef', 'replace', 'ab\x1BNdef'), + (b'\x1B$(Q\x2E\x23\x1B(B', 'strict', '\u3402' ), + (b'\x1B$(Q\x2E\x22\x1B(B', 'strict', '\U0002000B' ), + (b'\x1B$(Q\x24\x77\x1B(B', 'strict', '\u304B\u309A'), + (b'\x1B$(P\x21\x22\x1B(B', 'strict', '\u4E02' ), + (b'\x1B$(P\x7E\x76\x1B(B', 'strict', '\U0002A6B2' ), + ('\u3402', 'strict', b'\x1B$(Q\x2E\x23\x1B(B'), + ('\U0002000B', 'strict', b'\x1B$(Q\x2E\x22\x1B(B'), + ('\u304B\u309A', 'strict', b'\x1B$(Q\x24\x77\x1B(B'), + ('\u4E02', 'strict', b'\x1B$(P\x21\x22\x1B(B'), + ('\U0002A6B2', 'strict', b'\x1B$(P\x7E\x76\x1B(B'), + (b'ab\x1B$(Q\x2E\x21\x1B(Bdef', 'replace', 'ab\u4FF1def'), + ('ab\u4FF1def', 'replace', b'ab\x1B$(Q\x2E\x21\x1B(Bdef'), + ) + xmlcharnametest = ( + '\xAB\u211C\xBB = \u2329\u1234\u232A', + b'\x1B$(Q\x29\x28\x1B(Bℜ\x1B$(Q\x29\x32\x1B(B = ⟨ሴ⟩' + ) + class Test_ISO2022_KR(multibytecodec_support.TestBase, unittest.TestCase): encoding = 'iso2022_kr' tstring = multibytecodec_support.load_teststring('iso2022_kr') 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 new file mode 100644 index 00000000000000..268a3d310f2b49 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-10-27-19-38-33.gh-issue-102388.vd5YUZ.rst @@ -0,0 +1 @@ +Fix a bug where ``iso2022_jp_3`` and ``iso2022_jp_2004`` codecs read out of bounds diff --git a/Modules/cjkcodecs/_codecs_iso2022.c b/Modules/cjkcodecs/_codecs_iso2022.c index 7394cf67e0e7dd..6d906ecdd396c2 100644 --- a/Modules/cjkcodecs/_codecs_iso2022.c +++ b/Modules/cjkcodecs/_codecs_iso2022.c @@ -181,8 +181,9 @@ ENCODER(iso2022) encoded = MAP_UNMAPPABLE; for (dsg = CONFIG_DESIGNATIONS; dsg->mark; dsg++) { + Py_UCS4 buf[2] = {c, 0}; Py_ssize_t length = 1; - encoded = dsg->encoder(&c, &length); + encoded = dsg->encoder(buf, &length); if (encoded == MAP_MULTIPLE_AVAIL) { /* this implementation won't work for pair * of non-bmp characters. */ @@ -191,9 +192,11 @@ ENCODER(iso2022) return MBERR_TOOFEW; length = -1; } - else + else { + buf[1] = INCHAR2; length = 2; - encoded = dsg->encoder(&c, &length); + } + encoded = dsg->encoder(buf, &length); if (encoded != MAP_UNMAPPABLE) { insize = length; break; From 4edf25565f20550024f98bd2a561d3ee15cc3aac Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Mon, 6 Nov 2023 18:57:10 -0800 Subject: [PATCH 571/632] [3.11] gh-111797: fix enum how-to (GH-111805) remove extra error line in how-to --- Doc/howto/enum.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/Doc/howto/enum.rst b/Doc/howto/enum.rst index a714f6d9aa3c7e..3deb071144633b 100644 --- a/Doc/howto/enum.rst +++ b/Doc/howto/enum.rst @@ -1401,7 +1401,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:: From 942ef7bde4ad22f1cc73bfc69fe0540395e75405 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 7 Nov 2023 23:04:18 +0100 Subject: [PATCH 572/632] [3.11] gh-111806: Fix `test_recursion` in `test_richcmp` on WASI builds (GH-111830) (GH-111832) gh-111806: Fix `test_recursion` in `test_richcmp` on WASI builds (GH-111830) (cherry picked from commit f115a55f0e455a4b43a1da9fd838a60a101f182a) Co-authored-by: Nikita Sobolev --- Lib/test/test_richcmp.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_richcmp.py b/Lib/test/test_richcmp.py index 58729a9fea62fa..5f449cdc05c6ba 100644 --- a/Lib/test/test_richcmp.py +++ b/Lib/test/test_richcmp.py @@ -221,6 +221,7 @@ def do(bad): self.assertRaises(Exc, func, Bad()) @support.no_tracing + @support.infinite_recursion(25) def test_recursion(self): # Check that comparison for recursive objects fails gracefully from collections import UserList From 45f17a165bb20e472cc050442d556598b4a0b5d3 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 8 Nov 2023 15:47:54 +0100 Subject: [PATCH 573/632] [3.11] Glossary: Add "static type checker" (GH-111837) (#111855) Glossary: Add "static type checker" (GH-111837) (cherry picked from commit 8ab7ad63086b1793c24b1c5aaa19b60fc0e6540e) Co-authored-by: Jelle Zijlstra Co-authored-by: Alex Waygood --- Doc/glossary.rst | 9 +++++++-- Doc/howto/pyporting.rst | 5 +++-- Doc/library/datetime.rst | 3 ++- Doc/library/typing.rst | 4 ++-- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Doc/glossary.rst b/Doc/glossary.rst index f318a37b654e9d..47a95250f661be 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -1130,6 +1130,11 @@ Glossary an :term:`expression` or one of several constructs with a keyword, such as :keyword:`if`, :keyword:`while` or :keyword:`for`. + static type checker + An external tool that reads Python code and analyzes it, looking for + issues such as incorrect types. See also :term:`type hints ` + and the :mod:`typing` module. + strong reference In Python's C API, a strong reference is a reference to an object which is owned by the code holding the reference. The strong @@ -1206,8 +1211,8 @@ Glossary attribute, or a function parameter or return value. Type hints are optional and are not enforced by Python but - they are useful to static type analysis tools, and aid IDEs with code - completion and refactoring. + they are useful to :term:`static type checkers `. + They can also aid IDEs with code completion and refactoring. Type hints of global variables, class attributes, and functions, but not local variables, can be accessed using diff --git a/Doc/howto/pyporting.rst b/Doc/howto/pyporting.rst index 6c30a0dd7d6bcc..501b16d82d4d6f 100644 --- a/Doc/howto/pyporting.rst +++ b/Doc/howto/pyporting.rst @@ -39,7 +39,8 @@ are: #. Once your dependencies are no longer blocking you, use continuous integration to make sure you stay compatible with Python 2 and 3 (tox_ can help test against multiple versions of Python; ``python -m pip install tox``) -#. Consider using optional static type checking to make sure your type usage +#. Consider using optional :term:`static type checking ` + to make sure your type usage works in both Python 2 and 3 (e.g. use mypy_ to check your typing under both Python 2 and Python 3; ``python -m pip install mypy``). @@ -395,7 +396,7 @@ comparisons occur, making the mistake much easier to track down. Consider using optional static type checking -------------------------------------------- -Another way to help port your code is to use a static type checker like +Another way to help port your code is to use a :term:`static type checker` like mypy_ or pytype_ on your code. These tools can be used to analyze your code as if it's being run under Python 2, then you can run the tool a second time as if your code is running under Python 3. By running a static type checker twice like diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index 1c2abc8cf4e30e..caab35acb94c88 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -38,7 +38,8 @@ on efficient attribute extraction for output formatting and manipulation. Third-party library with expanded time zone and parsing support. Package `DateType `_ - Third-party library that introduces distinct static types to e.g. allow static type checkers + Third-party library that introduces distinct static types to e.g. allow + :term:`static type checkers ` to differentiate between naive and aware datetimes. .. _datetime-naive-aware: diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 0decbaf39749c0..f00a4ce20c8a51 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -18,8 +18,8 @@ .. note:: The Python runtime does not enforce function and variable type annotations. - They can be used by third party tools such as type checkers, IDEs, linters, - etc. + They can be used by third party tools such as :term:`type checkers `, + IDEs, linters, etc. -------------- From a60bbdee3a640620555c4ebe3942c0e0f8af6a26 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Wed, 8 Nov 2023 20:19:47 +0300 Subject: [PATCH 574/632] [3.11] gh-108303: Move more typing related files to Lib/test/typinganndata (GH-111825) (#111860) --- Lib/test/test_typing.py | 3 +-- Lib/test/{ => typinganndata}/_typed_dict_helper.py | 0 Lib/test/{ => typinganndata}/mod_generics_cache.py | 0 3 files changed, 1 insertion(+), 2 deletions(-) rename Lib/test/{ => typinganndata}/_typed_dict_helper.py (100%) rename Lib/test/{ => typinganndata}/mod_generics_cache.py (100%) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index fa279b557de2c6..30056e49ae29d9 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -44,8 +44,7 @@ import types from test.support import import_helper, captured_stderr, cpython_only -from test import mod_generics_cache -from test import _typed_dict_helper +from test.typinganndata import mod_generics_cache, _typed_dict_helper py_typing = import_helper.import_fresh_module('typing', blocked=['_typing']) diff --git a/Lib/test/_typed_dict_helper.py b/Lib/test/typinganndata/_typed_dict_helper.py similarity index 100% rename from Lib/test/_typed_dict_helper.py rename to Lib/test/typinganndata/_typed_dict_helper.py diff --git a/Lib/test/mod_generics_cache.py b/Lib/test/typinganndata/mod_generics_cache.py similarity index 100% rename from Lib/test/mod_generics_cache.py rename to Lib/test/typinganndata/mod_generics_cache.py From 409a0ac8e5e6505400df7a686e8aa1b694006cea Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 9 Nov 2023 15:43:28 +0100 Subject: [PATCH 575/632] [3.11] gh-108303: Move config parser data to `Lib/test/configparserdata/` (gh-111879) (gh-111883) gh-108303: Move config parser data to `Lib/test/configparserdata/` (gh-111879) (cherry picked from commit cc18b886a51672c59622837a2b8e83bf6be28c58) Co-authored-by: Nikita Sobolev --- Lib/idlelib/idle_test/test_config.py | 8 ++++---- Lib/test/{ => configdata}/cfgparser.1 | 0 Lib/test/{ => configdata}/cfgparser.2 | 0 Lib/test/{ => configdata}/cfgparser.3 | 0 Lib/test/test_configparser.py | 16 ++++++++-------- 5 files changed, 12 insertions(+), 12 deletions(-) rename Lib/test/{ => configdata}/cfgparser.1 (100%) rename Lib/test/{ => configdata}/cfgparser.2 (100%) rename Lib/test/{ => configdata}/cfgparser.3 (100%) diff --git a/Lib/idlelib/idle_test/test_config.py b/Lib/idlelib/idle_test/test_config.py index a746f1538a62b0..6d75cf7aa67dcc 100644 --- a/Lib/idlelib/idle_test/test_config.py +++ b/Lib/idlelib/idle_test/test_config.py @@ -85,8 +85,8 @@ def test_load_nothing(self): self.assertEqual(parser.sections(), []) def test_load_file(self): - # Borrow test/cfgparser.1 from test_configparser. - config_path = findfile('cfgparser.1') + # Borrow test/configdata/cfgparser.1 from test_configparser. + config_path = findfile('cfgparser.1', subdir='configdata') parser = config.IdleConfParser(config_path) parser.Load() @@ -294,8 +294,8 @@ def test_create_config_handlers(self): def test_load_cfg_files(self): conf = self.new_config(_utest=True) - # Borrow test/cfgparser.1 from test_configparser. - config_path = findfile('cfgparser.1') + # Borrow test/configdata/cfgparser.1 from test_configparser. + config_path = findfile('cfgparser.1', subdir='configdata') conf.defaultCfg['foo'] = config.IdleConfParser(config_path) conf.userCfg['foo'] = config.IdleUserConfParser(config_path) diff --git a/Lib/test/cfgparser.1 b/Lib/test/configdata/cfgparser.1 similarity index 100% rename from Lib/test/cfgparser.1 rename to Lib/test/configdata/cfgparser.1 diff --git a/Lib/test/cfgparser.2 b/Lib/test/configdata/cfgparser.2 similarity index 100% rename from Lib/test/cfgparser.2 rename to Lib/test/configdata/cfgparser.2 diff --git a/Lib/test/cfgparser.3 b/Lib/test/configdata/cfgparser.3 similarity index 100% rename from Lib/test/cfgparser.3 rename to Lib/test/configdata/cfgparser.3 diff --git a/Lib/test/test_configparser.py b/Lib/test/test_configparser.py index 59c4b275cb46d7..f08dad06aba38d 100644 --- a/Lib/test/test_configparser.py +++ b/Lib/test/test_configparser.py @@ -544,7 +544,7 @@ def test_parse_errors(self): "[Foo]\n wrong-indent\n") self.assertEqual(e.args, ('',)) # read_file on a real file - tricky = support.findfile("cfgparser.3") + tricky = support.findfile("cfgparser.3", subdir="configdata") if self.delimiters[0] == '=': error = configparser.ParsingError expected = (tricky,) @@ -718,7 +718,7 @@ class mystr(str): def test_read_returns_file_list(self): if self.delimiters[0] != '=': self.skipTest('incompatible format') - file1 = support.findfile("cfgparser.1") + file1 = support.findfile("cfgparser.1", subdir="configdata") # check when we pass a mix of readable and non-readable files: cf = self.newconfig() parsed_files = cf.read([file1, "nonexistent-file"], encoding="utf-8") @@ -751,7 +751,7 @@ def test_read_returns_file_list(self): def test_read_returns_file_list_with_bytestring_path(self): if self.delimiters[0] != '=': self.skipTest('incompatible format') - file1_bytestring = support.findfile("cfgparser.1").encode() + file1_bytestring = support.findfile("cfgparser.1", subdir="configdata").encode() # check when passing an existing bytestring path cf = self.newconfig() parsed_files = cf.read(file1_bytestring, encoding="utf-8") @@ -1161,7 +1161,7 @@ class RawConfigParserTestSambaConf(CfgParserTestCaseClass, unittest.TestCase): empty_lines_in_values = False def test_reading(self): - smbconf = support.findfile("cfgparser.2") + smbconf = support.findfile("cfgparser.2", subdir="configdata") # check when we pass a mix of readable and non-readable files: cf = self.newconfig() parsed_files = cf.read([smbconf, "nonexistent-file"], encoding='utf-8') @@ -1356,7 +1356,7 @@ class ConfigParserTestCaseTrickyFile(CfgParserTestCaseClass, unittest.TestCase): allow_no_value = True def test_cfgparser_dot_3(self): - tricky = support.findfile("cfgparser.3") + tricky = support.findfile("cfgparser.3", subdir="configdata") cf = self.newconfig() self.assertEqual(len(cf.read(tricky, encoding='utf-8')), 1) self.assertEqual(cf.sections(), ['strange', @@ -1388,7 +1388,7 @@ def test_cfgparser_dot_3(self): self.assertEqual(cf.get('more interpolation', 'lets'), 'go shopping') def test_unicode_failure(self): - tricky = support.findfile("cfgparser.3") + tricky = support.findfile("cfgparser.3", subdir="configdata") cf = self.newconfig() with self.assertRaises(UnicodeDecodeError): cf.read(tricky, encoding='ascii') @@ -1489,7 +1489,7 @@ def fromstring(self, string, defaults=None): class FakeFile: def __init__(self): - file_path = support.findfile("cfgparser.1") + file_path = support.findfile("cfgparser.1", subdir="configdata") with open(file_path, encoding="utf-8") as f: self.lines = f.readlines() self.lines.reverse() @@ -1510,7 +1510,7 @@ def readline_generator(f): class ReadFileTestCase(unittest.TestCase): def test_file(self): - file_paths = [support.findfile("cfgparser.1")] + file_paths = [support.findfile("cfgparser.1", subdir="configdata")] try: file_paths.append(file_paths[0].encode('utf8')) except UnicodeEncodeError: From 3e7e39c9777c21ea32e3deb29f6e5c96e416bb35 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 9 Nov 2023 16:10:26 +0100 Subject: [PATCH 576/632] [3.11] gh-108303: Move more files to `Lib/test/test_module` (GH-111880) (#111892) gh-108303: Move more files to `Lib/test/test_module` (GH-111880) (cherry picked from commit 0c42f7304a2757fe0f78bc6c6fbb33225cd9da15) Co-authored-by: Nikita Sobolev --- Lib/test/test_module/__init__.py | 2 +- Lib/test/{ => test_module}/final_a.py | 4 ++-- Lib/test/{ => test_module}/final_b.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename Lib/test/{ => test_module}/final_a.py (79%) rename Lib/test/{ => test_module}/final_b.py (79%) diff --git a/Lib/test/test_module/__init__.py b/Lib/test/test_module/__init__.py index c2bca45bb60c44..b470dfc6c9b2bd 100644 --- a/Lib/test/test_module/__init__.py +++ b/Lib/test/test_module/__init__.py @@ -268,7 +268,7 @@ def test_module_repr_source(self): 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 import final_a") + rc, out, err = assert_python_ok("-c", "from test.test_module import final_a") self.assertFalse(err) lines = out.splitlines() self.assertEqual(set(lines), { diff --git a/Lib/test/final_a.py b/Lib/test/test_module/final_a.py similarity index 79% rename from Lib/test/final_a.py rename to Lib/test/test_module/final_a.py index 390ee8895a8a9e..a983f3111248e0 100644 --- a/Lib/test/final_a.py +++ b/Lib/test/test_module/final_a.py @@ -3,7 +3,7 @@ """ import shutil -import test.final_b +import test.test_module.final_b x = 'a' @@ -11,7 +11,7 @@ class C: def __del__(self): # Inspect module globals and builtins print("x =", x) - print("final_b.x =", test.final_b.x) + print("final_b.x =", test.test_module.final_b.x) print("shutil.rmtree =", getattr(shutil.rmtree, '__name__', None)) print("len =", getattr(len, '__name__', None)) diff --git a/Lib/test/final_b.py b/Lib/test/test_module/final_b.py similarity index 79% rename from Lib/test/final_b.py rename to Lib/test/test_module/final_b.py index 7228d82b880156..f3e8d5594904f2 100644 --- a/Lib/test/final_b.py +++ b/Lib/test/test_module/final_b.py @@ -3,7 +3,7 @@ """ import shutil -import test.final_a +import test.test_module.final_a x = 'b' @@ -11,7 +11,7 @@ class C: def __del__(self): # Inspect module globals and builtins print("x =", x) - print("final_a.x =", test.final_a.x) + print("final_a.x =", test.test_module.final_a.x) print("shutil.rmtree =", getattr(shutil.rmtree, '__name__', None)) print("len =", getattr(len, '__name__', None)) From 63205e5692041c59283b8fa667310d12fd5846ee Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 9 Nov 2023 16:37:04 +0100 Subject: [PATCH 577/632] [3.11] gh-111881: Import doctest lazily in libregrtest (GH-111884) (#111894) gh-111881: Import doctest lazily in libregrtest (GH-111884) In most cases, doctest is not needed. So don't always import it at startup. The change reduces the number of modules already imported when a test is run. (cherry picked from commit 6f09f69b7f85962f66d10637c3325bbb2b2d9853) Co-authored-by: Victor Stinner --- Lib/test/libregrtest/single.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Lib/test/libregrtest/single.py b/Lib/test/libregrtest/single.py index ad75ef54a8c3f8..5c7bc7d40fb394 100644 --- a/Lib/test/libregrtest/single.py +++ b/Lib/test/libregrtest/single.py @@ -1,4 +1,3 @@ -import doctest import faulthandler import gc import importlib @@ -99,14 +98,18 @@ def regrtest_runner(result: TestResult, test_func, runtests: RunTests) -> None: stats = test_result case unittest.TestResult(): stats = TestStats.from_unittest(test_result) - case doctest.TestResults(): - stats = TestStats.from_doctest(test_result) case None: print_warning(f"{result.test_name} test runner returned None: {test_func}") stats = None case _: - print_warning(f"Unknown test result type: {type(test_result)}") - stats = None + # Don't import doctest at top level since only few tests return + # a doctest.TestResult instance. + import doctest + if isinstance(test_result, doctest.TestResults): + stats = TestStats.from_doctest(test_result) + else: + print_warning(f"Unknown test result type: {type(test_result)}") + stats = None result.stats = stats From f108976ebbfe28bc5c9a62d95555c7adc396b82f Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 9 Nov 2023 17:46:27 +0100 Subject: [PATCH 578/632] [3.11] gh-111881: Use lazy import in test.support (#111885) (#111890) (#111902) [3.12] gh-111881: Use lazy import in test.support (#111885) (#111890) gh-111881: Use lazy import in test.support (#111885) * Import lazily getpass in test.support Backport to 3.11: test.support.os_helper is unchanged. (cherry picked from commit 0372e3b02a7e3dc1c564dba94dcd817c5472b04f) (cherry picked from commit e983ca859de279682631dbb13b959f14a7d89a7b) --- Lib/test/support/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 7ebc42e5bdd01c..dc7a6e6741b8bb 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -6,7 +6,6 @@ import contextlib import dataclasses import functools -import getpass import os import re import stat @@ -380,6 +379,7 @@ def wrapper(*args, **kw): def skip_if_buildbot(reason=None): """Decorator raising SkipTest if running on a buildbot.""" + import getpass if not reason: reason = 'not suitable for buildbots' try: From 316221c23ff1b8acfe5c51298373464191c7d47f Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 9 Nov 2023 20:13:43 +0100 Subject: [PATCH 579/632] [3.11] gh-111895: Convert definition list to bullet list for readability on mobile (GH-111898) (#111909) gh-111895: Convert definition list to bullet list for readability on mobile (GH-111898) Convert definition list to bullet list for readability on mobile (cherry picked from commit 7d21e3d5ee9858aee570aa6c5b6a6e87d776f4b5) Co-authored-by: Hugo van Kemenade --- Doc/howto/enum.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Doc/howto/enum.rst b/Doc/howto/enum.rst index 3deb071144633b..a533b5f1020a1b 100644 --- a/Doc/howto/enum.rst +++ b/Doc/howto/enum.rst @@ -573,9 +573,9 @@ The complete signature is:: start=1, ) -:value: What the new enum class will record as its name. +* *value*: What the new enum class will record as its name. -:names: The enum members. This can be a whitespace- or comma-separated string +* *names*: The enum members. This can be a whitespace- or comma-separated string (values will start at 1 unless otherwise specified):: 'RED GREEN BLUE' | 'RED,GREEN,BLUE' | 'RED, GREEN, BLUE' @@ -592,13 +592,13 @@ The complete signature is:: {'CHARTREUSE': 7, 'SEA_GREEN': 11, 'ROSEMARY': 42} -:module: name of module where new enum class can be found. +* *module*: name of module where new enum class can be found. -:qualname: where in module new enum class can be found. +* *qualname*: where in module new enum class can be found. -:type: type to mix in to new enum class. +* *type*: type to mix in to new enum class. -:start: number to start counting at if only names are passed in. +* *start*: number to start counting at if only names are passed in. .. versionchanged:: 3.5 The *start* parameter was added. From 078cdccc2ae343ed66cfc044c8150575f8d6cf94 Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Thu, 9 Nov 2023 19:51:43 +0000 Subject: [PATCH 580/632] [3.11] gh-110875: Handle '.' properties in logging formatter configuration correctly. (GH-110943) (GH-111914) Co-authored-by: Vinay Sajip --- Lib/logging/config.py | 12 ++++++------ Lib/test/test_logging.py | 42 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/Lib/logging/config.py b/Lib/logging/config.py index 7e78a64a2f08a6..735ffbe1946780 100644 --- a/Lib/logging/config.py +++ b/Lib/logging/config.py @@ -1,4 +1,4 @@ -# Copyright 2001-2019 by Vinay Sajip. All Rights Reserved. +# Copyright 2001-2023 by Vinay Sajip. All Rights Reserved. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose and without fee is hereby granted, @@ -19,7 +19,7 @@ is based on PEP 282 and comments thereto in comp.lang.python, and influenced by Apache's log4j system. -Copyright (C) 2001-2019 Vinay Sajip. All Rights Reserved. +Copyright (C) 2001-2023 Vinay Sajip. All Rights Reserved. To use, simply 'import logging' and log away! """ @@ -477,10 +477,10 @@ def configure_custom(self, config): c = config.pop('()') if not callable(c): c = self.resolve(c) - props = config.pop('.', None) # Check for valid identifiers - kwargs = {k: config[k] for k in config if valid_ident(k)} + kwargs = {k: config[k] for k in config if (k != '.' and valid_ident(k))} result = c(**kwargs) + props = config.pop('.', None) if props: for name, value in props.items(): setattr(result, name, value) @@ -752,8 +752,7 @@ def configure_handler(self, config): 'address' in config: config['address'] = self.as_tuple(config['address']) factory = klass - props = config.pop('.', None) - kwargs = {k: config[k] for k in config if valid_ident(k)} + kwargs = {k: config[k] for k in config if (k != '.' and valid_ident(k))} try: result = factory(**kwargs) except TypeError as te: @@ -771,6 +770,7 @@ def configure_handler(self, config): result.setLevel(logging._checkLevel(level)) if filters: self.add_filters(result, filters) + props = config.pop('.', None) if props: for name, value in props.items(): setattr(result, name, value) diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index e097acd85eb72b..757cdc72bbc89c 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -1,4 +1,4 @@ -# Copyright 2001-2022 by Vinay Sajip. All Rights Reserved. +# Copyright 2001-2023 by Vinay Sajip. All Rights Reserved. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose and without fee is hereby granted, @@ -16,7 +16,7 @@ """Test harness for the logging module. Run all tests. -Copyright (C) 2001-2022 Vinay Sajip. All Rights Reserved. +Copyright (C) 2001-2023 Vinay Sajip. All Rights Reserved. """ import logging @@ -2881,6 +2881,39 @@ class ConfigDictTest(BaseTest): }, } + class CustomFormatter(logging.Formatter): + custom_property = "." + + def format(self, record): + return super().format(record) + + config17 = { + 'version': 1, + 'formatters': { + "custom": { + "()": CustomFormatter, + "style": "{", + "datefmt": "%Y-%m-%d %H:%M:%S", + "format": "{message}", # <-- to force an exception when configuring + ".": { + "custom_property": "value" + } + } + }, + 'handlers' : { + 'hand1' : { + 'class' : 'logging.StreamHandler', + 'formatter' : 'custom', + 'level' : 'NOTSET', + 'stream' : 'ext://sys.stdout', + }, + }, + 'root' : { + 'level' : 'WARNING', + 'handlers' : ['hand1'], + }, + } + out_of_order = { "version": 1, "formatters": { @@ -3295,6 +3328,11 @@ def cleanup(h1, fn): handler = logging.root.handlers[0] self.addCleanup(cleanup, handler, fn) + def test_config17_ok(self): + self.apply_config(self.config17) + h = logging._handlers['hand1'] + self.assertEqual(h.formatter.custom_property, 'value') + def setup_via_listener(self, text, verify=None): text = text.encode("utf-8") # Ask for a randomly assigned port (by using port 0) From 226f4bc69234cb64e52c027e548d4a6dc7f8f0c5 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Thu, 9 Nov 2023 15:36:15 -0800 Subject: [PATCH 581/632] [3.11] GH-111804: Drop posix.fallocate() under WASI (GH-111869) (GH-111920) GH-111804: Drop posix.fallocate() under WASI (GH-111869) Drop posix.fallocate() under WASI. The underlying POSIX function, posix_fallocate(), was found to vary too much between implementations to remain in WASI. As such, while it was available in WASI preview1, it's been dropped in preview2. --- .../Library/2023-11-08-15-58-57.gh-issue-111804.uAXTOL.rst | 2 ++ Modules/clinic/posixmodule.c.h | 6 +++--- Modules/posixmodule.c | 7 +++++-- 3 files changed, 10 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-11-08-15-58-57.gh-issue-111804.uAXTOL.rst 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 new file mode 100644 index 00000000000000..2696f2f492a8b0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-08-15-58-57.gh-issue-111804.uAXTOL.rst @@ -0,0 +1,2 @@ +Remove posix.fallocate() under WASI as the underlying posix_fallocate() is +not available in WASI preview2. diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index e5efd1a3a547ab..8d5a5fecbb2287 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -6259,7 +6259,7 @@ os_truncate(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject #endif /* (defined HAVE_TRUNCATE || defined MS_WINDOWS) */ -#if (defined(HAVE_POSIX_FALLOCATE) && !defined(POSIX_FADVISE_AIX_BUG)) +#if (defined(HAVE_POSIX_FALLOCATE) && !defined(POSIX_FADVISE_AIX_BUG) && !defined(__wasi__)) PyDoc_STRVAR(os_posix_fallocate__doc__, "posix_fallocate($module, fd, offset, length, /)\n" @@ -6304,7 +6304,7 @@ os_posix_fallocate(PyObject *module, PyObject *const *args, Py_ssize_t nargs) return return_value; } -#endif /* (defined(HAVE_POSIX_FALLOCATE) && !defined(POSIX_FADVISE_AIX_BUG)) */ +#endif /* (defined(HAVE_POSIX_FALLOCATE) && !defined(POSIX_FADVISE_AIX_BUG) && !defined(__wasi__)) */ #if (defined(HAVE_POSIX_FADVISE) && !defined(POSIX_FADVISE_AIX_BUG)) @@ -9388,4 +9388,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=8277545a0dea1505 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=b649ad9a4e1f2427 input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 2e4a66ab1bb64a..18e983e5aa1d99 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -10987,7 +10987,10 @@ os_truncate_impl(PyObject *module, path_t *path, Py_off_t length) #endif -#if defined(HAVE_POSIX_FALLOCATE) && !defined(POSIX_FADVISE_AIX_BUG) +/* GH-111804: Due to posix_fallocate() not having consistent semantics across + OSs, support was dropped in WASI preview2. */ +#if defined(HAVE_POSIX_FALLOCATE) && !defined(POSIX_FADVISE_AIX_BUG) && \ + !defined(__wasi__) /*[clinic input] os.posix_fallocate @@ -11025,7 +11028,7 @@ os_posix_fallocate_impl(PyObject *module, int fd, Py_off_t offset, errno = result; return posix_error(); } -#endif /* HAVE_POSIX_FALLOCATE) && !POSIX_FADVISE_AIX_BUG */ +#endif /* HAVE_POSIX_FALLOCATE) && !POSIX_FADVISE_AIX_BUG && !defined(__wasi__) */ #if defined(HAVE_POSIX_FADVISE) && !defined(POSIX_FADVISE_AIX_BUG) From 0e45786a6aed3d70ad346fd95272858da01464b7 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 10 Nov 2023 08:32:41 +0100 Subject: [PATCH 582/632] gh-111356: io: Add missing documented objects to io.__all__ (GH-111370) Add DEFAULT_BUFFER_SIZE, text_encoding, and IncrementalNewlineDecoder. (cherry picked from commit baeb7718f8981319c5cb1fbdd46d162ded7964ea) Co-authored-by: Nicolas Tessore --- Lib/io.py | 3 ++- Lib/test/test_io.py | 24 +++++++++++-------- ...-10-30-08-50-46.gh-issue-111356.Bc8LvA.rst | 1 + 3 files changed, 17 insertions(+), 11 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-10-30-08-50-46.gh-issue-111356.Bc8LvA.rst diff --git a/Lib/io.py b/Lib/io.py index a4186499c64715..a58ab01ede3a70 100644 --- a/Lib/io.py +++ b/Lib/io.py @@ -45,7 +45,8 @@ "FileIO", "BytesIO", "StringIO", "BufferedIOBase", "BufferedReader", "BufferedWriter", "BufferedRWPair", "BufferedRandom", "TextIOBase", "TextIOWrapper", - "UnsupportedOperation", "SEEK_SET", "SEEK_CUR", "SEEK_END"] + "UnsupportedOperation", "SEEK_SET", "SEEK_CUR", "SEEK_END", + "DEFAULT_BUFFER_SIZE", "text_encoding", "IncrementalNewlineDecoder"] import _io diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index e54a13c2d84bde..0f1d526b0e7b3b 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -3949,19 +3949,18 @@ class PyIncrementalNewlineDecoderTest(IncrementalNewlineDecoderTest): class MiscIOTest(unittest.TestCase): + # for test__all__, actual values are set in subclasses + name_of_module = None + extra_exported = () + not_exported = () + def tearDown(self): os_helper.unlink(os_helper.TESTFN) def test___all__(self): - for name in self.io.__all__: - obj = getattr(self.io, name, None) - self.assertIsNotNone(obj, name) - if name in ("open", "open_code"): - continue - elif "error" in name.lower() or name == "UnsupportedOperation": - self.assertTrue(issubclass(obj, Exception), name) - elif not name.startswith("SEEK_"): - self.assertTrue(issubclass(obj, self.IOBase)) + support.check__all__(self, self.io, self.name_of_module, + extra=self.extra_exported, + not_exported=self.not_exported) def test_attributes(self): f = self.open(os_helper.TESTFN, "wb", buffering=0) @@ -4328,6 +4327,8 @@ def test_openwrapper(self): class CMiscIOTest(MiscIOTest): io = io + name_of_module = "io", "_io" + extra_exported = "BlockingIOError", def test_readinto_buffer_overflow(self): # Issue #18025 @@ -4392,6 +4393,9 @@ def test_daemon_threads_shutdown_stderr_deadlock(self): class PyMiscIOTest(MiscIOTest): io = pyio + name_of_module = "_pyio", "io" + extra_exported = "BlockingIOError", "open_code", + not_exported = "valid_seek_flags", @unittest.skipIf(os.name == 'nt', 'POSIX signals required for this test.') @@ -4679,7 +4683,7 @@ def load_tests(loader, tests, pattern): mocks = (MockRawIO, MisbehavedRawIO, MockFileIO, CloseFailureIO, MockNonBlockWriterIO, MockUnseekableIO, MockRawIOWithoutRead, SlowFlushRawIO) - all_members = io.__all__ + ["IncrementalNewlineDecoder"] + all_members = io.__all__ c_io_ns = {name : getattr(io, name) for name in all_members} py_io_ns = {name : getattr(pyio, name) for name in all_members} globs = globals() 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 new file mode 100644 index 00000000000000..a821b52b982175 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-10-30-08-50-46.gh-issue-111356.Bc8LvA.rst @@ -0,0 +1 @@ +Added :func:`io.text_encoding()`, :data:`io.DEFAULT_BUFFER_SIZE`, and :class:`io.IncrementalNewlineDecoder` to ``io.__all__``. From ecc8df2ee0f32de542df8ea26d0c0533c98ba677 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 10 Nov 2023 10:54:38 +0100 Subject: [PATCH 583/632] [3.11] gh-111929: Fix regrtest --pgo: test_str => test_unicode (GH-111938) (#111940) gh-111929: Fix regrtest --pgo: test_str => test_unicode (GH-111938) test_unicode was renamed to test_str in Python 3.13, but Python 3.12 still uses test_unicode name. (cherry picked from commit 5f42a2bc4017f2ed023f9cf19fdbffabd57527f5) Co-authored-by: Victor Stinner --- Lib/test/libregrtest/pgo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/libregrtest/pgo.py b/Lib/test/libregrtest/pgo.py index e3a6927be5db1d..cabbba73d5eff5 100644 --- a/Lib/test/libregrtest/pgo.py +++ b/Lib/test/libregrtest/pgo.py @@ -42,10 +42,10 @@ 'test_set', 'test_sqlite3', 'test_statistics', - 'test_str', 'test_struct', 'test_tabnanny', 'test_time', + 'test_unicode', 'test_xml_etree', 'test_xml_etree_c', ] From 7b8445109d7dd8b319f5239ca013e83938579b4c Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 10 Nov 2023 13:23:57 +0100 Subject: [PATCH 584/632] [3.11] gh-108303: Install `Lib/test/configdata` (GH-111899) (#111945) gh-108303: Install `Lib/test/configdata` (GH-111899) (cherry picked from commit 65d6dc27156112ac6a9f722b7b62529c94e0344b) Co-authored-by: Nikita Sobolev --- Makefile.pre.in | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile.pre.in b/Makefile.pre.in index ada866d83c0423..21aecd4161adb2 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1951,6 +1951,7 @@ TESTSUBDIRS= ctypes/test \ test/certdata/capath \ test/cjkencodings \ test/crashers \ + test/configdata \ test/data \ test/decimaltestdata \ test/dtracedata \ From 8222ad01fe5e802649f0a1bd30d26d4623533c51 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 10 Nov 2023 14:32:02 +0100 Subject: [PATCH 585/632] [3.11] [3.12] gh-109181: Fix refleak in tb_get_lineno() (GH-111948) (#111951) [3.12] gh-109181: Fix refleak in tb_get_lineno() (GH-111948) PyFrame_GetCode() returns a strong reference. (cherry picked from commit 4b0c875d91727440251a8427a80d8515e39d18cd) Co-authored-by: Victor Stinner --- Python/traceback.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Python/traceback.c b/Python/traceback.c index 664a73fdfbaa31..722f007459dc3b 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -115,7 +115,10 @@ static int tb_get_lineno(PyTracebackObject* tb) { PyFrameObject* frame = tb->tb_frame; assert(frame != NULL); - return PyCode_Addr2Line(PyFrame_GetCode(frame), tb->tb_lasti); + PyCodeObject *code = PyFrame_GetCode(frame); + int lineno = PyCode_Addr2Line(code, tb->tb_lasti); + Py_DECREF(code); + return lineno; } static PyObject * From e433785e489285b2f9c77731c799294309c2cec9 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 10 Nov 2023 15:12:24 +0100 Subject: [PATCH 586/632] [3.11] gh-111912: Run test_posix on Windows (GH-111913) (GH-111954) (cherry picked from commit 64fea3211d08082236d05c38ee728f922eb7d8ed) Co-authored-by: Serhiy Storchaka --- Lib/test/test_posix.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index e4956b55a07e0a..912f07ec4436b5 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -6,9 +6,6 @@ from test.support import warnings_helper from test.support.script_helper import assert_python_ok -# Skip these tests if there is no posix module. -posix = import_helper.import_module('posix') - import errno import sys import signal @@ -22,6 +19,11 @@ import textwrap from contextlib import contextmanager +try: + import posix +except ImportError: + import nt as posix + try: import pwd except ImportError: @@ -1009,6 +1011,7 @@ def test_environ(self): self.assertEqual(type(k), item_type) self.assertEqual(type(v), item_type) + @unittest.skipUnless(os.name == 'posix', "see bug gh-111841") def test_putenv(self): with self.assertRaises(ValueError): os.putenv('FRUIT\0VEGETABLE', 'cabbage') @@ -1220,6 +1223,7 @@ def test_sched_setaffinity(self): self.assertRaises(OSError, posix.sched_setaffinity, -1, mask) @unittest.skipIf(support.is_wasi, "No dynamic linking on WASI") + @unittest.skipUnless(os.name == 'posix', "POSIX-only test") def test_rtld_constants(self): # check presence of major RTLD_* constants posix.RTLD_LAZY From 7b55a955bc4066c5db4c842b3d7319df487fb750 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 10 Nov 2023 15:43:51 +0100 Subject: [PATCH 587/632] [3.11] gh-111251: Fix error checking in _blake2 module init (GH-111252) (#111298) Co-authored-by: Nikita Sobolev Co-authored-by: Hugo van Kemenade --- ...-10-24-12-09-46.gh-issue-111251.urFYtn.rst | 1 + Modules/_blake2/blake2module.c | 25 +++++++++++++------ 2 files changed, 18 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-10-24-12-09-46.gh-issue-111251.urFYtn.rst 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 new file mode 100644 index 00000000000000..3a87cb25da5cb4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-10-24-12-09-46.gh-issue-111251.urFYtn.rst @@ -0,0 +1 @@ +Fix :mod:`_blake2` not checking for errors when initializing. diff --git a/Modules/_blake2/blake2module.c b/Modules/_blake2/blake2module.c index 44d783b40d0453..93478f5c8bda0d 100644 --- a/Modules/_blake2/blake2module.c +++ b/Modules/_blake2/blake2module.c @@ -74,6 +74,12 @@ _blake2_free(void *module) Py_DECREF(x); \ } while(0) +#define ADD_INT_CONST(NAME, VALUE) do { \ + if (PyModule_AddIntConstant(m, NAME, VALUE) < 0) { \ + return -1; \ + } \ +} while (0) + static int blake2_exec(PyObject *m) { @@ -95,10 +101,10 @@ blake2_exec(PyObject *m) ADD_INT(d, "MAX_KEY_SIZE", BLAKE2B_KEYBYTES); ADD_INT(d, "MAX_DIGEST_SIZE", BLAKE2B_OUTBYTES); - PyModule_AddIntConstant(m, "BLAKE2B_SALT_SIZE", BLAKE2B_SALTBYTES); - PyModule_AddIntConstant(m, "BLAKE2B_PERSON_SIZE", BLAKE2B_PERSONALBYTES); - PyModule_AddIntConstant(m, "BLAKE2B_MAX_KEY_SIZE", BLAKE2B_KEYBYTES); - PyModule_AddIntConstant(m, "BLAKE2B_MAX_DIGEST_SIZE", BLAKE2B_OUTBYTES); + ADD_INT_CONST("BLAKE2B_SALT_SIZE", BLAKE2B_SALTBYTES); + ADD_INT_CONST("BLAKE2B_PERSON_SIZE", BLAKE2B_PERSONALBYTES); + ADD_INT_CONST("BLAKE2B_MAX_KEY_SIZE", BLAKE2B_KEYBYTES); + ADD_INT_CONST("BLAKE2B_MAX_DIGEST_SIZE", BLAKE2B_OUTBYTES); /* BLAKE2s */ st->blake2s_type = (PyTypeObject *)PyType_FromModuleAndSpec( @@ -117,14 +123,17 @@ blake2_exec(PyObject *m) ADD_INT(d, "MAX_KEY_SIZE", BLAKE2S_KEYBYTES); ADD_INT(d, "MAX_DIGEST_SIZE", BLAKE2S_OUTBYTES); - PyModule_AddIntConstant(m, "BLAKE2S_SALT_SIZE", BLAKE2S_SALTBYTES); - PyModule_AddIntConstant(m, "BLAKE2S_PERSON_SIZE", BLAKE2S_PERSONALBYTES); - PyModule_AddIntConstant(m, "BLAKE2S_MAX_KEY_SIZE", BLAKE2S_KEYBYTES); - PyModule_AddIntConstant(m, "BLAKE2S_MAX_DIGEST_SIZE", BLAKE2S_OUTBYTES); + ADD_INT_CONST("BLAKE2S_SALT_SIZE", BLAKE2S_SALTBYTES); + ADD_INT_CONST("BLAKE2S_PERSON_SIZE", BLAKE2S_PERSONALBYTES); + ADD_INT_CONST("BLAKE2S_MAX_KEY_SIZE", BLAKE2S_KEYBYTES); + ADD_INT_CONST("BLAKE2S_MAX_DIGEST_SIZE", BLAKE2S_OUTBYTES); return 0; } +#undef ADD_INT +#undef ADD_INT_CONST + static PyModuleDef_Slot _blake2_slots[] = { {Py_mod_exec, blake2_exec}, {0, NULL} From cd3e2d3a6ca4bfcc8c99eb50e92a2ce0b5b1ddf6 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 11 Nov 2023 10:17:29 +0100 Subject: [PATCH 588/632] [3.11] gh-111841: Fix os.putenv() and os.unsetenv() with embedded NUL on Windows (GH-111842) (GH-111967) (cherry picked from commit 0b06d2482d77e02c5d40e221f6046c9c355458b2) Co-authored-by: Serhiy Storchaka --- Lib/test/test_os.py | 5 ++++- Lib/test/test_posix.py | 14 +++++++------- .../2023-11-08-11-50-49.gh-issue-111841.iSqdQf.rst | 2 ++ Modules/posixmodule.c | 7 ++++++- 4 files changed, 19 insertions(+), 9 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-11-08-11-50-49.gh-issue-111841.iSqdQf.rst diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index ff1121efdf74da..441e1f977a55d3 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -1130,9 +1130,12 @@ def test_putenv_unsetenv(self): def test_putenv_unsetenv_error(self): # Empty variable name is invalid. # "=" and null character are not allowed in a variable name. - for name in ('', '=name', 'na=me', 'name=', 'name\0', 'na\0me'): + for name in ('', '=name', 'na=me', 'name='): self.assertRaises((OSError, ValueError), os.putenv, name, "value") self.assertRaises((OSError, ValueError), os.unsetenv, name) + for name in ('name\0', 'na\0me'): + self.assertRaises(ValueError, os.putenv, name, "value") + self.assertRaises(ValueError, os.unsetenv, name) if sys.platform == "win32": # On Windows, an environment variable string ("name=value" string) diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index 912f07ec4436b5..0b1068bfff6ff9 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -1011,20 +1011,20 @@ def test_environ(self): self.assertEqual(type(k), item_type) self.assertEqual(type(v), item_type) - @unittest.skipUnless(os.name == 'posix', "see bug gh-111841") def test_putenv(self): with self.assertRaises(ValueError): os.putenv('FRUIT\0VEGETABLE', 'cabbage') - with self.assertRaises(ValueError): - os.putenv(b'FRUIT\0VEGETABLE', b'cabbage') with self.assertRaises(ValueError): os.putenv('FRUIT', 'orange\0VEGETABLE=cabbage') - with self.assertRaises(ValueError): - os.putenv(b'FRUIT', b'orange\0VEGETABLE=cabbage') with self.assertRaises(ValueError): os.putenv('FRUIT=ORANGE', 'lemon') - with self.assertRaises(ValueError): - os.putenv(b'FRUIT=ORANGE', b'lemon') + if os.name == 'posix': + with self.assertRaises(ValueError): + os.putenv(b'FRUIT\0VEGETABLE', b'cabbage') + with self.assertRaises(ValueError): + os.putenv(b'FRUIT', b'orange\0VEGETABLE=cabbage') + with self.assertRaises(ValueError): + os.putenv(b'FRUIT=ORANGE', b'lemon') @unittest.skipUnless(hasattr(posix, 'getcwd'), 'test needs posix.getcwd()') def test_getcwd_long_pathnames(self): 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 new file mode 100644 index 00000000000000..cd1780988aeac7 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-08-11-50-49.gh-issue-111841.iSqdQf.rst @@ -0,0 +1,2 @@ +Fix truncating arguments on an embedded null character in :meth:`os.putenv` +and :meth:`os.unsetenv` on Windows. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 18e983e5aa1d99..9ac021f6c5f8b0 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -11102,7 +11102,6 @@ win32_putenv(PyObject *name, PyObject *value) } Py_ssize_t size; - /* PyUnicode_AsWideCharString() rejects embedded null characters */ wchar_t *env = PyUnicode_AsWideCharString(unicode, &size); Py_DECREF(unicode); @@ -11116,6 +11115,12 @@ win32_putenv(PyObject *name, PyObject *value) PyMem_Free(env); return NULL; } + if (wcslen(env) != (size_t)size) { + PyErr_SetString(PyExc_ValueError, + "embedded null character"); + PyMem_Free(env); + return NULL; + } /* _wputenv() and SetEnvironmentVariableW() update the environment in the Process Environment Block (PEB). _wputenv() also updates CRT 'environ' From 5a6d1dbc10878ff2d04b2e0687409855787312ff Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 12 Nov 2023 01:24:02 +0100 Subject: [PATCH 589/632] [3.11] Fix undefined behaviour in datetime.time.fromisoformat() (GH-111982) (#111991) Fix undefined behaviour in datetime.time.fromisoformat() (GH-111982) Fix undefined behaviour in datetime.time.fromisoformat() when parsing a string without a timezone. 'tzoffset' is not assigned to by parse_isoformat_time if it returns 0, but time_fromisoformat then passes tzoffset to another function, which is undefined behaviour (even if the function in question does not use the value). (cherry picked from commit 21615f77b5a580e83589abae618dbe7c298700e2) Co-authored-by: T. Wouters --- Modules/_datetimemodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 5c67f38ec2111b..df0ce722d59af8 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -4649,7 +4649,7 @@ time_fromisoformat(PyObject *cls, PyObject *tstr) { } int hour = 0, minute = 0, second = 0, microsecond = 0; - int tzoffset, tzimicrosecond = 0; + int tzoffset = 0, tzimicrosecond = 0; int rv = parse_isoformat_time(p, len, &hour, &minute, &second, µsecond, &tzoffset, &tzimicrosecond); From 9536fe1656a8be775172bf8d14751ad6e3ebbd0a Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 12 Nov 2023 13:49:59 +0100 Subject: [PATCH 590/632] [3.11] gh-112001: Fix test_builtins_have_signatures in test_inspect (GH-112002) (GH-112004) (cherry picked from commit 40752c1c1e8cec80e99a2c9796f4fde2f8b5d3e2) Co-authored-by: Serhiy Storchaka --- Lib/test/test_inspect/test_inspect.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index 6d4050a5ded100..f6d95bd26304fb 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -4285,19 +4285,14 @@ def test_builtins_have_signatures(self): # These have unrepresentable parameter default values of NULL needs_null = {"anext"} no_signature |= needs_null - # These need PEP 457 groups or a signature change to accept None - needs_semantic_update = {"round"} - no_signature |= needs_semantic_update # These need *args support in Argument Clinic - needs_varargs = {"breakpoint", "min", "max", "print", - "__build_class__"} + needs_varargs = {"breakpoint", "min", "max", "__build_class__"} no_signature |= needs_varargs - # These simply weren't covered in the initial AC conversion - # for builtin callables - not_converted_yet = {"open", "__import__"} - no_signature |= not_converted_yet # These builtin types are expected to provide introspection info - types_with_signatures = set() + types_with_signatures = { + 'complex', 'enumerate', 'float', 'list', 'memoryview', 'object', + 'property', 'reversed', 'tuple', + } # Check the signatures we expect to be there ns = vars(builtins) for name, obj in sorted(ns.items()): @@ -4316,9 +4311,9 @@ def test_builtins_have_signatures(self): # This ensures this test will start failing as more signatures are # added, so the affected items can be moved into the scope of the # regression test above - for name in no_signature: + for name in no_signature - needs_null: with self.subTest(builtin=name): - self.assertIsNone(obj.__text_signature__) + self.assertIsNone(ns[name].__text_signature__) def test_python_function_override_signature(self): def func(*args, **kwargs): From aa3232db23e0dabbabb0a1f8fc9bb20bf7ec68c9 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 13 Nov 2023 00:00:38 +0100 Subject: [PATCH 591/632] [3.11] gh-111929: Fix regrtest clear_caches() (#111949) gh-111929: Fix regrtest clear_caches() Python 3.11 doesn't have the fractions._hash_algorithm.cache_clear() function. --- Lib/test/libregrtest/utils.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Lib/test/libregrtest/utils.py b/Lib/test/libregrtest/utils.py index f6de62af1f0ed5..25f3e853137316 100644 --- a/Lib/test/libregrtest/utils.py +++ b/Lib/test/libregrtest/utils.py @@ -272,13 +272,6 @@ def clear_caches(): for f in typing._cleanups: f() - try: - fractions = sys.modules['fractions'] - except KeyError: - pass - else: - fractions._hash_algorithm.cache_clear() - def get_build_info(): # Get most important configure and build options as a list of strings. From b64afbc55c27274a73ade0680606cde2a26470cb Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 13 Nov 2023 01:14:03 +0100 Subject: [PATCH 592/632] [3.11] gh-111944: Add assignment expression parentheses requirements (GH-111977) (#112011) Augment the list of places where parentheses are required around assignnment statements. In particular, 'a := 0' and 'a = b := 1' are syntax errors. (cherry picked from commit 9a2f25d374f027f6509484d66e1c7bba03977b99) Co-authored-by: Terry Jan Reedy --- Doc/reference/expressions.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index 5cba4a6934f05b..9207fd3f3c02ee 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -1764,10 +1764,11 @@ Or, when processing a file stream in chunks: while chunk := file.read(9000): process(chunk) -Assignment expressions must be surrounded by parentheses when used -as sub-expressions in slicing, conditional, lambda, -keyword-argument, and comprehension-if expressions -and in ``assert`` and ``with`` statements. +Assignment expressions must be surrounded by parentheses when +used as expression statements and when used as sub-expressions in +slicing, conditional, lambda, +keyword-argument, and comprehension-if expressions and +in ``assert``, ``with``, and ``assignment`` statements. In all other places where they can be used, parentheses are not required, including in ``if`` and ``while`` statements. From d4217e5db570b72774e5254173dd0718eb3c30bd Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 13 Nov 2023 10:58:43 +0200 Subject: [PATCH 593/632] [3.11] Docs: Add `make htmllive` to rebuild and reload HTML files in your browser (GH-111900) (#112023) Co-authored-by: Hugo van Kemenade --- Doc/Makefile | 35 +++++++++++++++++++++++++++++++---- Doc/requirements.txt | 1 + 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/Doc/Makefile b/Doc/Makefile index ca82353eb454f3..f01b8a4ff958d9 100644 --- a/Doc/Makefile +++ b/Doc/Makefile @@ -22,16 +22,14 @@ PAPEROPT_letter = -D latex_elements.papersize=letterpaper ALLSPHINXOPTS = -b $(BUILDER) -d build/doctrees $(PAPEROPT_$(PAPER)) -j $(JOBS) \ $(SPHINXOPTS) $(SPHINXERRORHANDLING) . build/$(BUILDER) $(SOURCES) -.PHONY: help build html htmlhelp latex text texinfo changes linkcheck \ - suspicious coverage doctest pydoc-topics htmlview clean dist check serve \ - autobuild-dev autobuild-stable venv - +.PHONY: help help: @echo "Please use \`make ' where is one of" @echo " clean to remove build files" @echo " venv to create a venv with necessary tools" @echo " html to make standalone HTML files" @echo " htmlview to open the index page built by the html target in your browser" + @echo " htmllive to rebuild and reload HTML files in your browser" @echo " htmlhelp to make HTML files and a HTML help project" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " text to make plain text files" @@ -46,6 +44,7 @@ help: @echo " suspicious to check for suspicious markup in output text" @echo " check to run a check for frequent markup errors" +.PHONY: build build: -mkdir -p build # Look first for a Misc/NEWS file (building from a source release tarball @@ -72,38 +71,46 @@ build: $(SPHINXBUILD) $(ALLSPHINXOPTS) @echo +.PHONY: html html: BUILDER = html html: build @echo "Build finished. The HTML pages are in build/html." +.PHONY: htmlhelp htmlhelp: BUILDER = htmlhelp htmlhelp: build @echo "Build finished; now you can run HTML Help Workshop with the" \ "build/htmlhelp/pydoc.hhp project file." +.PHONY: latex latex: BUILDER = latex latex: build @echo "Build finished; the LaTeX files are in build/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." +.PHONY: text text: BUILDER = text text: build @echo "Build finished; the text files are in build/text." +.PHONY: texinfo texinfo: BUILDER = texinfo texinfo: build @echo "Build finished; the python.texi file is in build/texinfo." @echo "Run \`make info' in that directory to run it through makeinfo." +.PHONY: epub epub: BUILDER = epub epub: build @echo "Build finished; the epub files are in build/epub." +.PHONY: changes changes: BUILDER = changes changes: build @echo "The overview file is in build/changes." +.PHONY: linkcheck linkcheck: BUILDER = linkcheck linkcheck: @$(MAKE) build BUILDER=$(BUILDER) || { \ @@ -111,6 +118,7 @@ linkcheck: "or in build/$(BUILDER)/output.txt"; \ false; } +.PHONY: suspicious suspicious: BUILDER = suspicious suspicious: @$(MAKE) build BUILDER=$(BUILDER) || { \ @@ -123,10 +131,12 @@ suspicious: @echo "⚠ make check" @echo "⚠ instead." +.PHONY: coverage coverage: BUILDER = coverage coverage: build @echo "Coverage finished; see c.txt and python.txt in build/coverage" +.PHONY: doctest doctest: BUILDER = doctest doctest: @$(MAKE) build BUILDER=$(BUILDER) || { \ @@ -134,20 +144,30 @@ doctest: "results in build/doctest/output.txt"; \ false; } +.PHONY: pydoc-topics pydoc-topics: BUILDER = pydoc-topics pydoc-topics: build @echo "Building finished; now run this:" \ "cp build/pydoc-topics/topics.py ../Lib/pydoc_data/topics.py" +.PHONY: htmlview htmlview: html $(PYTHON) -c "import os, webbrowser; webbrowser.open('file://' + os.path.realpath('build/html/index.html'))" +.PHONY: htmllive +htmllive: SPHINXBUILD = $(VENVDIR)/bin/sphinx-autobuild +htmllive: SPHINXOPTS = --re-ignore="/venv/" +htmllive: html + +.PHONY: clean clean: clean-venv -rm -rf build/* +.PHONY: clean-venv clean-venv: rm -rf $(VENVDIR) +.PHONY: venv venv: @if [ -d $(VENVDIR) ] ; then \ echo "venv already exists."; \ @@ -159,6 +179,7 @@ venv: echo "The venv has been created in the $(VENVDIR) directory"; \ fi +.PHONY: dist dist: rm -rf dist mkdir -p dist @@ -213,10 +234,12 @@ dist: rm -r dist/python-$(DISTVERSION)-docs-texinfo rm dist/python-$(DISTVERSION)-docs-texinfo.tar +.PHONY: check check: venv $(VENVDIR)/bin/python3 -m pre_commit --version > /dev/null || $(VENVDIR)/bin/python3 -m pip install pre-commit $(VENVDIR)/bin/python3 -m pre_commit run --all-files +.PHONY: serve serve: @echo "The serve target was removed, use htmlview instead (see bpo-36329)" @@ -228,15 +251,18 @@ serve: # output files) # for development releases: always build +.PHONY: autobuild-dev autobuild-dev: make dist SPHINXOPTS='$(SPHINXOPTS) -Ea -A daily=1' # for quick rebuilds (HTML only) +.PHONY: autobuild-dev-html autobuild-dev-html: make html SPHINXOPTS='$(SPHINXOPTS) -Ea -A daily=1' # for stable releases: only build if not in pre-release stage (alpha, beta) # release candidate downloads are okay, since the stable tree can be in that stage +.PHONY: autobuild-stable autobuild-stable: @case $(DISTVERSION) in *[ab]*) \ echo "Not building; $(DISTVERSION) is not a release version."; \ @@ -244,6 +270,7 @@ autobuild-stable: esac @make autobuild-dev +.PHONY: autobuild-stable-html autobuild-stable-html: @case $(DISTVERSION) in *[ab]*) \ echo "Not building; $(DISTVERSION) is not a release version."; \ diff --git a/Doc/requirements.txt b/Doc/requirements.txt index df79ae63840471..d81fef288caed3 100644 --- a/Doc/requirements.txt +++ b/Doc/requirements.txt @@ -10,6 +10,7 @@ sphinx==4.5.0 blurb +sphinx-autobuild sphinxext-opengraph>=0.7.1 # The theme used by the documentation is stored separately, so we need From 4baf633c5322ce48f2540e2a41ccbe4a92d4025a Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 13 Nov 2023 14:01:17 +0000 Subject: [PATCH 594/632] [3.11] gh-111681: minor fix to a typing doctest (#111682) (#112037) --- Doc/library/typing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index f00a4ce20c8a51..0eda01a323e490 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1785,7 +1785,7 @@ for creating generic types. .. doctest:: - >>> from typing import ParamSpec + >>> from typing import ParamSpec, get_origin >>> P = ParamSpec("P") >>> get_origin(P.args) is P True From 46081fe9b50230530c34b3594da04b279d52f102 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 13 Nov 2023 20:50:56 +0100 Subject: [PATCH 595/632] [3.11] gh-112007: Re-organize help utility intro message (GH-112017) (#112048) gh-112007: Re-organize help utility intro message (GH-112017) Most important: move how-to-quit sentence to the end and mention 'q'. Re-group the other sentences and improve some wording. --------- (cherry picked from commit b28bb130bbc2ad956828819967d83e06d30a65c5) Co-authored-by: Terry Jan Reedy --- Lib/pydoc.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/Lib/pydoc.py b/Lib/pydoc.py index 5e191bc1c4b2a6..14b99039b3bbda 100755 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -2073,20 +2073,22 @@ def help(self, request, is_cli=False): self.output.write('\n') def intro(self): - self.output.write(''' -Welcome to Python {0}'s help utility! - -If this is your first time using Python, you should definitely check out -the tutorial on the internet at https://docs.python.org/{0}/tutorial/. + self.output.write('''\ +Welcome to Python {0}'s help utility! If this is your first time using +Python, you should definitely check out the tutorial at +https://docs.python.org/{0}/tutorial/. Enter the name of any module, keyword, or topic to get help on writing -Python programs and using Python modules. To quit this help utility and -return to the interpreter, just type "quit". +Python programs and using Python modules. To get a list of available +modules, keywords, symbols, or topics, enter "modules", "keywords", +"symbols", or "topics". + +Each module also comes with a one-line summary of what it does; to list +the modules whose name or summary contain a given string such as "spam", +enter "modules spam". -To get a list of available modules, keywords, symbols, or topics, type -"modules", "keywords", "symbols", or "topics". Each module also comes -with a one-line summary of what it does; to list the modules whose name -or summary contain a given string such as "spam", type "modules spam". +To quit this help utility and return to the interpreter, +enter "q" or "quit". '''.format('%d.%d' % sys.version_info[:2])) def list(self, items, columns=4, width=80): From e73216d32b10ffbf9d0ee82e8c646b91f745ed91 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 14 Nov 2023 13:25:38 +0100 Subject: [PATCH 596/632] [3.11] gh-110944: Move pty helper to test.support and add basic pdb completion test (GH-111826) (GH-112025) gh-110944: Move pty helper to test.support and add basic pdb completion test (GH-111826) (cherry picked from commit 1c7ed7e9ebc53290c831d7b610219fa737153a1b) Co-authored-by: Tian Gao --- Lib/test/support/pty_helper.py | 60 ++++++++++++++++++++++++++++++++++ Lib/test/test_pdb.py | 30 +++++++++++++++++ Lib/test/test_readline.py | 55 +------------------------------ 3 files changed, 91 insertions(+), 54 deletions(-) create mode 100644 Lib/test/support/pty_helper.py diff --git a/Lib/test/support/pty_helper.py b/Lib/test/support/pty_helper.py new file mode 100644 index 00000000000000..11037d22516448 --- /dev/null +++ b/Lib/test/support/pty_helper.py @@ -0,0 +1,60 @@ +""" +Helper to run a script in a pseudo-terminal. +""" +import os +import selectors +import subprocess +import sys +from contextlib import ExitStack +from errno import EIO + +from test.support.import_helper import import_module + +def run_pty(script, input=b"dummy input\r", env=None): + pty = import_module('pty') + output = bytearray() + [master, slave] = pty.openpty() + args = (sys.executable, '-c', script) + proc = subprocess.Popen(args, stdin=slave, stdout=slave, stderr=slave, env=env) + os.close(slave) + with ExitStack() as cleanup: + cleanup.enter_context(proc) + def terminate(proc): + try: + proc.terminate() + except ProcessLookupError: + # Workaround for Open/Net BSD bug (Issue 16762) + pass + cleanup.callback(terminate, proc) + cleanup.callback(os.close, master) + # Avoid using DefaultSelector and PollSelector. Kqueue() does not + # work with pseudo-terminals on OS X < 10.9 (Issue 20365) and Open + # BSD (Issue 20667). Poll() does not work with OS X 10.6 or 10.4 + # either (Issue 20472). Hopefully the file descriptor is low enough + # to use with select(). + sel = cleanup.enter_context(selectors.SelectSelector()) + sel.register(master, selectors.EVENT_READ | selectors.EVENT_WRITE) + os.set_blocking(master, False) + while True: + for [_, events] in sel.select(): + if events & selectors.EVENT_READ: + try: + chunk = os.read(master, 0x10000) + except OSError as err: + # Linux raises EIO when slave is closed (Issue 5380) + if err.errno != EIO: + raise + chunk = b"" + if not chunk: + return output + output.extend(chunk) + if events & selectors.EVENT_WRITE: + try: + input = input[os.write(master, input):] + except OSError as err: + # Apparently EIO means the slave was closed + if err.errno != EIO: + raise + input = b"" # Stop writing + if not input: + sel.modify(master, selectors.EVENT_READ) diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 5f9bcc5af82ad6..7d4c686ac77faf 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -15,6 +15,8 @@ from io import StringIO from test import support from test.support import os_helper +from test.support.import_helper import import_module +from test.support.pty_helper import run_pty # This little helper class is essential for testing pdb under doctest. from test.test_doctest import _FakeInput from unittest.mock import patch @@ -2485,6 +2487,34 @@ def test_checkline_is_not_executable(self): self.assertFalse(db.checkline(os_helper.TESTFN, lineno)) +@support.requires_subprocess() +class PdbTestReadline(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') + if readline.__doc__ and "libedit" in readline.__doc__: + raise unittest.SkipTest("libedit readline is not supported for pdb") + + def test_basic_completion(self): + script = textwrap.dedent(""" + import pdb; pdb.Pdb().set_trace() + # Concatenate strings so that the output doesn't appear in the source + print('hello' + '!') + """) + + # List everything starting with 'co', there should be multiple matches + # then add ntin and complete 'contin' to 'continue' + input = b"co\t\tntin\t\n" + + output = run_pty(script, input) + + self.assertIn(b'commands', output) + self.assertIn(b'condition', output) + self.assertIn(b'continue', output) + self.assertIn(b'hello!', output) + + def load_tests(loader, tests, pattern): from test import test_pdb tests.addTest(doctest.DocTestSuite(test_pdb)) diff --git a/Lib/test/test_readline.py b/Lib/test/test_readline.py index 59dbef90380053..835280f2281cde 100644 --- a/Lib/test/test_readline.py +++ b/Lib/test/test_readline.py @@ -1,18 +1,15 @@ """ Very minimal unittests for parts of the readline module. """ -from contextlib import ExitStack -from errno import EIO import locale import os -import selectors -import subprocess import sys import tempfile import unittest from test.support import verbose from test.support.import_helper import import_module from test.support.os_helper import unlink, temp_dir, TESTFN +from test.support.pty_helper import run_pty from test.support.script_helper import assert_python_ok # Skip tests if there is no readline module @@ -304,55 +301,5 @@ def test_history_size(self): self.assertEqual(lines[-1].strip(), b"last input") -def run_pty(script, input=b"dummy input\r", env=None): - pty = import_module('pty') - output = bytearray() - [master, slave] = pty.openpty() - args = (sys.executable, '-c', script) - proc = subprocess.Popen(args, stdin=slave, stdout=slave, stderr=slave, env=env) - os.close(slave) - with ExitStack() as cleanup: - cleanup.enter_context(proc) - def terminate(proc): - try: - proc.terminate() - except ProcessLookupError: - # Workaround for Open/Net BSD bug (Issue 16762) - pass - cleanup.callback(terminate, proc) - cleanup.callback(os.close, master) - # Avoid using DefaultSelector and PollSelector. Kqueue() does not - # work with pseudo-terminals on OS X < 10.9 (Issue 20365) and Open - # BSD (Issue 20667). Poll() does not work with OS X 10.6 or 10.4 - # either (Issue 20472). Hopefully the file descriptor is low enough - # to use with select(). - sel = cleanup.enter_context(selectors.SelectSelector()) - sel.register(master, selectors.EVENT_READ | selectors.EVENT_WRITE) - os.set_blocking(master, False) - while True: - for [_, events] in sel.select(): - if events & selectors.EVENT_READ: - try: - chunk = os.read(master, 0x10000) - except OSError as err: - # Linux raises EIO when slave is closed (Issue 5380) - if err.errno != EIO: - raise - chunk = b"" - if not chunk: - return output - output.extend(chunk) - if events & selectors.EVENT_WRITE: - try: - input = input[os.write(master, input):] - except OSError as err: - # Apparently EIO means the slave was closed - if err.errno != EIO: - raise - input = b"" # Stop writing - if not input: - sel.modify(master, selectors.EVENT_READ) - - if __name__ == "__main__": unittest.main() From a92b9e5b2b50638c1a01e0e7fe77c81148d74285 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 15 Nov 2023 06:20:17 +0100 Subject: [PATCH 597/632] [3.11] Docs: Add the time to the HTML last updated format (GH-110091) (#112103) Docs: Add the time to the HTML last updated format (GH-110091) (cherry picked from commit 6c214dea7c503eb42bd130d43e8880f39bff0350) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/conf.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Doc/conf.py b/Doc/conf.py index 8c92799ecbbf3b..3f5ba5d969d313 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -280,9 +280,8 @@ "pr_id": os.getenv("READTHEDOCS_VERSION") } -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -html_last_updated_fmt = '%b %d, %Y' +# This 'Last updated on:' timestamp is inserted at the bottom of every page. +html_last_updated_fmt = time.strftime('%b %d, %Y (%H:%M UTC)', time.gmtime()) # Path to find HTML templates. templates_path = ['tools/templates'] From e2421a36f0868e2c5eb492ca9e74ae3d7c37357e Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 15 Nov 2023 15:20:18 +0100 Subject: [PATCH 598/632] [3.11] gh-111942: Fix crashes in TextIOWrapper.reconfigure() (GH-111976) (GH-112059) * Fix crash when encoding is not string or None. * Fix crash when both line_buffering and write_through raise exception when converted ti int. * Add a number of tests for constructor and reconfigure() method with invalid arguments. (cherry picked from commit ee06fffd38cb51ce1c045da9d8336d9ce13c318a) Co-authored-by: Serhiy Storchaka --- Lib/test/test_io.py | 84 ++++++++++++++++++- ...-11-10-22-08-28.gh-issue-111942.MDFm6v.rst | 2 + ...-11-14-18-43-55.gh-issue-111942.x1pnrj.rst | 2 + Modules/_io/textio.c | 50 ++++++++++- 4 files changed, 132 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-11-10-22-08-28.gh-issue-111942.MDFm6v.rst create mode 100644 Misc/NEWS.d/next/Library/2023-11-14-18-43-55.gh-issue-111942.x1pnrj.rst diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 0f1d526b0e7b3b..78537057f32666 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -81,6 +81,10 @@ def _default_chunk_size(): ) +class BadIndex: + def __index__(self): + 1/0 + class MockRawIOWithoutRead: """A RawIO implementation without read(), so as to exercise the default RawIO.read() which calls readinto().""" @@ -2613,8 +2617,29 @@ def test_constructor(self): self.assertEqual(t.encoding, "utf-8") self.assertEqual(t.line_buffering, True) self.assertEqual("\xe9\n", t.readline()) - self.assertRaises(TypeError, t.__init__, b, encoding="utf-8", newline=42) - self.assertRaises(ValueError, t.__init__, b, encoding="utf-8", newline='xyzzy') + invalid_type = TypeError if self.is_C else ValueError + with self.assertRaises(invalid_type): + t.__init__(b, encoding=42) + with self.assertRaises(UnicodeEncodeError): + t.__init__(b, encoding='\udcfe') + with self.assertRaises(ValueError): + t.__init__(b, encoding='utf-8\0') + with self.assertRaises(invalid_type): + t.__init__(b, encoding="utf-8", errors=42) + if support.Py_DEBUG or sys.flags.dev_mode or self.is_C: + with self.assertRaises(UnicodeEncodeError): + t.__init__(b, encoding="utf-8", errors='\udcfe') + if support.Py_DEBUG or sys.flags.dev_mode or self.is_C: + with self.assertRaises(ValueError): + t.__init__(b, encoding="utf-8", errors='replace\0') + with self.assertRaises(TypeError): + t.__init__(b, encoding="utf-8", newline=42) + with self.assertRaises(ValueError): + t.__init__(b, encoding="utf-8", newline='\udcfe') + with self.assertRaises(ValueError): + t.__init__(b, encoding="utf-8", newline='\n\0') + with self.assertRaises(ValueError): + t.__init__(b, encoding="utf-8", newline='xyzzy') def test_uninitialized(self): t = self.TextIOWrapper.__new__(self.TextIOWrapper) @@ -3663,6 +3688,59 @@ def test_reconfigure_defaults(self): self.assertEqual(txt.detach().getvalue(), b'LF\nCRLF\r\n') + def test_reconfigure_errors(self): + txt = self.TextIOWrapper(self.BytesIO(), 'ascii', 'replace', '\r') + with self.assertRaises(TypeError): # there was a crash + txt.reconfigure(encoding=42) + if self.is_C: + with self.assertRaises(UnicodeEncodeError): + txt.reconfigure(encoding='\udcfe') + with self.assertRaises(LookupError): + txt.reconfigure(encoding='locale\0') + # TODO: txt.reconfigure(encoding='utf-8\0') + # TODO: txt.reconfigure(encoding='nonexisting') + with self.assertRaises(TypeError): + txt.reconfigure(errors=42) + if self.is_C: + with self.assertRaises(UnicodeEncodeError): + txt.reconfigure(errors='\udcfe') + # TODO: txt.reconfigure(errors='ignore\0') + # TODO: txt.reconfigure(errors='nonexisting') + with self.assertRaises(TypeError): + txt.reconfigure(newline=42) + with self.assertRaises(ValueError): + txt.reconfigure(newline='\udcfe') + with self.assertRaises(ValueError): + txt.reconfigure(newline='xyz') + if not self.is_C: + # TODO: Should fail in C too. + with self.assertRaises(ValueError): + txt.reconfigure(newline='\n\0') + if self.is_C: + # TODO: Use __bool__(), not __index__(). + with self.assertRaises(ZeroDivisionError): + txt.reconfigure(line_buffering=BadIndex()) + with self.assertRaises(OverflowError): + txt.reconfigure(line_buffering=2**1000) + with self.assertRaises(ZeroDivisionError): + txt.reconfigure(write_through=BadIndex()) + with self.assertRaises(OverflowError): + txt.reconfigure(write_through=2**1000) + with self.assertRaises(ZeroDivisionError): # there was a crash + txt.reconfigure(line_buffering=BadIndex(), + write_through=BadIndex()) + self.assertEqual(txt.encoding, 'ascii') + self.assertEqual(txt.errors, 'replace') + self.assertIs(txt.line_buffering, False) + self.assertIs(txt.write_through, False) + + txt.reconfigure(encoding='latin1', errors='ignore', newline='\r\n', + line_buffering=True, write_through=True) + self.assertEqual(txt.encoding, 'latin1') + self.assertEqual(txt.errors, 'ignore') + self.assertIs(txt.line_buffering, True) + self.assertIs(txt.write_through, True) + def test_reconfigure_newline(self): raw = self.BytesIO(b'CR\rEOF') txt = self.TextIOWrapper(raw, 'ascii', newline='\n') @@ -4693,9 +4771,11 @@ def load_tests(loader, tests, pattern): if test.__name__.startswith("C"): for name, obj in c_io_ns.items(): setattr(test, name, obj) + test.is_C = True elif test.__name__.startswith("Py"): for name, obj in py_io_ns.items(): setattr(test, name, obj) + test.is_C = False suite = loader.suiteClass() for test in tests: 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 new file mode 100644 index 00000000000000..4fc505c8f257a6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-10-22-08-28.gh-issue-111942.MDFm6v.rst @@ -0,0 +1,2 @@ +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-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 new file mode 100644 index 00000000000000..ca58a6fa5d6ae1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-14-18-43-55.gh-issue-111942.x1pnrj.rst @@ -0,0 +1,2 @@ +Fix SystemError in the TextIOWrapper constructor with non-encodable "errors" +argument in non-debug mode. diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 7d5afb4c4441d6..b994dc3d7c45b1 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -1099,6 +1099,15 @@ _io_TextIOWrapper___init___impl(textio *self, PyObject *buffer, else if (io_check_errors(errors)) { return -1; } + Py_ssize_t errors_len; + const char *errors_str = PyUnicode_AsUTF8AndSize(errors, &errors_len); + if (errors_str == NULL) { + return -1; + } + if (strlen(errors_str) != (size_t)errors_len) { + PyErr_SetString(PyExc_ValueError, "embedded null character"); + return -1; + } if (validate_newline(newline) < 0) { return -1; @@ -1171,11 +1180,11 @@ _io_TextIOWrapper___init___impl(textio *self, PyObject *buffer, Py_INCREF(buffer); /* Build the decoder object */ - if (_textiowrapper_set_decoder(self, codec_info, PyUnicode_AsUTF8(errors)) != 0) + if (_textiowrapper_set_decoder(self, codec_info, errors_str) != 0) goto error; /* Build the encoder object */ - if (_textiowrapper_set_encoder(self, codec_info, PyUnicode_AsUTF8(errors)) != 0) + if (_textiowrapper_set_encoder(self, codec_info, errors_str) != 0) goto error; /* Finished sorting out the codec details */ @@ -1272,24 +1281,34 @@ textiowrapper_change_encoding(textio *self, PyObject *encoding, errors = &_Py_ID(strict); } } + Py_INCREF(errors); + const char *c_encoding = PyUnicode_AsUTF8(encoding); + if (c_encoding == NULL) { + Py_DECREF(encoding); + Py_DECREF(errors); + return -1; + } const char *c_errors = PyUnicode_AsUTF8(errors); if (c_errors == NULL) { Py_DECREF(encoding); + Py_DECREF(errors); return -1; } // Create new encoder & decoder PyObject *codec_info = _PyCodec_LookupTextEncoding( - PyUnicode_AsUTF8(encoding), "codecs.open()"); + c_encoding, "codecs.open()"); if (codec_info == NULL) { Py_DECREF(encoding); + Py_DECREF(errors); return -1; } if (_textiowrapper_set_decoder(self, codec_info, c_errors) != 0 || _textiowrapper_set_encoder(self, codec_info, c_errors) != 0) { Py_DECREF(codec_info); Py_DECREF(encoding); + Py_DECREF(errors); return -1; } Py_DECREF(codec_info); @@ -1327,6 +1346,26 @@ _io_TextIOWrapper_reconfigure_impl(textio *self, PyObject *encoding, int write_through; const char *newline = NULL; + if (encoding != Py_None && !PyUnicode_Check(encoding)) { + PyErr_Format(PyExc_TypeError, + "reconfigure() argument 'encoding' must be str or None, not %s", + Py_TYPE(encoding)->tp_name); + return NULL; + } + if (errors != Py_None && !PyUnicode_Check(errors)) { + PyErr_Format(PyExc_TypeError, + "reconfigure() argument 'errors' must be str or None, not %s", + Py_TYPE(errors)->tp_name); + return NULL; + } + if (newline_obj != NULL && newline_obj != Py_None && + !PyUnicode_Check(newline_obj)) + { + PyErr_Format(PyExc_TypeError, + "reconfigure() argument 'newline' must be str or None, not %s", + Py_TYPE(newline_obj)->tp_name); + return NULL; + } /* Check if something is in the read buffer */ if (self->decoded_chars != NULL) { if (encoding != Py_None || errors != Py_None || newline_obj != NULL) { @@ -1345,9 +1384,12 @@ _io_TextIOWrapper_reconfigure_impl(textio *self, PyObject *encoding, line_buffering = convert_optional_bool(line_buffering_obj, self->line_buffering); + if (line_buffering < 0) { + return NULL; + } write_through = convert_optional_bool(write_through_obj, self->write_through); - if (line_buffering < 0 || write_through < 0) { + if (write_through < 0) { return NULL; } From cdfef0c38317ca25841a0461a934ebc3a8b81686 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Thu, 16 Nov 2023 02:39:30 +0200 Subject: [PATCH 599/632] [3.11] gh-111062: CI: Move OS test jobs to reusable workflows (gh-111570) CI: Move OS test jobs to reusable workflows Co-authored-by: Donghee Na --- .github/workflows/build.yml | 161 +++---------------------- .github/workflows/reusable-macos.yml | 46 +++++++ .github/workflows/reusable-ubuntu.yml | 71 +++++++++++ .github/workflows/reusable-windows.yml | 53 ++++++++ 4 files changed, 184 insertions(+), 147 deletions(-) create mode 100644 .github/workflows/reusable-macos.yml create mode 100644 .github/workflows/reusable-ubuntu.yml create mode 100644 .github/workflows/reusable-windows.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 71901d4cab2ae5..ad9ec980a9540a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -99,7 +99,7 @@ jobs: steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v4 - - name: Install Dependencies + - name: Install dependencies run: | sudo ./.github/workflows/posix-deps-apt.sh sudo apt-get install -yq abigail-tools @@ -115,7 +115,7 @@ jobs: run: | if ! make check-abidump; then echo "Generated ABI file is not up to date." - echo "Please, add the release manager of this branch as a reviewer of this PR." + echo "Please add the release manager of this branch as a reviewer of this PR." echo "" echo "The up to date ABI file should be attached to this build as an artifact." echo "" @@ -194,159 +194,32 @@ jobs: - name: Check limited ABI symbols run: make check-limited-abi - build_win32: - name: 'Windows (x86)' - runs-on: windows-latest - timeout-minutes: 60 - needs: check_source - if: needs.check_source.outputs.run_tests == 'true' - env: - IncludeUwp: 'true' - steps: - - uses: actions/checkout@v4 - - name: Build CPython - run: .\PCbuild\build.bat -e -d -p Win32 - - name: Display build info - run: .\python.bat -m test.pythoninfo - - name: Tests - run: .\PCbuild\rt.bat -p Win32 -d -q -uall -u-cpu -rwW --slowest --timeout=1200 -j0 - - build_win_amd64: - name: 'Windows (x64)' - runs-on: windows-latest - timeout-minutes: 60 + build_windows: + name: 'Windows' needs: check_source if: needs.check_source.outputs.run_tests == 'true' - env: - IncludeUwp: 'true' - steps: - - uses: actions/checkout@v4 - - name: Register MSVC problem matcher - run: echo "::add-matcher::.github/problem-matchers/msvc.json" - - name: Build CPython - run: .\PCbuild\build.bat -e -d -p x64 - - name: Display build info - run: .\python.bat -m test.pythoninfo - - name: Tests - run: .\PCbuild\rt.bat -p x64 -d -q -uall -u-cpu -rwW --slowest --timeout=1200 -j0 - - build_win_arm64: - name: 'Windows (arm64)' - runs-on: windows-latest - timeout-minutes: 60 - needs: check_source - if: needs.check_source.outputs.run_tests == 'true' - env: - IncludeUwp: 'true' - steps: - - uses: actions/checkout@v4 - - name: Register MSVC problem matcher - run: echo "::add-matcher::.github/problem-matchers/msvc.json" - - name: Build CPython - run: .\PCbuild\build.bat -e -d -p arm64 + uses: ./.github/workflows/reusable-windows.yml build_macos: name: 'macOS' - runs-on: macos-latest - timeout-minutes: 60 needs: check_source if: needs.check_source.outputs.run_tests == 'true' - env: - HOMEBREW_NO_ANALYTICS: 1 - HOMEBREW_NO_AUTO_UPDATE: 1 - HOMEBREW_NO_INSTALL_CLEANUP: 1 - PYTHONSTRICTEXTENSIONBUILD: 1 - steps: - - uses: actions/checkout@v4 - - name: Restore config.cache - uses: actions/cache@v3 - with: - path: config.cache - key: ${{ github.job }}-${{ runner.os }}-${{ needs.check_source.outputs.config_hash }} - - name: Install Homebrew dependencies - run: brew install pkg-config openssl@3.0 xz gdbm tcl-tk - - name: Configure CPython - run: | - GDBM_CFLAGS="-I$(brew --prefix gdbm)/include" \ - GDBM_LIBS="-L$(brew --prefix gdbm)/lib -lgdbm" \ - ./configure \ - --config-cache \ - --with-pydebug \ - --prefix=/opt/python-dev \ - --with-openssl="$(brew --prefix openssl@3.0)" - - name: Build CPython - run: make -j4 - - name: Display build info - run: make pythoninfo - - name: Tests - run: make buildbottest TESTOPTS="-j4 -uall,-cpu" + uses: ./.github/workflows/reusable-macos.yml + with: + config_hash: ${{ needs.check_source.outputs.config_hash }} build_ubuntu: name: 'Ubuntu' - runs-on: ubuntu-20.04 - timeout-minutes: 60 needs: check_source if: needs.check_source.outputs.run_tests == 'true' - env: - OPENSSL_VER: 3.0.11 - PYTHONSTRICTEXTENSIONBUILD: 1 - steps: - - uses: actions/checkout@v4 - - name: Register gcc problem matcher - run: echo "::add-matcher::.github/problem-matchers/gcc.json" - - name: Install dependencies - run: sudo ./.github/workflows/posix-deps-apt.sh - - name: Configure OpenSSL env vars - run: | - echo "MULTISSL_DIR=${GITHUB_WORKSPACE}/multissl" >> $GITHUB_ENV - echo "OPENSSL_DIR=${GITHUB_WORKSPACE}/multissl/openssl/${OPENSSL_VER}" >> $GITHUB_ENV - echo "LD_LIBRARY_PATH=${GITHUB_WORKSPACE}/multissl/openssl/${OPENSSL_VER}/lib" >> $GITHUB_ENV - - name: 'Restore OpenSSL build' - id: cache-openssl - uses: actions/cache@v3 - with: - path: ./multissl/openssl/${{ env.OPENSSL_VER }} - key: ${{ runner.os }}-multissl-openssl-${{ env.OPENSSL_VER }} - - name: Install OpenSSL - if: steps.cache-openssl.outputs.cache-hit != 'true' - run: python3 Tools/ssl/multissltests.py --steps=library --base-directory $MULTISSL_DIR --openssl $OPENSSL_VER --system Linux - - name: Add ccache to PATH - run: | - echo "PATH=/usr/lib/ccache:$PATH" >> $GITHUB_ENV - - name: Configure ccache action - uses: hendrikmuhs/ccache-action@v1.2 - - name: Setup directory envs for out-of-tree builds - run: | - echo "CPYTHON_RO_SRCDIR=$(realpath -m ${GITHUB_WORKSPACE}/../cpython-ro-srcdir)" >> $GITHUB_ENV - echo "CPYTHON_BUILDDIR=$(realpath -m ${GITHUB_WORKSPACE}/../cpython-builddir)" >> $GITHUB_ENV - - name: Create directories for read-only out-of-tree builds - run: mkdir -p $CPYTHON_RO_SRCDIR $CPYTHON_BUILDDIR - - name: Bind mount sources read-only - run: sudo mount --bind -o ro $GITHUB_WORKSPACE $CPYTHON_RO_SRCDIR - - name: Restore config.cache - uses: actions/cache@v3 - with: - path: ${{ env.CPYTHON_BUILDDIR }}/config.cache - key: ${{ github.job }}-${{ runner.os }}-${{ needs.check_source.outputs.config_hash }} - - name: Configure CPython out-of-tree - working-directory: ${{ env.CPYTHON_BUILDDIR }} - run: | + uses: ./.github/workflows/reusable-ubuntu.yml + with: + config_hash: ${{ needs.check_source.outputs.config_hash }} + options: | ../cpython-ro-srcdir/configure \ --config-cache \ --with-pydebug \ --with-openssl=$OPENSSL_DIR - - name: Build CPython out-of-tree - working-directory: ${{ env.CPYTHON_BUILDDIR }} - run: make -j4 - - name: Display build info - working-directory: ${{ env.CPYTHON_BUILDDIR }} - run: make pythoninfo - - name: Remount sources writable for tests - # some tests write to srcdir, lack of pyc files slows down testing - run: sudo mount $CPYTHON_RO_SRCDIR -oremount,rw - - name: Tests - working-directory: ${{ env.CPYTHON_BUILDDIR }} - run: xvfb-run make buildbottest TESTOPTS="-j4 -uall,-cpu" build_ubuntu_ssltests: name: 'Ubuntu SSL tests with OpenSSL' @@ -463,12 +336,10 @@ jobs: - check_source # Transitive dependency, needed to access `run_tests` value - check-docs - check_generated_files - - build_win32 - - build_win_amd64 - - build_win_arm64 - build_macos - build_ubuntu - build_ubuntu_ssltests + - build_windows - build_asan runs-on: ubuntu-latest @@ -480,8 +351,6 @@ jobs: allowed-failures: >- build_macos, build_ubuntu_ssltests, - build_win32, - build_win_arm64, allowed-skips: >- ${{ !fromJSON(needs.check_source.outputs.run-docs) @@ -494,12 +363,10 @@ jobs: needs.check_source.outputs.run_tests != 'true' && ' check_generated_files, - build_win32, - build_win_amd64, - build_win_arm64, build_macos, build_ubuntu, build_ubuntu_ssltests, + build_windows, build_asan, ' || '' diff --git a/.github/workflows/reusable-macos.yml b/.github/workflows/reusable-macos.yml new file mode 100644 index 00000000000000..3ed8303ac16d97 --- /dev/null +++ b/.github/workflows/reusable-macos.yml @@ -0,0 +1,46 @@ +on: + workflow_call: + inputs: + config_hash: + required: true + type: string + free-threaded: + required: false + type: boolean + default: false + +jobs: + build_macos: + name: 'build and test' + runs-on: macos-latest + timeout-minutes: 60 + env: + HOMEBREW_NO_ANALYTICS: 1 + HOMEBREW_NO_AUTO_UPDATE: 1 + HOMEBREW_NO_INSTALL_CLEANUP: 1 + PYTHONSTRICTEXTENSIONBUILD: 1 + steps: + - uses: actions/checkout@v4 + - name: Restore config.cache + uses: actions/cache@v3 + with: + path: config.cache + key: ${{ github.job }}-${{ runner.os }}-${{ inputs.config_hash }} + - name: Install Homebrew dependencies + run: brew install pkg-config openssl@3.0 xz gdbm tcl-tk + - name: Configure CPython + run: | + GDBM_CFLAGS="-I$(brew --prefix gdbm)/include" \ + GDBM_LIBS="-L$(brew --prefix gdbm)/lib -lgdbm" \ + ./configure \ + --config-cache \ + --with-pydebug \ + ${{ inputs.free-threaded && '--disable-gil' || '' }} \ + --prefix=/opt/python-dev \ + --with-openssl="$(brew --prefix openssl@3.0)" + - name: Build CPython + run: make -j4 + - name: Display build info + run: make pythoninfo + - name: Tests + run: make buildbottest TESTOPTS="-j4 -uall,-cpu" diff --git a/.github/workflows/reusable-ubuntu.yml b/.github/workflows/reusable-ubuntu.yml new file mode 100644 index 00000000000000..56268c8bfd3419 --- /dev/null +++ b/.github/workflows/reusable-ubuntu.yml @@ -0,0 +1,71 @@ +on: + workflow_call: + inputs: + config_hash: + required: true + type: string + options: + required: true + type: string + +jobs: + build_ubuntu_reusable: + name: 'build and test' + timeout-minutes: 60 + runs-on: ubuntu-20.04 + env: + OPENSSL_VER: 3.0.11 + PYTHONSTRICTEXTENSIONBUILD: 1 + steps: + - uses: actions/checkout@v4 + - name: Register gcc problem matcher + run: echo "::add-matcher::.github/problem-matchers/gcc.json" + - name: Install dependencies + run: sudo ./.github/workflows/posix-deps-apt.sh + - name: Configure OpenSSL env vars + run: | + echo "MULTISSL_DIR=${GITHUB_WORKSPACE}/multissl" >> $GITHUB_ENV + echo "OPENSSL_DIR=${GITHUB_WORKSPACE}/multissl/openssl/${OPENSSL_VER}" >> $GITHUB_ENV + echo "LD_LIBRARY_PATH=${GITHUB_WORKSPACE}/multissl/openssl/${OPENSSL_VER}/lib" >> $GITHUB_ENV + - name: 'Restore OpenSSL build' + id: cache-openssl + uses: actions/cache@v3 + with: + path: ./multissl/openssl/${{ env.OPENSSL_VER }} + key: ${{ runner.os }}-multissl-openssl-${{ env.OPENSSL_VER }} + - name: Install OpenSSL + if: steps.cache-openssl.outputs.cache-hit != 'true' + run: python3 Tools/ssl/multissltests.py --steps=library --base-directory $MULTISSL_DIR --openssl $OPENSSL_VER --system Linux + - name: Add ccache to PATH + run: | + echo "PATH=/usr/lib/ccache:$PATH" >> $GITHUB_ENV + - name: Configure ccache action + uses: hendrikmuhs/ccache-action@v1.2 + - name: Setup directory envs for out-of-tree builds + run: | + echo "CPYTHON_RO_SRCDIR=$(realpath -m ${GITHUB_WORKSPACE}/../cpython-ro-srcdir)" >> $GITHUB_ENV + echo "CPYTHON_BUILDDIR=$(realpath -m ${GITHUB_WORKSPACE}/../cpython-builddir)" >> $GITHUB_ENV + - name: Create directories for read-only out-of-tree builds + run: mkdir -p $CPYTHON_RO_SRCDIR $CPYTHON_BUILDDIR + - name: Bind mount sources read-only + run: sudo mount --bind -o ro $GITHUB_WORKSPACE $CPYTHON_RO_SRCDIR + - name: Restore config.cache + uses: actions/cache@v3 + with: + path: ${{ env.CPYTHON_BUILDDIR }}/config.cache + key: ${{ github.job }}-${{ runner.os }}-${{ inputs.config_hash }} + - name: Configure CPython out-of-tree + working-directory: ${{ env.CPYTHON_BUILDDIR }} + run: ${{ inputs.options }} + - name: Build CPython out-of-tree + working-directory: ${{ env.CPYTHON_BUILDDIR }} + run: make -j4 + - name: Display build info + working-directory: ${{ env.CPYTHON_BUILDDIR }} + run: make pythoninfo + - name: Remount sources writable for tests + # some tests write to srcdir, lack of pyc files slows down testing + run: sudo mount $CPYTHON_RO_SRCDIR -oremount,rw + - name: Tests + working-directory: ${{ env.CPYTHON_BUILDDIR }} + run: xvfb-run make buildbottest TESTOPTS="-j4 -uall,-cpu" diff --git a/.github/workflows/reusable-windows.yml b/.github/workflows/reusable-windows.yml new file mode 100644 index 00000000000000..98fb7cfa015587 --- /dev/null +++ b/.github/workflows/reusable-windows.yml @@ -0,0 +1,53 @@ +on: + workflow_call: + inputs: + free-threaded: + required: false + type: boolean + default: false + +jobs: + build_win32: + name: 'build and test (x86)' + runs-on: windows-latest + timeout-minutes: 60 + env: + IncludeUwp: 'true' + steps: + - uses: actions/checkout@v4 + - name: Build CPython + run: .\PCbuild\build.bat -e -d -p Win32 ${{ inputs.free-threaded && '--disable-gil' || '' }} + - name: Display build info + run: .\python.bat -m test.pythoninfo + - name: Tests + run: .\PCbuild\rt.bat -p Win32 -d -q -uall -u-cpu -rwW --slowest --timeout=1200 -j0 + + build_win_amd64: + name: 'build and test (x64)' + runs-on: windows-latest + timeout-minutes: 60 + env: + IncludeUwp: 'true' + steps: + - uses: actions/checkout@v4 + - name: Register MSVC problem matcher + run: echo "::add-matcher::.github/problem-matchers/msvc.json" + - name: Build CPython + run: .\PCbuild\build.bat -e -d -p x64 ${{ inputs.free-threaded && '--disable-gil' || '' }} + - name: Display build info + run: .\python.bat -m test.pythoninfo + - name: Tests + run: .\PCbuild\rt.bat -p x64 -d -q -uall -u-cpu -rwW --slowest --timeout=1200 -j0 + + build_win_arm64: + name: 'build (arm64)' + runs-on: windows-latest + timeout-minutes: 60 + env: + IncludeUwp: 'true' + steps: + - uses: actions/checkout@v4 + - name: Register MSVC problem matcher + run: echo "::add-matcher::.github/problem-matchers/msvc.json" + - name: Build CPython + run: .\PCbuild\build.bat -e -d -p arm64 ${{ inputs.free-threaded && '--disable-gil' || '' }} From 132a74922a786aec6c9dc35e6756736c4ebcb966 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 16 Nov 2023 13:07:53 +0100 Subject: [PATCH 600/632] [3.11] gh-102837: more tests for the math module (GH-111930)(GH-102523) (GH-112030) (GH-112041) [3.12] gh-102837: more tests for the math module (GH-111930)(GH-102523) (GH-112030) * gh-102837: improve test coverage for math module (GH-102523) (Only the test changes from GH-102523 are cherry-picked) - input checks for math_1(L989), math_1a(L1023), math_2(L1064,L1071), hypot(L2682), log(L2307), ldexp(L2168), ceil(L1165), floor(L1236,L1239) and dist(L2587,L2588,L2628). - improve fsum coverage for exceptional cases (L1433,L1438,L1451,L1497), ditto fmod(L2378) (all line numbers are wrt the main branch at 5e6661bce9) * gh-102837: more tests for the math module (GH-111930) Add tests to improve coverage: * fsum: L1369, L1379, L1383, L1412 * trunc: L2081 * log: L2267 * dist: L2577, L2579 * hypot: L2632 * (not cherry-picked for 3.11: sumprod) * pow: L2982 * prod: L3294, L3308, L3318-3330 // line numbers wrt to 9dc4fb8204 (cherry picked from commit c61de456db0186b65d479d41e84127832205d30d) --------- Co-authored-by: Sergey B Kirpichev (cherry picked from commit c6aea46a71d158f993cc723c14b4bf7982b73a2a) --- Lib/test/test_math.py | 80 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index bf0d0a56e6ac8b..7edcce9bbde23b 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -234,6 +234,10 @@ def __init__(self, value): def __index__(self): return self.value +class BadDescr: + def __get__(self, obj, objtype=None): + raise ValueError + class MathTests(unittest.TestCase): def ftest(self, name, got, expected, ulp_tol=5, abs_tol=0.0): @@ -323,6 +327,7 @@ def testAtan2(self): self.ftest('atan2(0, 1)', math.atan2(0, 1), 0) self.ftest('atan2(1, 1)', math.atan2(1, 1), math.pi/4) self.ftest('atan2(1, 0)', math.atan2(1, 0), math.pi/2) + self.ftest('atan2(1, -1)', math.atan2(1, -1), 3*math.pi/4) # math.atan2(0, x) self.ftest('atan2(0., -inf)', math.atan2(0., NINF), math.pi) @@ -416,16 +421,22 @@ def __ceil__(self): return 42 class TestNoCeil: pass + class TestBadCeil: + __ceil__ = BadDescr() self.assertEqual(math.ceil(TestCeil()), 42) self.assertEqual(math.ceil(FloatCeil()), 42) self.assertEqual(math.ceil(FloatLike(42.5)), 43) self.assertRaises(TypeError, math.ceil, TestNoCeil()) + self.assertRaises(ValueError, math.ceil, TestBadCeil()) t = TestNoCeil() t.__ceil__ = lambda *args: args self.assertRaises(TypeError, math.ceil, t) self.assertRaises(TypeError, math.ceil, t, 0) + self.assertEqual(math.ceil(FloatLike(+1.0)), +1.0) + self.assertEqual(math.ceil(FloatLike(-1.0)), -1.0) + @requires_IEEE_754 def testCopysign(self): self.assertEqual(math.copysign(1, 42), 1.0) @@ -566,16 +577,22 @@ def __floor__(self): return 42 class TestNoFloor: pass + class TestBadFloor: + __floor__ = BadDescr() self.assertEqual(math.floor(TestFloor()), 42) self.assertEqual(math.floor(FloatFloor()), 42) self.assertEqual(math.floor(FloatLike(41.9)), 41) self.assertRaises(TypeError, math.floor, TestNoFloor()) + self.assertRaises(ValueError, math.floor, TestBadFloor()) t = TestNoFloor() t.__floor__ = lambda *args: args self.assertRaises(TypeError, math.floor, t) self.assertRaises(TypeError, math.floor, t, 0) + self.assertEqual(math.floor(FloatLike(+1.0)), +1.0) + self.assertEqual(math.floor(FloatLike(-1.0)), -1.0) + def testFmod(self): self.assertRaises(TypeError, math.fmod) self.ftest('fmod(10, 1)', math.fmod(10, 1), 0.0) @@ -597,6 +614,7 @@ def testFmod(self): self.assertEqual(math.fmod(-3.0, NINF), -3.0) self.assertEqual(math.fmod(0.0, 3.0), 0.0) self.assertEqual(math.fmod(0.0, NINF), 0.0) + self.assertRaises(ValueError, math.fmod, INF, INF) def testFrexp(self): self.assertRaises(TypeError, math.frexp) @@ -666,6 +684,7 @@ def msum(iterable): ([], 0.0), ([0.0], 0.0), ([1e100, 1.0, -1e100, 1e-100, 1e50, -1.0, -1e50], 1e-100), + ([1e100, 1.0, -1e100, 1e-100, 1e50, -1, -1e50], 1e-100), ([2.0**53, -0.5, -2.0**-54], 2.0**53-1.0), ([2.0**53, 1.0, 2.0**-100], 2.0**53+2.0), ([2.0**53+10.0, 1.0, 2.0**-100], 2.0**53+12.0), @@ -713,6 +732,22 @@ def msum(iterable): s = msum(vals) self.assertEqual(msum(vals), math.fsum(vals)) + self.assertEqual(math.fsum([1.0, math.inf]), math.inf) + self.assertTrue(math.isnan(math.fsum([math.nan, 1.0]))) + self.assertEqual(math.fsum([1e100, FloatLike(1.0), -1e100, 1e-100, + 1e50, FloatLike(-1.0), -1e50]), 1e-100) + self.assertRaises(OverflowError, math.fsum, [1e+308, 1e+308]) + self.assertRaises(ValueError, math.fsum, [math.inf, -math.inf]) + self.assertRaises(TypeError, math.fsum, ['spam']) + self.assertRaises(TypeError, math.fsum, 1) + self.assertRaises(OverflowError, math.fsum, [10**1000]) + + def bad_iter(): + yield 1.0 + raise ZeroDivisionError + + self.assertRaises(ZeroDivisionError, math.fsum, bad_iter()) + def testGcd(self): gcd = math.gcd self.assertEqual(gcd(0, 0), 0) @@ -773,6 +808,8 @@ def testHypot(self): # Test allowable types (those with __float__) self.assertEqual(hypot(12.0, 5.0), 13.0) self.assertEqual(hypot(12, 5), 13) + self.assertEqual(hypot(1, -1), math.sqrt(2)) + self.assertEqual(hypot(1, FloatLike(-1.)), math.sqrt(2)) self.assertEqual(hypot(Decimal(12), Decimal(5)), 13) self.assertEqual(hypot(Fraction(12, 32), Fraction(5, 32)), Fraction(13, 32)) self.assertEqual(hypot(bool(1), bool(0), bool(1), bool(1)), math.sqrt(3)) @@ -830,6 +867,8 @@ def testHypot(self): scale = FLOAT_MIN / 2.0 ** exp self.assertEqual(math.hypot(4*scale, 3*scale), 5*scale) + self.assertRaises(TypeError, math.hypot, *([1.0]*18), 'spam') + @requires_IEEE_754 @unittest.skipIf(HAVE_DOUBLE_ROUNDING, "hypot() loses accuracy on machines with double rounding") @@ -922,6 +961,10 @@ def testDist(self): # Test allowable types (those with __float__) self.assertEqual(dist((14.0, 1.0), (2.0, -4.0)), 13.0) self.assertEqual(dist((14, 1), (2, -4)), 13) + self.assertEqual(dist((FloatLike(14.), 1), (2, -4)), 13) + self.assertEqual(dist((11, 1), (FloatLike(-1.), -4)), 13) + self.assertEqual(dist((14, FloatLike(-1.)), (2, -6)), 13) + self.assertEqual(dist((14, -1), (2, -6)), 13) self.assertEqual(dist((D(14), D(1)), (D(2), D(-4))), D(13)) self.assertEqual(dist((F(14, 32), F(1, 32)), (F(2, 32), F(-4, 32))), F(13, 32)) @@ -965,6 +1008,8 @@ class T(tuple): dist((1, 2, 3, 4), (5, 6, 7)) with self.assertRaises(ValueError): # Check dimension agree dist((1, 2, 3), (4, 5, 6, 7)) + with self.assertRaises(TypeError): + dist((1,)*17 + ("spam",), (1,)*18) with self.assertRaises(TypeError): # Rejects invalid types dist("abc", "xyz") int_too_big_for_float = 10 ** (sys.float_info.max_10_exp + 5) @@ -972,6 +1017,16 @@ class T(tuple): dist((1, int_too_big_for_float), (2, 3)) with self.assertRaises((ValueError, OverflowError)): dist((2, 3), (1, int_too_big_for_float)) + with self.assertRaises(TypeError): + dist((1,), 2) + with self.assertRaises(TypeError): + dist([1], 2) + + class BadFloat: + __float__ = BadDescr() + + with self.assertRaises(ValueError): + dist([1], [BadFloat()]) # Verify that the one dimensional case is equivalent to abs() for i in range(20): @@ -1110,6 +1165,7 @@ def test_lcm(self): def testLdexp(self): self.assertRaises(TypeError, math.ldexp) + self.assertRaises(TypeError, math.ldexp, 2.0, 1.1) self.ftest('ldexp(0,1)', math.ldexp(0,1), 0) self.ftest('ldexp(1,1)', math.ldexp(1,1), 2) self.ftest('ldexp(1,-1)', math.ldexp(1,-1), 0.5) @@ -1142,6 +1198,7 @@ def testLdexp(self): def testLog(self): self.assertRaises(TypeError, math.log) + self.assertRaises(TypeError, math.log, 1, 2, 3) self.ftest('log(1/e)', math.log(1/math.e), -1) self.ftest('log(1)', math.log(1), 0) self.ftest('log(e)', math.log(math.e), 1) @@ -1152,6 +1209,7 @@ def testLog(self): 2302.5850929940457) self.assertRaises(ValueError, math.log, -1.5) self.assertRaises(ValueError, math.log, -10**1000) + self.assertRaises(ValueError, math.log, 10, -10) self.assertRaises(ValueError, math.log, NINF) self.assertEqual(math.log(INF), INF) self.assertTrue(math.isnan(math.log(NAN))) @@ -1235,6 +1293,7 @@ def testPow(self): self.assertTrue(math.isnan(math.pow(2, NAN))) self.assertTrue(math.isnan(math.pow(0, NAN))) self.assertEqual(math.pow(1, NAN), 1) + self.assertRaises(OverflowError, math.pow, 1e+100, 1e+100) # pow(0., x) self.assertEqual(math.pow(0., INF), 0.) @@ -1591,6 +1650,8 @@ def __trunc__(self): return 23 class TestNoTrunc: pass + class TestBadTrunc: + __trunc__ = BadDescr() self.assertEqual(math.trunc(TestTrunc()), 23) self.assertEqual(math.trunc(FloatTrunc()), 23) @@ -1599,6 +1660,7 @@ class TestNoTrunc: self.assertRaises(TypeError, math.trunc, 1, 2) self.assertRaises(TypeError, math.trunc, FloatLike(23.5)) self.assertRaises(TypeError, math.trunc, TestNoTrunc()) + self.assertRaises(ValueError, math.trunc, TestBadTrunc()) def testIsfinite(self): self.assertTrue(math.isfinite(0.0)) @@ -1799,6 +1861,8 @@ def test_mtestfile(self): '\n '.join(failures)) def test_prod(self): + from fractions import Fraction as F + prod = math.prod self.assertEqual(prod([]), 1) self.assertEqual(prod([], start=5), 5) @@ -1810,6 +1874,14 @@ def test_prod(self): self.assertEqual(prod([1.0, 2.0, 3.0, 4.0, 5.0]), 120.0) self.assertEqual(prod([1, 2, 3, 4.0, 5.0]), 120.0) self.assertEqual(prod([1.0, 2.0, 3.0, 4, 5]), 120.0) + self.assertEqual(prod([1., F(3, 2)]), 1.5) + + # Error in multiplication + class BadMultiply: + def __rmul__(self, other): + raise RuntimeError + with self.assertRaises(RuntimeError): + prod([10., BadMultiply()]) # Test overflow in fast-path for integers self.assertEqual(prod([1, 1, 2**32, 1, 1]), 2**32) @@ -2109,6 +2181,14 @@ def __float__(self): # argument to a float. self.assertFalse(getattr(y, "converted", False)) + def test_input_exceptions(self): + self.assertRaises(TypeError, math.exp, "spam") + self.assertRaises(TypeError, math.erf, "spam") + self.assertRaises(TypeError, math.atan2, "spam", 1.0) + self.assertRaises(TypeError, math.atan2, 1.0, "spam") + self.assertRaises(TypeError, math.atan2, 1.0) + self.assertRaises(TypeError, math.atan2, 1.0, 2.0, 3.0) + # Custom assertions. def assertIsNaN(self, value): From b4f3a62383dfad182cd848acef4a530ecd4f9132 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 16 Nov 2023 13:34:21 +0100 Subject: [PATCH 601/632] [3.11] gh-110812: Isolating Extension Modules HOWTO: List GC-related gotchas (GH-111504) (GH-112147) gh-110812: Isolating Extension Modules HOWTO: List GC-related gotchas (GH-111504) (cherry picked from commit 985679f05d1b72965bfbed99d1499c22815375e4) Co-authored-by: Petr Viktorin Co-authored-by: Hugo van Kemenade Co-authored-by: Eric Snow --- Doc/howto/isolating-extensions.rst | 103 +++++++++++++++++++++++++++-- 1 file changed, 97 insertions(+), 6 deletions(-) diff --git a/Doc/howto/isolating-extensions.rst b/Doc/howto/isolating-extensions.rst index 732fd6f993b722..0d4caf3049ec72 100644 --- a/Doc/howto/isolating-extensions.rst +++ b/Doc/howto/isolating-extensions.rst @@ -337,12 +337,44 @@ That is, heap types should: - Define a traverse function using ``Py_tp_traverse``, which visits the type (e.g. using :c:expr:`Py_VISIT(Py_TYPE(self))`). -Please refer to the :ref:`the documentation ` of +Please refer to the the documentation of :c:macro:`Py_TPFLAGS_HAVE_GC` and :c:member:`~PyTypeObject.tp_traverse` for additional considerations. -If your traverse function delegates to the ``tp_traverse`` of its base class -(or another type), ensure that ``Py_TYPE(self)`` is visited only once. +The API for defining heap types grew organically, leaving it +somewhat awkward to use in its current state. +The following sections will guide you through common issues. + + +``tp_traverse`` in Python 3.8 and lower +....................................... + +The requirement to visit the type from ``tp_traverse`` was added in Python 3.9. +If you support Python 3.8 and lower, the traverse function must *not* +visit the type, so it must be more complicated:: + + static int my_traverse(PyObject *self, visitproc visit, void *arg) + { + if (Py_Version >= 0x03090000) { + Py_VISIT(Py_TYPE(self)); + } + return 0; + } + +Unfortunately, :c:data:`Py_Version` was only added in Python 3.11. +As a replacement, use: + +* :c:macro:`PY_VERSION_HEX`, if not using the stable ABI, or +* :py:data:`sys.version_info` (via :c:func:`PySys_GetObject` and + :c:func:`PyArg_ParseTuple`). + + +Delegating ``tp_traverse`` +.......................... + +If your traverse function delegates to the :c:member:`~PyTypeObject.tp_traverse` +of its base class (or another type), ensure that ``Py_TYPE(self)`` is visited +only once. Note that only heap type are expected to visit the type in ``tp_traverse``. For example, if your traverse function includes:: @@ -354,11 +386,70 @@ For example, if your traverse function includes:: if (base->tp_flags & Py_TPFLAGS_HEAPTYPE) { // a heap type's tp_traverse already visited Py_TYPE(self) } else { - Py_VISIT(Py_TYPE(self)); + if (Py_Version >= 0x03090000) { + Py_VISIT(Py_TYPE(self)); + } } -It is not necessary to handle the type's reference count in ``tp_new`` -and ``tp_clear``. +It is not necessary to handle the type's reference count in +:c:member:`~PyTypeObject.tp_new` and :c:member:`~PyTypeObject.tp_clear`. + + +Defining ``tp_dealloc`` +....................... + +If your type has a custom :c:member:`~PyTypeObject.tp_dealloc` function, +it needs to: + +- call :c:func:`PyObject_GC_UnTrack` before any fields are invalidated, and +- decrement the reference count of the type. + +To keep the type valid while ``tp_free`` is called, the type's refcount needs +to be decremented *after* the instance is deallocated. For example:: + + static void my_dealloc(PyObject *self) + { + PyObject_GC_UnTrack(self); + ... + PyTypeObject *type = Py_TYPE(self); + type->tp_free(self); + Py_DECREF(type); + } + +The default ``tp_dealloc`` function does this, so +if your type does *not* override +``tp_dealloc`` you don't need to add it. + + +Not overriding ``tp_free`` +.......................... + +The :c:member:`~PyTypeObject.tp_free` slot of a heap type must be set to +:c:func:`PyObject_GC_Del`. +This is the default; do not override it. + + +Avoiding ``PyObject_New`` +......................... + +GC-tracked objects need to be allocated using GC-aware functions. + +If you use use :c:func:`PyObject_New` or :c:func:`PyObject_NewVar`: + +- Get and call type's :c:member:`~PyTypeObject.tp_alloc` slot, if possible. + That is, replace ``TYPE *o = PyObject_New(TYPE, typeobj)`` with:: + + TYPE *o = typeobj->tp_alloc(typeobj, 0); + + Replace ``o = PyObject_NewVar(TYPE, typeobj, size)`` with the same, + but use size instead of the 0. + +- If the above is not possible (e.g. inside a custom ``tp_alloc``), + call :c:func:`PyObject_GC_New` or :c:func:`PyObject_GC_NewVar`:: + + TYPE *o = PyObject_GC_New(TYPE, typeobj); + + TYPE *o = PyObject_GC_NewVar(TYPE, typeobj, size); Module State Access from Classes From 533da5ed67249305e26e97aa0b8d32f8045cde26 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 16 Nov 2023 18:10:02 +0100 Subject: [PATCH 602/632] [3.11] gh-111811: Fix test_recursive_repr for WASI (GH-112130) (#112132) gh-111811: Fix test_recursive_repr for WASI (GH-112130) (cherry picked from commit 7218bac8c84115a8e9a18a4a8f3146235068facb) Co-authored-by: Kushal Das --- Lib/test/test_xml_etree.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py index c095dd0b59300c..57f5de34fdc108 100644 --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -2564,6 +2564,7 @@ def __eq__(self, o): e.extend([ET.Element('bar')]) self.assertRaises(ValueError, e.remove, X('baz')) + @support.infinite_recursion(25) def test_recursive_repr(self): # Issue #25455 e = ET.Element('foo') From 7e4b66b86f7bc43760b4e413bb468b7f87168f0c Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 17 Nov 2023 02:11:39 +0100 Subject: [PATCH 603/632] [3.11] gh-112165: Fix typo in `__main__.py` (GH-112183) (#112185) gh-112165: Fix typo in `__main__.py` (GH-112183) Change '[2]' to '[1]' to get second argument. (cherry picked from commit 8cd70eefc7f3363cfa0d43f34522c3072fa9e160) Co-authored-by: Terry Jan Reedy --- Doc/library/__main__.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/__main__.rst b/Doc/library/__main__.rst index 435310d525d754..280af06386addf 100644 --- a/Doc/library/__main__.rst +++ b/Doc/library/__main__.rst @@ -227,7 +227,7 @@ students:: import sys from .student import search_students - student_name = sys.argv[2] if len(sys.argv) >= 2 else '' + student_name = sys.argv[1] if len(sys.argv) >= 2 else '' print(f'Found student: {search_students(student_name)}') Note that ``from .student import search_students`` is an example of a relative From e7aa40a3414b1834fb7aa891d76a81398a0ee828 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 17 Nov 2023 19:14:20 +0100 Subject: [PATCH 604/632] [3.11] gh-112194: Convert more examples to doctests in `typing.py` (GH-112195) (#112209) gh-112194: Convert more examples to doctests in `typing.py` (GH-112195) (cherry picked from commit 949b2cc6eae6ef4f3312dfd4e2650a138446fe77) Co-authored-by: Nikita Sobolev Co-authored-by: Alex Waygood --- Lib/typing.py | 74 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/Lib/typing.py b/Lib/typing.py index 977e37d623bcd2..fa812f9bd2355a 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -211,8 +211,12 @@ def _should_unflatten_callable_args(typ, args): For example:: - assert collections.abc.Callable[[int, int], str].__args__ == (int, int, str) - assert collections.abc.Callable[ParamSpec, str].__args__ == (ParamSpec, str) + >>> import collections.abc + >>> P = ParamSpec('P') + >>> collections.abc.Callable[[int, int], str].__args__ == (int, int, str) + True + >>> collections.abc.Callable[P, str].__args__ == (P, str) + True As a result, if we need to reconstruct the Callable from its __args__, we need to unflatten it. @@ -250,7 +254,10 @@ def _collect_parameters(args): For example:: - assert _collect_parameters((T, Callable[P, T])) == (T, P) + >>> P = ParamSpec('P') + >>> T = TypeVar('T') + >>> _collect_parameters((T, Callable[P, T])) + (~T, ~P) """ parameters = [] for t in args: @@ -2417,14 +2424,15 @@ def get_origin(tp): Examples:: - assert get_origin(Literal[42]) is Literal - assert get_origin(int) is None - assert get_origin(ClassVar[int]) is ClassVar - assert get_origin(Generic) is Generic - assert get_origin(Generic[T]) is Generic - assert get_origin(Union[T, int]) is Union - assert get_origin(List[Tuple[T, T]][int]) is list - assert get_origin(P.args) is P + >>> P = ParamSpec('P') + >>> assert get_origin(Literal[42]) is Literal + >>> assert get_origin(int) is None + >>> assert get_origin(ClassVar[int]) is ClassVar + >>> assert get_origin(Generic) is Generic + >>> assert get_origin(Generic[T]) is Generic + >>> assert get_origin(Union[T, int]) is Union + >>> assert get_origin(List[Tuple[T, T]][int]) is list + >>> assert get_origin(P.args) is P """ if isinstance(tp, _AnnotatedAlias): return Annotated @@ -2445,11 +2453,12 @@ def get_args(tp): Examples:: - assert get_args(Dict[str, int]) == (str, int) - assert get_args(int) == () - assert get_args(Union[int, Union[T, int], str][int]) == (int, str) - assert get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int]) - assert get_args(Callable[[], T][int]) == ([], int) + >>> T = TypeVar('T') + >>> assert get_args(Dict[str, int]) == (str, int) + >>> assert get_args(int) == () + >>> assert get_args(Union[int, Union[T, int], str][int]) == (int, str) + >>> assert get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int]) + >>> assert get_args(Callable[[], T][int]) == ([], int) """ if isinstance(tp, _AnnotatedAlias): return (tp.__origin__,) + tp.__metadata__ @@ -2468,12 +2477,15 @@ def is_typeddict(tp): For example:: - class Film(TypedDict): - title: str - year: int - - is_typeddict(Film) # => True - is_typeddict(Union[list, str]) # => False + >>> from typing import TypedDict + >>> class Film(TypedDict): + ... title: str + ... year: int + ... + >>> is_typeddict(Film) + True + >>> is_typeddict(dict) + False """ return isinstance(tp, _TypedDictMeta) @@ -3022,15 +3034,15 @@ def TypedDict(typename, fields=None, /, *, total=True, **kwargs): Usage:: - class Point2D(TypedDict): - x: int - y: int - label: str - - a: Point2D = {'x': 1, 'y': 2, 'label': 'good'} # OK - b: Point2D = {'z': 3, 'label': 'bad'} # Fails type check - - assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first') + >>> class Point2D(TypedDict): + ... x: int + ... y: int + ... label: str + ... + >>> a: Point2D = {'x': 1, 'y': 2, 'label': 'good'} # OK + >>> b: Point2D = {'z': 3, 'label': 'bad'} # Fails type check + >>> Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first') + True The type info can be accessed via the Point2D.__annotations__ dict, and the Point2D.__required_keys__ and Point2D.__optional_keys__ frozensets. From 93fbcd644318b83619d069301851419b9238d5b6 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Sat, 18 Nov 2023 14:13:55 +0300 Subject: [PATCH 605/632] [3.11] gh-112155: Run `typing.py` doctests during tests (GH-112156) (#112231) --- Lib/test/test_typing.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 30056e49ae29d9..12f78b580effaa 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -8466,5 +8466,11 @@ def test_is_not_instance_of_iterable(self): self.assertNotIsInstance(type_to_test, collections.abc.Iterable) +def load_tests(loader, tests, pattern): + import doctest + tests.addTests(doctest.DocTestSuite(typing)) + return tests + + if __name__ == '__main__': main() From e19d75df7617026d4822fcd95de3d4d277d53525 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 19 Nov 2023 05:34:54 +0100 Subject: [PATCH 606/632] [3.11] gh-79871: IDLE - Fix and test debugger module (GH-11451) (#112257) 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%. --------- (cherry picked from commit adedcfa06b553242d8033f6d9bebbcb3bc0dbb4d) Co-authored-by: Anthony Shaw 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 0a7ee59f730a56..3c3d715e9894a8 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 d065e30b00cc4ba3db33c45563341a33588e7bb9 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 19 Nov 2023 07:59:56 +0100 Subject: [PATCH 607/632] [3.11] IDLE: Fix test_debugger bug and buildbot failures (GH-112258) (#112260) IDLE: Fix test_debugger bug and buildbot failures (GH-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. (cherry picked from commit 14fd86a59d0d91fe72641efeb14a59d99127dec3) Co-authored-by: Terry Jan Reedy --- 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 e9a97c3bf9a516607d11904e55f9004710b1493b Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 19 Nov 2023 11:02:49 +0100 Subject: [PATCH 608/632] [3.11] gh-110383: Fix documentation profile cumtime fix (GH-112221) (#112263) Co-authored-by: Alex Ptakhin 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 11b91be55b2d77caf64fb97831452f23f219e433 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sun, 19 Nov 2023 13:29:28 +0100 Subject: [PATCH 609/632] [3.11] gh-110383: Explained which error message is generated when there is an unhandled exception (GH-111574) (#112265) Co-authored-by: Unique-Usman <86585626+Unique-Usman@users.noreply.github.com> 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 f6e11eab491b63ded24607a28cf53be056bdb926 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 20 Nov 2023 00:25:40 +0100 Subject: [PATCH 610/632] [3.11] gh-73561: Omit interface scope from IPv6 when used as Host header (GH-93324) (#112273) gh-73561: Omit interface scope from IPv6 when used as Host header (GH-93324) Omit the `@interface_scope` from an IPv6 address when used as Host header by `http.client`. --------- (cherry picked from commit ce1096f974d3158a92e050f9226700775b8db398) [Google LLC] Co-authored-by: Michael <35783820+mib1185@users.noreply.github.com> --- 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 1c7f4639db5888..87bca4d76aba92 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 @@ -1161,7 +1168,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 @@ -1178,8 +1185,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 f6a9c820b54d31..f9da4927f682e9 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -285,6 +285,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 6c51c84b39f210668fbfacce9e49bff3e2fbe3a7 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Mon, 20 Nov 2023 12:04:38 +0300 Subject: [PATCH 611/632] [3.11] gh-112266: Remove `(if defined)` part from `__dict__` and `__weakref__` docstrings (GH-112268) (#112276) --- 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 be224feca872ed..cbf93a88af9234 100644 --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -40,8 +40,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 = ('', '', '', '') @@ -105,10 +105,10 @@ class C(builtins.object) | Data descriptors defined here: |\x20\x20 | __dict__ - | dictionary for instance variables (if defined) + | dictionary for instance variables |\x20\x20 | __weakref__ - | list of weak references to the object (if defined) + | list of weak references to the object FUNCTIONS doc_func() @@ -166,16 +166,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': } @@ -192,9 +192,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() @@ -826,10 +826,10 @@ class B(A) | Data descriptors inherited from A: |\x20\x20 | __dict__ - | dictionary for instance variables (if defined) + | dictionary for instance variables |\x20\x20 | __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()) @@ -858,9 +858,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 4bdf1881581fc3..8c2e725cac9d2e 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -2399,21 +2399,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 13975051b5ce3cee6de841716165a78389efb58f Mon Sep 17 00:00:00 2001 From: DPR Date: Tue, 21 Nov 2023 07:12:17 +0800 Subject: [PATCH 612/632] [3.11] gh-109538: Catch closed loop runtime error and issue warning (GH-111983) (#112141) Issue a ResourceWarning instead. Co-authored-by: Hugo van Kemenade (cherry picked from commit e0f512797596282bff63260f8102592aad37cdf1) --- Lib/asyncio/streams.py | 7 ++- Lib/test/test_asyncio/test_streams.py | 56 +++++++++++++++++++ ...-11-11-16-42-48.gh-issue-109538.cMG5ux.rst | 1 + 3 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-11-11-16-42-48.gh-issue-109538.cMG5ux.rst diff --git a/Lib/asyncio/streams.py b/Lib/asyncio/streams.py index 7c407067e05a16..23b6e4c32f7c68 100644 --- a/Lib/asyncio/streams.py +++ b/Lib/asyncio/streams.py @@ -404,8 +404,11 @@ async def start_tls(self, sslcontext, *, def __del__(self): if not self._transport.is_closing(): - self.close() - + if self._loop.is_closed(): + warnings.warn("loop is closed", ResourceWarning) + else: + self.close() + warnings.warn(f"unclosed {self!r}", ResourceWarning) class StreamReader: diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py index 86354306f1fff3..f5decbe53dacb3 100644 --- a/Lib/test/test_asyncio/test_streams.py +++ b/Lib/test/test_asyncio/test_streams.py @@ -1067,6 +1067,62 @@ def test_eof_feed_when_closing_writer(self): self.assertEqual(messages, []) + def test_unclosed_resource_warnings(self): + async def inner(httpd): + rd, wr = await asyncio.open_connection(*httpd.address) + + wr.write(b'GET / HTTP/1.0\r\n\r\n') + data = await rd.readline() + self.assertEqual(data, b'HTTP/1.0 200 OK\r\n') + data = await rd.read() + self.assertTrue(data.endswith(b'\r\n\r\nTest message')) + with self.assertWarns(ResourceWarning) as cm: + del wr + gc.collect() + self.assertEqual(len(cm.warnings), 1) + self.assertTrue(str(cm.warnings[0].message).startswith("unclosed None: port = socket_helper.find_unused_port() 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 new file mode 100644 index 00000000000000..d1ee4c054a3f19 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-11-16-42-48.gh-issue-109538.cMG5ux.rst @@ -0,0 +1 @@ +Issue warning message instead of having :class:`RuntimeError` be displayed when event loop has already been closed at :meth:`StreamWriter.__del__`. From 253ad788c65f33b9bf91aaa956801335e725ae84 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 21 Nov 2023 09:29:34 +0100 Subject: [PATCH 613/632] [3.11] gh-110950: add upstream Tk fixes to macOS installer. (GH-111041) (#112293) 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) (cherry picked from commit d67f947c72af8a215db2fd285e5de9b1e671fde1) Co-authored-by: Christopher Chavez 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 f3728e3259e514..69bbde2cf1336b 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'] result.extend([ 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 640454fd32c314fd92239c571ae620dabc5aacef Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 22 Nov 2023 06:41:51 +0100 Subject: [PATCH 614/632] [3.11] Fix docstring and var name of itertools recipe (GH-112113) (#112311) Fix docstring and var name of itertools recipe (GH-112113) `prepend()` works with arbitrary iterables, not only iterators. In fact, the example given uses a `list`, which is iterable, but not an iterator. (cherry picked from commit 6c47eaccfa2550c140a24bc6e520d968731d9689) Co-authored-by: Sebastian Rittau --- 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 2196e53502f5a4..24e2684496c075 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -758,10 +758,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 d3c12144d34ee2b477708071321afa94a8b40a6f Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 23 Nov 2023 22:21:49 +0100 Subject: [PATCH 615/632] [3.11] Remove bogus annotations from the descriptor howto guide (gh-112349) (gh-112350) --- 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 0a7263614670a1..ec7d99b440ee44 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 669b8fab31e0e8edf1f19464a7cfbc187e9dd664 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 24 Nov 2023 19:15:42 +0100 Subject: [PATCH 616/632] [3.11] gh-59254: mention in open() doc that line buffering is for writing (GH-112318) (#112379) gh-59254: mention in open() doc that line buffering is for writing (GH-112318) (cherry picked from commit fafae08cc7caa25f2bd6b29106b50ef76c3e296f) Co-authored-by: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> --- 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 c287f52156ba63..0e5f36f57daf17 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -1220,7 +1220,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 8b6068e5553f81ad5d1b0be2fe35e1eb53c763ef Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 25 Nov 2023 08:59:28 +0100 Subject: [PATCH 617/632] [3.11] gh-101100: Define `test.regrtest` module to fix references (GH-112381) (#112391) gh-101100: Define `test.regrtest` module to fix references (GH-112381) Define test.regrtest module to fix references (cherry picked from commit d525d01e2794e7e736527eaa7ee309ca1252f5bd) Co-authored-by: Hugo van Kemenade --- Doc/library/test.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Doc/library/test.rst b/Doc/library/test.rst index 7fc2a7c8466f66..bf1a2f361a2888 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 68db0ed903ee677e0f2684a5598ef2006680cf27 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 25 Nov 2023 09:00:44 +0100 Subject: [PATCH 618/632] [3.11] gh-101100: Define `_tkinter` module to fix references (GH-112382) (#112393) gh-101100: Define `_tkinter` module to fix references (GH-112382) Define _tkinter module to fix references (cherry picked from commit 6b961b8ceaba372b78d03feaceb4837bf7236694) Co-authored-by: Hugo van Kemenade --- Doc/library/tkinter.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Doc/library/tkinter.rst b/Doc/library/tkinter.rst index ee34f2659cf3d8..aa78a0fe93d53d 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 8c9f273760c60d1c59696b3e16ead4cbd03392f2 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 25 Nov 2023 18:39:16 +0100 Subject: [PATCH 619/632] [3.11] gh-94722: fix DocTest.__eq__ for case of no line number on one side (GH-112385) (#112401) gh-94722: fix DocTest.__eq__ for case of no line number on one side (GH-112385) (cherry picked from commit fbb9027a037ff1bfaf3f596df033ca45743ee980) Co-authored-by: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> --- 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 21abe475ef85f5..5012b24b98a339 100644 --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -569,9 +569,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 617529dded3b73..6c0da0b156a5d2 100644 --- a/Lib/test/test_doctest.py +++ b/Lib/test/test_doctest.py @@ -416,6 +416,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 d9254f9f04a12b1543a91f4fae2a3da17ccfa05b Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sat, 25 Nov 2023 21:26:05 +0200 Subject: [PATCH 620/632] [3.11] gh-101100 : Fix Sphinx warnings in `library/doctest.rst` (GH-112399) (#112404) 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 d00c4c88d57176..ef7e2b9a47a08c 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:: @@ -401,10 +401,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. @@ -956,7 +956,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. @@ -995,7 +996,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 @@ -1109,19 +1110,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 @@ -1142,14 +1148,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 @@ -1319,7 +1325,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: @@ -1532,7 +1539,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) @@ -1690,12 +1697,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 c5826698bd35f5..48d37c0539a50c 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -2332,6 +2332,8 @@ Loading and running tests test names. +.. _load_tests-protocol: + load_tests Protocol ################### diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index e59d6198032314..cb7558ce08e53c 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -52,7 +52,6 @@ Doc/library/datetime.rst Doc/library/dbm.rst Doc/library/decimal.rst Doc/library/dis.rst -Doc/library/doctest.rst Doc/library/email.charset.rst Doc/library/email.compat32-message.rst Doc/library/email.errors.rst From 49005e4d02f49a1512577cb6c3eba2b68dbdd467 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 25 Nov 2023 23:26:22 +0100 Subject: [PATCH 621/632] [3.11] gh-112331: Fix reference manual description of attribute lookup mechanics (gh-112375) (gh-112413) --- 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 9207fd3f3c02ee..7130aa2f6b7b7b 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -806,12 +806,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 fc657d0c609eff0ca8ff7de22741be6ce11a9bd2 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 26 Nov 2023 14:22:17 +0200 Subject: [PATCH 622/632] [3.11] gh-101100: Fix Sphinx reference warnings (GH-112416) (#112422) --- Doc/c-api/set.rst | 2 +- Doc/extending/newtypes.rst | 4 ++-- Doc/library/asyncio-stream.rst | 4 ++++ Doc/library/email.errors.rst | 9 +++++++++ Doc/library/gzip.rst | 2 +- Doc/library/importlib.resources.rst | 4 ++-- Doc/library/xml.rst | 2 +- Doc/tools/.nitignore | 7 ------- 8 files changed, 20 insertions(+), 14 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 8ea76aec8290db..dd561e611d18f9 100644 --- a/Doc/extending/newtypes.rst +++ b/Doc/extending/newtypes.rst @@ -321,7 +321,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. @@ -473,7 +473,7 @@ instance of your data type. Here is a simple example:: return result; } -:c:type:`Py_hash_t` is a signed integer type with a platform-varying width. +:c:type:`!Py_hash_t` is a signed integer type with a platform-varying width. Returning ``-1`` from :c:member:`~PyTypeObject.tp_hash` indicates an error, which is why you should be careful to avoid returning it when hash computation is successful, as seen above. diff --git a/Doc/library/asyncio-stream.rst b/Doc/library/asyncio-stream.rst index e9d466d95e547e..4f7da27fbd350e 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 fdb546236aeadc..f95df41d9f344c 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 437da6927ab2f4..d338ce07a8a4b6 100644 --- a/Doc/library/importlib.resources.rst +++ b/Doc/library/importlib.resources.rst @@ -41,7 +41,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:: Package +.. class:: Package Whenever a function accepts a ``Package`` argument, you can pass in either a :class:`module object ` or a module name @@ -58,7 +58,7 @@ for example, a package and its resources can be imported from a zip file using containers (think subdirectories). *package* is either a name or a module object which conforms to the - :data:`Package` requirements. + :class:`Package` requirements. .. versionadded:: 3.9 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 cb7558ce08e53c..8ae1ab62e69540 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 @@ -33,7 +31,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 @@ -55,7 +52,6 @@ Doc/library/dis.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 @@ -67,12 +63,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 @@ -126,7 +120,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 faa1ef4893f0abc0acd7ce3abd393d06ea69374d Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 27 Nov 2023 10:34:35 +0100 Subject: [PATCH 623/632] [3.11] Docs: fix typo in doc for sqlite3.Cursor.execute (GH-112442) (#112445) Docs: fix typo in doc for sqlite3.Cursor.execute (GH-112442) (cherry picked from commit fb79e1ed4a985a487a02bb8585cc1bd2933dfa7c) Co-authored-by: Tom Levy --- 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 3e9b7a270ad5ef..36e9092685434a 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -1314,7 +1314,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 62e430af9eca7eddafc1f73dd8f7af289ffdf097 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 27 Nov 2023 16:03:45 +0100 Subject: [PATCH 624/632] [3.11] GH-101100: Fix reference warnings for ``socket`` methods (GH-110114) (#112456) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> 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 b5c1edaf5ad8e0..9cfa0c4f25e2c8 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 @@ -322,7 +322,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. @@ -339,7 +339,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.) @@ -378,7 +378,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. @@ -671,7 +671,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 `. @@ -767,7 +767,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 @@ -832,7 +832,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 @@ -948,7 +948,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. @@ -1232,7 +1232,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. @@ -1246,7 +1246,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. @@ -1965,10 +1965,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 87cd09bd7d523b..fe7efe8ca01ccd 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 5979d77da5d6b6..b9d0ab08e6b724 100644 --- a/Doc/whatsnew/2.7.rst +++ b/Doc/whatsnew/2.7.rst @@ -2382,8 +2382,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 6d9b1819b84a0285ed706d11b1f7060ddf141355 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 27 Nov 2023 19:41:05 +0200 Subject: [PATCH 625/632] [3.11] gh-84443: SSLSocket.recv_into() now support buffer protocol with itemsize != 1 (GH-20310) (GH-112459) It is also no longer use __len__(). (cherry picked from commit 812360fddda86d7aff5823f529ab720f57ddc411) Co-authored-by: Zackery Spytz --- 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 48d229f810af28..c0350a81b2f0eb 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -1299,10 +1299,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 b353f3265cbb9e..1f881038c329b5 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -9,6 +9,7 @@ from test.support import socket_helper from test.support import threading_helper from test.support import warnings_helper +import array import re import socket import select @@ -3765,6 +3766,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 581b24415525f10ca178d5537e9cbd7558ce938a Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 27 Nov 2023 20:11:09 +0200 Subject: [PATCH 626/632] [3.11] gh-112438: Fix support of format units with the "e" prefix in nested tuples in PyArg_Parse (gh-112439) (GH-112461) (cherry picked from commit 4eea1e82369fbf7a795d1956e7a8212a1b58009f) --- Lib/test/test_capi/test_getargs.py | 27 +++++++++++++++++++ ...-11-27-09-44-16.gh-issue-112438.GdNZiI.rst | 2 ++ Python/getargs.c | 2 +- 3 files changed, 30 insertions(+), 1 deletion(-) 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 27675533d1bb4b..1ce1ec3b41c954 100644 --- a/Lib/test/test_capi/test_getargs.py +++ b/Lib/test/test_capi/test_getargs.py @@ -1322,6 +1322,33 @@ def test_positional_only(self): with self.assertRaisesRegex(SystemError, 'Empty keyword'): parse((1,), {}, 'O|OO', ['', 'a', '']) + def test_nested_tuple(self): + parse = _testcapi.parse_tuple_and_keywords + + parse(((1, 2, 3),), {}, '(OOO)', ['a']) + parse((1, (2, 3), 4), {}, 'O(OO)O', ['a', 'b', 'c']) + 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']) + class Test_testcapi(unittest.TestCase): locals().update((name, getattr(_testcapi, name)) 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/Python/getargs.c b/Python/getargs.c index 3105bd556c1bf1..e18d7719929161 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -522,7 +522,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 054d18e883c23d7bca71714c0573c7f32f3a760b Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 27 Nov 2023 20:46:34 +0200 Subject: [PATCH 627/632] [3.11] bpo-41422: Visit the Pickler's and Unpickler's memo in tp_traverse (GH-21664) (GH-112465) (cherry picked from commit 967f2a3052c2d22e31564b428a9aa8cc63dc2a9f) Co-authored-by: kale-smoothie <34165060+kale-smoothie@users.noreply.github.com> --- .../2020-07-28-20-48-05.bpo-41422.iMwnMu.rst | 2 ++ Modules/_pickle.c | 14 ++++++++++++++ 2 files changed, 16 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 840877e2db643b..f0cb302184e00b 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -4691,6 +4691,13 @@ 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; } @@ -7215,6 +7222,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 43b081bfc49173405576b8eba09a6ca86aac641b Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 27 Nov 2023 19:56:27 +0100 Subject: [PATCH 628/632] [3.11] gh-112388: Fix an error that was causing the parser to try to overwrite tokenizer errors (GH-112410) (#112467) gh-112388: Fix an error that was causing the parser to try to overwrite tokenizer errors (GH-112410) (cherry picked from commit 2c8b19174274c183eb652932871f60570123fe99) Signed-off-by: Pablo Galindo Co-authored-by: Pablo Galindo Salgado --- 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 07049087cdacc0..bbd22eccb3e5b1 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -2152,6 +2152,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 005356110a4340..29d5a6e179f307 100644 --- a/Parser/pegen_errors.c +++ b/Parser/pegen_errors.c @@ -212,6 +212,10 @@ _PyPegen_tokenize_full_source_to_check_for_errors(Parser *p) { void * _PyPegen_raise_error(Parser *p, PyObject *errtype, 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 390a5b81a97a36d5166a62e7850fadfe9eba3cc3 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 27 Nov 2023 20:05:20 +0100 Subject: [PATCH 629/632] [3.11] gh-112387: Fix error positions for decoded strings with backwards tokenize errors (GH-112409) (#112469) gh-112387: Fix error positions for decoded strings with backwards tokenize errors (GH-112409) (cherry picked from commit 45d648597b1146431bf3d91041e60d7f040e70bf) Signed-off-by: Pablo Galindo Co-authored-by: Pablo Galindo Salgado --- 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 bbd22eccb3e5b1..88d117f641816d 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -2143,6 +2143,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", "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 29d5a6e179f307..fb9fa2909717f9 100644 --- a/Parser/pegen_errors.c +++ b/Parser/pegen_errors.c @@ -270,6 +270,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 c3e5d0d936acb988e547fef2f188fce379694d8c Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 27 Nov 2023 21:14:30 +0100 Subject: [PATCH 630/632] [3.11] gh-68166: Tkinter: Add tests and examples for element_create() (GH-111453) (GH-111858) * Remove mention of "vsapi" element type from the documentation. * Add tests for element_create() and other ttk.Style methods. * Add examples for element_create() in the documentation. (cherry picked from commit 005d1e8fc81539c60c6b21ebba34de3edd5bb232) Co-authored-by: Serhiy Storchaka --- Doc/library/tkinter.ttk.rst | 18 +- Lib/tkinter/test/test_ttk/test_style.py | 184 +++++++++++++++++- ...3-10-27-12-46-56.gh-issue-68166.0EbWW4.rst | 4 + 3 files changed, 203 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-10-27-12-46-56.gh-issue-68166.0EbWW4.rst diff --git a/Doc/library/tkinter.ttk.rst b/Doc/library/tkinter.ttk.rst index dc31a1a4c1850a..5fab1454944989 100644 --- a/Doc/library/tkinter.ttk.rst +++ b/Doc/library/tkinter.ttk.rst @@ -1391,8 +1391,7 @@ 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", "from" or "vsapi". The latter is only - available in Tk 8.6a for Windows XP and Vista and is not described here. + expected to be either "image" or "from". 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 @@ -1418,6 +1417,16 @@ option. If you don't know the class name of a widget, use the method Specifies a minimum width for the element. If less than zero, the base image's width is used as a default. + Example:: + + img1 = tkinter.PhotoImage(master=root, file='button.png') + img1 = tkinter.PhotoImage(master=root, file='button-pressed.png') + img1 = tkinter.PhotoImage(master=root, file='button-active.png') + style = ttk.Style(root) + style.element_create('Button.button', 'image', + img1, ('pressed', img2), ('active', img3), + border=(2, 4), sticky='we') + If "from" is used as the value of *etype*, :meth:`element_create` will clone an existing element. *args* is expected to contain a themename, from which @@ -1425,6 +1434,11 @@ option. If you don't know the class name of a widget, use the method If this element to clone from is not specified, an empty element will be used. *kw* is discarded. + Example:: + + style = ttk.Style(root) + style.element_create('plain.background', 'from', 'default') + .. method:: element_names() diff --git a/Lib/tkinter/test/test_ttk/test_style.py b/Lib/tkinter/test/test_ttk/test_style.py index f94adc41f4df8b..edef294a96a7ae 100644 --- a/Lib/tkinter/test/test_ttk/test_style.py +++ b/Lib/tkinter/test/test_ttk/test_style.py @@ -2,6 +2,7 @@ import sys import tkinter from tkinter import ttk +from tkinter import TclError from test import support from test.support import requires from tkinter.test.support import AbstractTkTest, get_tk_patchlevel @@ -122,7 +123,6 @@ def test_theme_use(self): self.style.theme_use(curr_theme) - def test_configure_custom_copy(self): style = self.style @@ -176,6 +176,188 @@ def test_map_custom_copy(self): for key, value in default.items(): self.assertEqual(style.map(newname, key), value) + def test_element_options(self): + style = self.style + element_names = style.element_names() + self.assertNotIsInstance(element_names, str) + for name in element_names: + self.assertIsInstance(name, str) + element_options = style.element_options(name) + self.assertNotIsInstance(element_options, str) + for optname in element_options: + self.assertIsInstance(optname, str) + + def test_element_create_errors(self): + style = self.style + with self.assertRaises(TypeError): + style.element_create('plain.newelem') + with self.assertRaisesRegex(TclError, 'No such element type spam'): + style.element_create('plain.newelem', 'spam') + + def test_element_create_from(self): + style = self.style + style.element_create('plain.background', 'from', 'default') + self.assertIn('plain.background', style.element_names()) + style.element_create('plain.arrow', 'from', 'default', 'rightarrow') + self.assertIn('plain.arrow', style.element_names()) + + def test_element_create_from_errors(self): + style = self.style + with self.assertRaises(IndexError): + style.element_create('plain.newelem', 'from') + with self.assertRaisesRegex(TclError, 'theme "spam" doesn\'t exist'): + style.element_create('plain.newelem', 'from', 'spam') + + def test_element_create_image(self): + style = self.style + image = tkinter.PhotoImage(master=self.root, width=12, height=10) + style.element_create('block', 'image', image) + self.assertIn('block', style.element_names()) + + style.layout('TestLabel1', [('block', {'sticky': 'news'})]) + a = ttk.Label(self.root, style='TestLabel1') + a.pack(expand=True, fill='both') + self.assertEqual(a.winfo_reqwidth(), 12) + self.assertEqual(a.winfo_reqheight(), 10) + + imgfile = support.findfile('python.xbm', subdir='imghdrdata') + img1 = tkinter.BitmapImage(master=self.root, file=imgfile, + foreground='yellow', background='blue') + img2 = tkinter.BitmapImage(master=self.root, file=imgfile, + foreground='blue', background='yellow') + img3 = tkinter.BitmapImage(master=self.root, file=imgfile, + foreground='white', background='black') + style.element_create('Button.button', 'image', + img1, ('pressed', img2), ('active', img3), + border=(2, 4), sticky='we') + self.assertIn('Button.button', style.element_names()) + + style.layout('Button', [('Button.button', {'sticky': 'news'})]) + b = ttk.Button(self.root, style='Button') + b.pack(expand=True, fill='both') + self.assertEqual(b.winfo_reqwidth(), 16) + self.assertEqual(b.winfo_reqheight(), 16) + + def test_element_create_image_errors(self): + style = self.style + image = tkinter.PhotoImage(master=self.root, width=10, height=10) + with self.assertRaises(IndexError): + style.element_create('block2', 'image') + with self.assertRaises(TypeError): + style.element_create('block2', 'image', image, 1) + with self.assertRaises(ValueError): + style.element_create('block2', 'image', image, ()) + with self.assertRaisesRegex(TclError, 'Invalid state name'): + style.element_create('block2', 'image', image, ('spam', image)) + with self.assertRaisesRegex(TclError, 'Invalid state name'): + style.element_create('block2', 'image', image, (1, image)) + with self.assertRaises(TypeError): + style.element_create('block2', 'image', image, ('pressed', 1, image)) + with self.assertRaises(TypeError): + style.element_create('block2', 'image', image, (1, 'selected', image)) + with self.assertRaisesRegex(TclError, 'bad option'): + style.element_create('block2', 'image', image, spam=1) + + def test_theme_create(self): + style = self.style + curr_theme = style.theme_use() + curr_layout = style.layout('TLabel') + style.theme_create('testtheme1') + self.assertIn('testtheme1', style.theme_names()) + + style.theme_create('testtheme2', settings={ + 'elem' : {'element create': ['from', 'default'],}, + 'TLabel' : { + 'configure': {'padding': 10}, + 'layout': [('elem', {'sticky': 'we'})], + }, + }) + self.assertIn('testtheme2', style.theme_names()) + + style.theme_create('testtheme3', 'testtheme2') + self.assertIn('testtheme3', style.theme_names()) + + style.theme_use('testtheme1') + self.assertEqual(style.element_names(), ()) + self.assertEqual(style.layout('TLabel'), curr_layout) + + style.theme_use('testtheme2') + self.assertEqual(style.element_names(), ('elem',)) + self.assertEqual(style.lookup('TLabel', 'padding'), '10') + self.assertEqual(style.layout('TLabel'), [('elem', {'sticky': 'we'})]) + + style.theme_use('testtheme3') + self.assertEqual(style.element_names(), ()) + self.assertEqual(style.lookup('TLabel', 'padding'), '') + self.assertEqual(style.layout('TLabel'), [('elem', {'sticky': 'we'})]) + + style.theme_use(curr_theme) + + def test_theme_create_image(self): + style = self.style + curr_theme = style.theme_use() + image = tkinter.PhotoImage(master=self.root, width=10, height=10) + new_theme = 'testtheme4' + style.theme_create(new_theme, settings={ + 'block' : { + 'element create': ['image', image, {'width': 120, 'height': 100}], + }, + 'TestWidget.block2' : { + 'element create': ['image', image], + }, + 'TestWidget' : { + 'configure': { + 'anchor': 'left', + 'padding': (3, 0, 0, 2), + 'foreground': 'yellow', + }, + 'map': { + 'foreground': [ + ('pressed', 'red'), + ('active', 'disabled', 'blue'), + ], + }, + 'layout': [ + ('TestWidget.block', {'sticky': 'we', 'side': 'left'}), + ('TestWidget.border', { + 'sticky': 'nsw', + 'border': 1, + 'children': [ + ('TestWidget.block2', {'sticky': 'nswe'}) + ] + }) + ], + }, + }) + + style.theme_use(new_theme) + self.assertIn('block', style.element_names()) + self.assertEqual(style.lookup('TestWidget', 'anchor'), 'left') + self.assertEqual(style.lookup('TestWidget', 'padding'), '3 0 0 2') + self.assertEqual(style.lookup('TestWidget', 'foreground'), 'yellow') + self.assertEqual(style.lookup('TestWidget', 'foreground', + ['active']), 'yellow') + self.assertEqual(style.lookup('TestWidget', 'foreground', + ['active', 'pressed']), 'red') + self.assertEqual(style.lookup('TestWidget', 'foreground', + ['active', 'disabled']), 'blue') + self.assertEqual(style.layout('TestWidget'), + [ + ('TestWidget.block', {'side': 'left', 'sticky': 'we'}), + ('TestWidget.border', { + 'sticky': 'nsw', + 'border': '1', + 'children': [('TestWidget.block2', {'sticky': 'nswe'})] + }) + ]) + + b = ttk.Label(self.root, style='TestWidget') + b.pack(expand=True, fill='both') + self.assertEqual(b.winfo_reqwidth(), 134) + self.assertEqual(b.winfo_reqheight(), 100) + + style.theme_use(curr_theme) + if __name__ == "__main__": unittest.main() 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 new file mode 100644 index 00000000000000..757a7004cc1dc0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-10-27-12-46-56.gh-issue-68166.0EbWW4.rst @@ -0,0 +1,4 @@ +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. From 03b522d3ef8cfc2735134b486bb2581237907b57 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Mon, 27 Nov 2023 15:54:21 -0800 Subject: [PATCH 631/632] [3.11] Backport PR #112477: correct socket AF_PACKET docs (#112478) Backport PR #112477: correct socket AF_PACKET docs Network byte order is not involved in the `int` on the Python side. That happens under the hood. Correctly use the term addresses instead of packets. --- Doc/library/socket.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index 9cfa0c4f25e2c8..b9f47ae2e6d3f4 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -185,12 +185,11 @@ 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. - - *proto* - An in network-byte-order integer specifying the Ethernet - protocol number. + - *proto* - An integer specifying the Ethernet protocol number. - *pkttype* - Optional integer specifying the packet type: - ``PACKET_HOST`` (the default) - Packet addressed to the local host. From b85070ceaf99acf1b8707764fe5214506a8e7389 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 28 Nov 2023 01:21:18 +0100 Subject: [PATCH 632/632] [3.11] Docs: fix markup for `importlib.machinery.NamespaceLoader` (GH-112479) (#112482) Docs: fix markup for `importlib.machinery.NamespaceLoader` (GH-112479) (cherry picked from commit 2e632fa07d13a58be62f59be4e656ad58b378f9b) Co-authored-by: Alex Waygood --- 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 386a47b05f93da..5978945fcd20f4 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -1106,7 +1106,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