From a89e6713c4de99d4be5a1304b134e57a24ab10ac Mon Sep 17 00:00:00 2001 From: Ruben Vorderman Date: Sat, 4 Feb 2023 21:07:30 +0100 Subject: [PATCH 001/247] gh-101322: Ensure test_zlib.ZlibDecompressorTest runs, fix errors in ZlibDecompressor (#101323) * Ensure test_zlib.ZlibDecompressorTest actually runs, fix errors in ZlibDecompressor. --- Lib/test/test_zlib.py | 9 +++++++-- .../2023-01-26-06-44-35.gh-issue-101323.h8Hk11.rst | 2 ++ Modules/zlibmodule.c | 1 + 3 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-01-26-06-44-35.gh-issue-101323.h8Hk11.rst diff --git a/Lib/test/test_zlib.py b/Lib/test/test_zlib.py index ae54f6c46891c6..3dac70eb12852c 100644 --- a/Lib/test/test_zlib.py +++ b/Lib/test/test_zlib.py @@ -944,13 +944,18 @@ def choose_lines(source, number, seed=None, generator=random): """ -class ZlibDecompressorTest(): +class ZlibDecompressorTest(unittest.TestCase): # Test adopted from test_bz2.py TEXT = HAMLET_SCENE DATA = zlib.compress(HAMLET_SCENE) BAD_DATA = b"Not a valid deflate block" + BIG_TEXT = DATA * ((128 * 1024 // len(DATA)) + 1) + BIG_DATA = zlib.compress(BIG_TEXT) + def test_Constructor(self): - self.assertRaises(TypeError, zlib._ZlibDecompressor, 42) + self.assertRaises(TypeError, zlib._ZlibDecompressor, "ASDA") + self.assertRaises(TypeError, zlib._ZlibDecompressor, -15, "notbytes") + self.assertRaises(TypeError, zlib._ZlibDecompressor, -15, b"bytes", 5) def testDecompress(self): zlibd = zlib._ZlibDecompressor() diff --git a/Misc/NEWS.d/next/Library/2023-01-26-06-44-35.gh-issue-101323.h8Hk11.rst b/Misc/NEWS.d/next/Library/2023-01-26-06-44-35.gh-issue-101323.h8Hk11.rst new file mode 100644 index 00000000000000..f8419e130dbcd7 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-26-06-44-35.gh-issue-101323.h8Hk11.rst @@ -0,0 +1,2 @@ +Fix a bug where errors where not thrown by zlib._ZlibDecompressor if +encountered during decompressing. diff --git a/Modules/zlibmodule.c b/Modules/zlibmodule.c index 1cdfd01320288b..e2f7dbaca87a9f 100644 --- a/Modules/zlibmodule.c +++ b/Modules/zlibmodule.c @@ -1519,6 +1519,7 @@ decompress_buf(ZlibDecompressor *self, Py_ssize_t max_length) } } else if (err != Z_OK && err != Z_BUF_ERROR) { zlib_error(state, self->zst, err, "while decompressing data"); + goto error; } self->avail_in_real += self->zst.avail_in; From 5a2b984568f72f0d7ff7c7b4ee8ce31af9fd1b7e Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sat, 4 Feb 2023 17:54:44 -0600 Subject: [PATCH 002/247] GH-100485: Create an alternative code path when an accurate fma() implementation is not available (#101567) --- Lib/test/test_math.py | 5 +++++ Modules/mathmodule.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 2c84e55ab45c57..8d849045b2d11f 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -1450,6 +1450,11 @@ def Trial(dotfunc, c, n): n = 20 # Length of vectors c = 1e30 # Target condition number + # If the following test fails, it means that the C math library + # implementation of fma() is not compliant with the C99 standard + # and is inaccurate. To solve this problem, make a new build + # with the symbol UNRELIABLE_FMA defined. That will enable a + # slower but accurate code path that avoids the fma() call. relative_err = median(Trial(math.sumprod, c, n) for i in range(times)) self.assertLess(relative_err, 1e-16) diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index e6cdb3bae1ecff..654336d6d9f4bc 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2851,6 +2851,8 @@ dl_sum(double a, double b) return (DoubleLength) {x, y}; } +#ifndef UNRELIABLE_FMA + static DoubleLength dl_mul(double x, double y) { @@ -2860,6 +2862,47 @@ dl_mul(double x, double y) return (DoubleLength) {z, zz}; } +#else + +/* + The default implementation of dl_mul() depends on the C math library + having an accurate fma() function as required by § 7.12.13.1 of the + C99 standard. + + The UNRELIABLE_FMA option is provided as a slower but accurate + alternative for builds where the fma() function is found wanting. + The speed penalty may be modest (17% slower on an Apple M1 Max), + so don't hesitate to enable this build option. + + The algorithms are from the T. J. Dekker paper: + A Floating-Point Technique for Extending the Available Precision + https://csclub.uwaterloo.ca/~pbarfuss/dekker1971.pdf +*/ + +static DoubleLength +dl_split(double x) { + // Dekker (5.5) and (5.6). + double t = x * 134217729.0; // Veltkamp constant = 2.0 ** 27 + 1 + double hi = t - (t - x); + double lo = x - hi; + return (DoubleLength) {hi, lo}; +} + +static DoubleLength +dl_mul(double x, double y) +{ + // Dekker (5.12) and mul12() + DoubleLength xx = dl_split(x); + DoubleLength yy = dl_split(y); + double p = xx.hi * yy.hi; + double q = xx.hi * yy.lo + xx.lo * yy.hi; + double z = p + q; + double zz = p - z + q + xx.lo * yy.lo; + return (DoubleLength) {z, zz}; +} + +#endif + typedef struct { double hi; double lo; double tiny; } TripleLength; static const TripleLength tl_zero = {0.0, 0.0, 0.0}; From ddd619cffa457776a22f224b7111bd39de289d66 Mon Sep 17 00:00:00 2001 From: Alexander Belopolsky Date: Sun, 5 Feb 2023 11:14:15 +0400 Subject: [PATCH 003/247] Fix detection of presence of time.tzset (gh-101539) (#101540) Resolves gh-101539 Related to gh-31898 --- Lib/test/datetimetester.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 6a1df174a1b972..570f803918c1ef 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -6173,7 +6173,7 @@ def test_gaps(self): self.assertEqual(ldt.fold, 0) @unittest.skipUnless( - hasattr(time, "tzset"), "time module has no attribute tzset" + hasattr(_time, "tzset"), "time module has no attribute tzset" ) def test_system_transitions(self): if ('Riyadh8' in self.zonename or From 6e4a521c2ab84c082ad71e540b045699f0dbbc11 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Sun, 5 Feb 2023 01:45:07 -0800 Subject: [PATCH 004/247] Add missing preposition in argparse docs (#101548) --- Doc/library/argparse.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst index 475cac70291e9a..dbaa5d0d9b995b 100644 --- a/Doc/library/argparse.rst +++ b/Doc/library/argparse.rst @@ -31,7 +31,7 @@ Core Functionality The :mod:`argparse` module's support for command-line interfaces is built around an instance of :class:`argparse.ArgumentParser`. It is a container for -argument specifications and has options that apply the parser as whole:: +argument specifications and has options that apply to the parser as whole:: parser = argparse.ArgumentParser( prog = 'ProgramName', From 9b60ee976a6b66fe96c2d39051612999c26561e5 Mon Sep 17 00:00:00 2001 From: busywhitespace Date: Sun, 5 Feb 2023 10:55:36 +0100 Subject: [PATCH 005/247] gh-101221: Add options in the documentation of timeit command (#101222) --- 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 5437704cec337b..32ab565aba0c08 100644 --- a/Doc/library/timeit.rst +++ b/Doc/library/timeit.rst @@ -206,7 +206,7 @@ Command-Line Interface When called as a program from the command line, the following form is used:: - python -m timeit [-n N] [-r N] [-u U] [-s S] [-h] [statement ...] + python -m timeit [-n N] [-r N] [-u U] [-s S] [-p] [-v] [-h] [statement ...] Where the following options are understood: From 39017e04b55d4c110787551dc9a0cb753f27d700 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sun, 5 Feb 2023 10:02:53 +0000 Subject: [PATCH 006/247] gh-101266: Fix __sizeof__ for subclasses of int (#101394) Fix the behaviour of the `__sizeof__` method (and hence the results returned by `sys.getsizeof`) for subclasses of `int`. Previously, `int` subclasses gave identical results to the `int` base class, ignoring the presence of the instance dictionary. * Issue: gh-101266 --- Lib/test/test_long.py | 39 +++++++++++++++++++ ...-01-28-13-11-52.gh-issue-101266.AxV3OF.rst | 1 + Objects/boolobject.c | 6 ++- Objects/longobject.c | 11 ++---- 4 files changed, 48 insertions(+), 9 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-28-13-11-52.gh-issue-101266.AxV3OF.rst diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py index 569ab15820e302..d299c34cec076d 100644 --- a/Lib/test/test_long.py +++ b/Lib/test/test_long.py @@ -1601,5 +1601,44 @@ def test_square(self): self.assertEqual(n**2, (1 << (2 * bitlen)) - (1 << (bitlen + 1)) + 1) + def test___sizeof__(self): + self.assertEqual(int.__itemsize__, sys.int_info.sizeof_digit) + + # Pairs (test_value, number of allocated digits) + test_values = [ + # We always allocate space for at least one digit, even for + # a value of zero; sys.getsizeof should reflect that. + (0, 1), + (1, 1), + (-1, 1), + (BASE-1, 1), + (1-BASE, 1), + (BASE, 2), + (-BASE, 2), + (BASE*BASE - 1, 2), + (BASE*BASE, 3), + ] + + for value, ndigits in test_values: + with self.subTest(value): + self.assertEqual( + value.__sizeof__(), + int.__basicsize__ + int.__itemsize__ * ndigits + ) + + # Same test for a subclass of int. + class MyInt(int): + pass + + self.assertEqual(MyInt.__itemsize__, sys.int_info.sizeof_digit) + + for value, ndigits in test_values: + with self.subTest(value): + self.assertEqual( + MyInt(value).__sizeof__(), + MyInt.__basicsize__ + MyInt.__itemsize__ * ndigits + ) + + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-28-13-11-52.gh-issue-101266.AxV3OF.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-28-13-11-52.gh-issue-101266.AxV3OF.rst new file mode 100644 index 00000000000000..51999bacb8de07 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-28-13-11-52.gh-issue-101266.AxV3OF.rst @@ -0,0 +1 @@ +Fix :func:`sys.getsizeof` reporting for :class:`int` subclasses. diff --git a/Objects/boolobject.c b/Objects/boolobject.c index 55b4a4077ab783..a035f463323823 100644 --- a/Objects/boolobject.c +++ b/Objects/boolobject.c @@ -4,6 +4,8 @@ #include "pycore_object.h" // _Py_FatalRefcountError() #include "pycore_runtime.h" // _Py_ID() +#include + /* We define bool_repr to return "False" or "True" */ static PyObject * @@ -153,8 +155,8 @@ bool_dealloc(PyObject* Py_UNUSED(ignore)) PyTypeObject PyBool_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "bool", - sizeof(struct _longobject), - 0, + offsetof(struct _longobject, long_value.ob_digit), /* tp_basicsize */ + sizeof(digit), /* tp_itemsize */ bool_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ diff --git a/Objects/longobject.c b/Objects/longobject.c index 65bf15648b07fb..8293f133bed213 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -5882,13 +5882,10 @@ static Py_ssize_t int___sizeof___impl(PyObject *self) /*[clinic end generated code: output=3303f008eaa6a0a5 input=9b51620c76fc4507]*/ { - Py_ssize_t res; - - res = offsetof(PyLongObject, long_value.ob_digit) - /* using Py_MAX(..., 1) because we always allocate space for at least - one digit, even though the integer zero has a Py_SIZE of 0 */ - + Py_MAX(Py_ABS(Py_SIZE(self)), 1)*sizeof(digit); - return res; + /* using Py_MAX(..., 1) because we always allocate space for at least + one digit, even though the integer zero has a Py_SIZE of 0 */ + Py_ssize_t ndigits = Py_MAX(Py_ABS(Py_SIZE(self)), 1); + return Py_TYPE(self)->tp_basicsize + Py_TYPE(self)->tp_itemsize * ndigits; } /*[clinic input] From 19ac43629e6fd73f2dbf9fd5a0b97d791c28acc7 Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Sun, 5 Feb 2023 11:30:44 +0000 Subject: [PATCH 007/247] gh-101570: Update bundled pip version to 23.0 (#101571) Update bundled pip version to 23.0 This is the current latest version of `pip`. --------- Co-authored-by: Pradyun Gedam Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> --- Lib/ensurepip/__init__.py | 2 +- ...none-any.whl => pip-23.0-py3-none-any.whl} | Bin 2051534 -> 2056044 bytes ...-02-04-21-01-49.gh-issue-101570.lbtUsD.rst | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) rename Lib/ensurepip/_bundled/{pip-22.3.1-py3-none-any.whl => pip-23.0-py3-none-any.whl} (73%) create mode 100644 Misc/NEWS.d/next/Library/2023-02-04-21-01-49.gh-issue-101570.lbtUsD.rst diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py index 1a2f57c07ba341..052b7bca28ddd3 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 = "22.3.1" +_PIP_VERSION = "23.0" _PROJECTS = [ ("setuptools", _SETUPTOOLS_VERSION, "py3"), ("pip", _PIP_VERSION, "py3"), diff --git a/Lib/ensurepip/_bundled/pip-22.3.1-py3-none-any.whl b/Lib/ensurepip/_bundled/pip-23.0-py3-none-any.whl similarity index 73% rename from Lib/ensurepip/_bundled/pip-22.3.1-py3-none-any.whl rename to Lib/ensurepip/_bundled/pip-23.0-py3-none-any.whl index c5b7753e757df23620ad5f039050e7dcd49fffb5..bb9aebf474cfe981dbf984bd9e36e23560111288 100644 GIT binary patch delta 520370 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 |%G3#nRz51y3_SSkD3X+Odirs*jJgznSlu-HY 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
    O0OJxAC|60&& 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 delta 515738 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

    ^JImakCpEjkdFidjv0UoaIzd!%~se0(1L-c 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
  1. +Q^5*P!0_7WQXdN9yKOIjhVYaXB7M=zL(hkk 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

    YF)hhqhpo9jr<~1W8vsw` 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

    u#>xo){ zzKMyo{ceU@GT|qOGC3{ewL5Te=zr4~(x3M_Bu#2}kVa`mRN;Y3pUM_p3c_(*N+j3} za%sG%kB@fq3E4j=dSq5h&g|A+2 z-TzLG0flkbVjkWYb9{EUHpv)HGN7~vf+E+&tf_4agCgTANepE2AYq$n&4OBV^Duk> z!ku*niFpP-4I5_Z+A^1?I0f;_R&q(O$RWzdR4{z)!B2vvIV|WNzW0}g{OLM!wBQ

    *nsi@e$hJLi7) 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

    s4$e@L4CjxI$3t(skZ-Ezo}tFOiyMGO_{z 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�UFpG;B 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 diff --git a/Misc/NEWS.d/next/Library/2023-02-04-21-01-49.gh-issue-101570.lbtUsD.rst b/Misc/NEWS.d/next/Library/2023-02-04-21-01-49.gh-issue-101570.lbtUsD.rst new file mode 100644 index 00000000000000..599edab6c07117 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-04-21-01-49.gh-issue-101570.lbtUsD.rst @@ -0,0 +1 @@ +Upgrade pip wheel bundled with ensurepip (pip 23.0) From 0672a6c23b2b72666e10d9c61fc025e66aad9c2b Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sun, 5 Feb 2023 16:36:33 +0000 Subject: [PATCH 008/247] Revert "gh-89381: Fix invalid signatures of math/cmath.log (#101404)" (#101580) This reverts commit 0ef92d979311ba82d4c41b22ef38e12e1b08b13d. --- Doc/library/cmath.rst | 2 +- Doc/library/math.rst | 9 ++-- Lib/test/test_math.py | 1 - ...3-01-16-10-42-58.gh-issue-89381.lM2WL0.rst | 1 - Modules/clinic/cmathmodule.c.h | 9 ++-- Modules/clinic/mathmodule.c.h | 44 +++++++++++-------- Modules/cmathmodule.c | 9 ++-- Modules/mathmodule.c | 14 +++--- 8 files changed, 47 insertions(+), 42 deletions(-) delete mode 100644 Misc/NEWS.d/next/Library/2023-01-16-10-42-58.gh-issue-89381.lM2WL0.rst diff --git a/Doc/library/cmath.rst b/Doc/library/cmath.rst index c575b90e6461ed..28cd96b0e12da9 100644 --- a/Doc/library/cmath.rst +++ b/Doc/library/cmath.rst @@ -89,7 +89,7 @@ Power and logarithmic functions logarithms. -.. function:: log(x, base=None) +.. function:: log(x[, base]) Returns the logarithm of *x* to the given *base*. If the *base* is not specified, returns the natural logarithm of *x*. There is one branch cut, from 0 diff --git a/Doc/library/math.rst b/Doc/library/math.rst index 9da22b6ad0562f..797f32408eac3d 100644 --- a/Doc/library/math.rst +++ b/Doc/library/math.rst @@ -393,12 +393,13 @@ Power and logarithmic functions .. versionadded:: 3.2 -.. function:: log(x, base=None) +.. function:: log(x[, base]) - Return the logarithm of *x* to the given *base*. + With one argument, return the natural logarithm of *x* (to base *e*). + + With two arguments, return the logarithm of *x* to the given *base*, + calculated as ``log(x)/log(base)``. - If the *base* is not specified, returns the natural - logarithm (base *e*) of *x*. .. function:: log1p(x) diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 8d849045b2d11f..433161c2dd4145 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -1146,7 +1146,6 @@ def testLog(self): 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) - self.ftest('log(e, None)', math.log(math.e, None), 1) self.ftest('log(32,2)', math.log(32,2), 5) self.ftest('log(10**40, 10)', math.log(10**40, 10), 40) self.ftest('log(10**40, 10**20)', math.log(10**40, 10**20), 2) diff --git a/Misc/NEWS.d/next/Library/2023-01-16-10-42-58.gh-issue-89381.lM2WL0.rst b/Misc/NEWS.d/next/Library/2023-01-16-10-42-58.gh-issue-89381.lM2WL0.rst deleted file mode 100644 index 7bffe7d226e38a..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-16-10-42-58.gh-issue-89381.lM2WL0.rst +++ /dev/null @@ -1 +0,0 @@ -:func:`~math.log` and :func:`~cmath.log` support default base=None values. diff --git a/Modules/clinic/cmathmodule.c.h b/Modules/clinic/cmathmodule.c.h index bc91c20f373bcd..b1da9452c61db8 100644 --- a/Modules/clinic/cmathmodule.c.h +++ b/Modules/clinic/cmathmodule.c.h @@ -639,13 +639,12 @@ cmath_tanh(PyObject *module, PyObject *arg) } PyDoc_STRVAR(cmath_log__doc__, -"log($module, z, base=None, /)\n" +"log($module, z, base=, /)\n" "--\n" "\n" "log(z[, base]) -> the logarithm of z to the given base.\n" "\n" -"If the base is not specified or is None, returns the\n" -"natural logarithm (base e) of z."); +"If the base not specified, returns the natural logarithm (base e) of z."); #define CMATH_LOG_METHODDEF \ {"log", _PyCFunction_CAST(cmath_log), METH_FASTCALL, cmath_log__doc__}, @@ -658,7 +657,7 @@ cmath_log(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; Py_complex x; - PyObject *y_obj = Py_None; + PyObject *y_obj = NULL; if (!_PyArg_CheckPositional("log", nargs, 1, 2)) { goto exit; @@ -983,4 +982,4 @@ cmath_isclose(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec exit: return return_value; } -/*[clinic end generated code: output=2630f8740909a8f7 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=0146c656e67f5d5f input=a9049054013a1b77]*/ diff --git a/Modules/clinic/mathmodule.c.h b/Modules/clinic/mathmodule.c.h index 0d61fd1be38ddb..1f9725883b9820 100644 --- a/Modules/clinic/mathmodule.c.h +++ b/Modules/clinic/mathmodule.c.h @@ -187,37 +187,43 @@ math_modf(PyObject *module, PyObject *arg) } PyDoc_STRVAR(math_log__doc__, -"log($module, x, base=None, /)\n" -"--\n" -"\n" +"log(x, [base=math.e])\n" "Return the logarithm of x to the given base.\n" "\n" -"If the base is not specified or is None, returns the natural\n" -"logarithm (base e) of x."); +"If the base not specified, returns the natural logarithm (base e) of x."); #define MATH_LOG_METHODDEF \ - {"log", _PyCFunction_CAST(math_log), METH_FASTCALL, math_log__doc__}, + {"log", (PyCFunction)math_log, METH_VARARGS, math_log__doc__}, static PyObject * -math_log_impl(PyObject *module, PyObject *x, PyObject *base); +math_log_impl(PyObject *module, PyObject *x, int group_right_1, + PyObject *base); static PyObject * -math_log(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +math_log(PyObject *module, PyObject *args) { PyObject *return_value = NULL; PyObject *x; - PyObject *base = Py_None; + int group_right_1 = 0; + PyObject *base = NULL; - if (!_PyArg_CheckPositional("log", nargs, 1, 2)) { - goto exit; - } - x = args[0]; - if (nargs < 2) { - goto skip_optional; + switch (PyTuple_GET_SIZE(args)) { + case 1: + if (!PyArg_ParseTuple(args, "O:log", &x)) { + goto exit; + } + break; + case 2: + if (!PyArg_ParseTuple(args, "OO:log", &x, &base)) { + goto exit; + } + group_right_1 = 1; + break; + default: + PyErr_SetString(PyExc_TypeError, "math.log requires 1 to 2 arguments"); + goto exit; } - base = args[1]; -skip_optional: - return_value = math_log_impl(module, x, base); + return_value = math_log_impl(module, x, group_right_1, base); exit: return return_value; @@ -948,4 +954,4 @@ math_ulp(PyObject *module, PyObject *arg) exit: return return_value; } -/*[clinic end generated code: output=afec63ebb0da709a input=a9049054013a1b77]*/ +/*[clinic end generated code: output=899211ec70e4506c input=a9049054013a1b77]*/ diff --git a/Modules/cmathmodule.c b/Modules/cmathmodule.c index 62caba031eda27..2038ac26e65857 100644 --- a/Modules/cmathmodule.c +++ b/Modules/cmathmodule.c @@ -952,24 +952,23 @@ cmath_tanh_impl(PyObject *module, Py_complex z) cmath.log z as x: Py_complex - base as y_obj: object = None + base as y_obj: object = NULL / log(z[, base]) -> the logarithm of z to the given base. -If the base is not specified or is None, returns the -natural logarithm (base e) of z. +If the base not specified, returns the natural logarithm (base e) of z. [clinic start generated code]*/ static PyObject * cmath_log_impl(PyObject *module, Py_complex x, PyObject *y_obj) -/*[clinic end generated code: output=4effdb7d258e0d94 input=e7db51859ebf70bf]*/ +/*[clinic end generated code: output=4effdb7d258e0d94 input=230ed3a71ecd000a]*/ { Py_complex y; errno = 0; x = c_log(x); - if (y_obj != Py_None) { + if (y_obj != NULL) { y = PyComplex_AsCComplex(y_obj); if (PyErr_Occurred()) { return NULL; diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 654336d6d9f4bc..992957e675a7a3 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2366,24 +2366,26 @@ loghelper(PyObject* arg, double (*func)(double)) math.log x: object - base: object = None + [ + base: object(c_default="NULL") = math.e + ] / Return the logarithm of x to the given base. -If the base is not specified or is None, returns the natural -logarithm (base e) of x. +If the base not specified, returns the natural logarithm (base e) of x. [clinic start generated code]*/ static PyObject * -math_log_impl(PyObject *module, PyObject *x, PyObject *base) -/*[clinic end generated code: output=1dead263cbb1e854 input=ef032cc9837943e1]*/ +math_log_impl(PyObject *module, PyObject *x, int group_right_1, + PyObject *base) +/*[clinic end generated code: output=7b5a39e526b73fc9 input=0f62d5726cbfebbd]*/ { PyObject *num, *den; PyObject *ans; num = loghelper(x, m_log); - if (num == NULL || base == Py_None) + if (num == NULL || base == NULL) return num; den = loghelper(base, m_log); From 90d85a9b4136aa1feb02f88aab614a3c29f20ed3 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sun, 5 Feb 2023 17:10:53 +0000 Subject: [PATCH 009/247] gh-76961: Fix the PEP3118 format string for ctypes.Structure (#5561) The summary of this diff is that it: * adds a `_ctypes_alloc_format_padding` function to append strings like `37x` to a format string to indicate 37 padding bytes * removes the branches that amount to "give up on producing a valid format string if the struct is packed" * combines the resulting adjacent `if (isStruct) {`s now that neither is `if (isStruct && !isPacked) {` * invokes `_ctypes_alloc_format_padding` to add padding between structure fields, and after the last structure field. The computation used for the total size is unchanged from ctypes already used. This patch does not affect any existing aligment computation; all it does is use subtraction to deduce the amount of paddnig introduced by the existing code. --- Without this fix, it would never include padding bytes - an assumption that was only valid in the case when `_pack_` was set - and this case was explicitly not implemented. This should allow conversion from ctypes structs to numpy structs Fixes https://github.com/numpy/numpy/issues/10528 --- Doc/library/ctypes.rst | 1 + Lib/test/test_ctypes/test_pep3118.py | 23 +++- .../2018-02-05-21-54-46.bpo-32780.Dtiz8z.rst | 3 + Modules/_ctypes/stgdict.c | 117 +++++++++++++----- 4 files changed, 111 insertions(+), 33 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2018-02-05-21-54-46.bpo-32780.Dtiz8z.rst diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 4de5c820f2c6ac..0642bbe9f99d0d 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -2510,6 +2510,7 @@ fields, or any other data types containing pointer type fields. An optional small integer that allows overriding the alignment of structure fields in the instance. :attr:`_pack_` must already be defined when :attr:`_fields_` is assigned, otherwise it will have no effect. + Setting this attribute to 0 is the same as not setting it at all. .. attribute:: _anonymous_ diff --git a/Lib/test/test_ctypes/test_pep3118.py b/Lib/test/test_ctypes/test_pep3118.py index efffc80a66fcb8..74fdf29fc9ad05 100644 --- a/Lib/test/test_ctypes/test_pep3118.py +++ b/Lib/test/test_ctypes/test_pep3118.py @@ -86,6 +86,20 @@ class PackedPoint(Structure): _pack_ = 2 _fields_ = [("x", c_long), ("y", c_long)] +class PointMidPad(Structure): + _fields_ = [("x", c_byte), ("y", c_uint64)] + +class PackedPointMidPad(Structure): + _pack_ = 2 + _fields_ = [("x", c_byte), ("y", c_uint64)] + +class PointEndPad(Structure): + _fields_ = [("x", c_uint64), ("y", c_byte)] + +class PackedPointEndPad(Structure): + _pack_ = 2 + _fields_ = [("x", c_uint64), ("y", c_byte)] + class Point2(Structure): pass Point2._fields_ = [("x", c_long), ("y", c_long)] @@ -185,10 +199,13 @@ class Complete(Structure): ## structures and unions - (Point, "T{ 0); + + if (padding == 1) { + /* Use x instead of 1x, for brevity */ + return _ctypes_alloc_format_string(prefix, "x"); + } + + int ret = PyOS_snprintf(buf, sizeof(buf), "%zdx", padding); + assert(0 <= ret && ret < sizeof(buf)); + return _ctypes_alloc_format_string(prefix, buf); +} + /* Retrieve the (optional) _pack_ attribute from a type, the _fields_ attribute, and create an StgDictObject. Used for Structure and Union subclasses. @@ -346,11 +369,10 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct { StgDictObject *stgdict, *basedict; Py_ssize_t len, offset, size, align, i; - Py_ssize_t union_size, total_align; + Py_ssize_t union_size, total_align, aligned_size; Py_ssize_t field_size = 0; int bitofs; PyObject *tmp; - int isPacked; int pack; Py_ssize_t ffi_ofs; int big_endian; @@ -374,7 +396,6 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct return -1; } if (tmp) { - isPacked = 1; pack = _PyLong_AsInt(tmp); Py_DECREF(tmp); if (pack < 0) { @@ -389,7 +410,7 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct } } else { - isPacked = 0; + /* Setting `_pack_ = 0` amounts to using the default alignment */ pack = 0; } @@ -470,12 +491,10 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct } assert(stgdict->format == NULL); - if (isStruct && !isPacked) { + if (isStruct) { stgdict->format = _ctypes_alloc_format_string(NULL, "T{"); } else { - /* PEP3118 doesn't support union, or packed structures (well, - only standard packing, but we don't support the pep for - that). Use 'B' for bytes. */ + /* PEP3118 doesn't support union. Use 'B' for bytes. */ stgdict->format = _ctypes_alloc_format_string(NULL, "B"); } if (stgdict->format == NULL) @@ -543,12 +562,14 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct } else bitsize = 0; - if (isStruct && !isPacked) { + if (isStruct) { const char *fieldfmt = dict->format ? dict->format : "B"; const char *fieldname = PyUnicode_AsUTF8(name); char *ptr; Py_ssize_t len; char *buf; + Py_ssize_t last_size = size; + Py_ssize_t padding; if (fieldname == NULL) { @@ -556,11 +577,38 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct return -1; } + /* construct the field now, as `prop->offset` is `offset` with + corrected alignment */ + prop = PyCField_FromDesc(desc, i, + &field_size, bitsize, &bitofs, + &size, &offset, &align, + pack, big_endian); + if (prop == NULL) { + Py_DECREF(pair); + return -1; + } + + /* number of bytes between the end of the last field and the start + of this one */ + padding = ((CFieldObject *)prop)->offset - last_size; + + if (padding > 0) { + ptr = stgdict->format; + stgdict->format = _ctypes_alloc_format_padding(ptr, padding); + PyMem_Free(ptr); + if (stgdict->format == NULL) { + Py_DECREF(pair); + Py_DECREF(prop); + return -1; + } + } + len = strlen(fieldname) + strlen(fieldfmt); buf = PyMem_Malloc(len + 2 + 1); if (buf == NULL) { Py_DECREF(pair); + Py_DECREF(prop); PyErr_NoMemory(); return -1; } @@ -578,15 +626,9 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct if (stgdict->format == NULL) { Py_DECREF(pair); + Py_DECREF(prop); return -1; } - } - - if (isStruct) { - prop = PyCField_FromDesc(desc, i, - &field_size, bitsize, &bitofs, - &size, &offset, &align, - pack, big_endian); } else /* union */ { size = 0; offset = 0; @@ -595,14 +637,14 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct &field_size, bitsize, &bitofs, &size, &offset, &align, pack, big_endian); + if (prop == NULL) { + Py_DECREF(pair); + return -1; + } union_size = max(size, union_size); } total_align = max(align, total_align); - if (!prop) { - Py_DECREF(pair); - return -1; - } if (-1 == PyObject_SetAttr(type, name, prop)) { Py_DECREF(prop); Py_DECREF(pair); @@ -612,26 +654,41 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct Py_DECREF(prop); } - if (isStruct && !isPacked) { - char *ptr = stgdict->format; + if (!isStruct) { + size = union_size; + } + + /* Adjust the size according to the alignment requirements */ + aligned_size = ((size + total_align - 1) / total_align) * total_align; + + if (isStruct) { + char *ptr; + Py_ssize_t padding; + + /* Pad up to the full size of the struct */ + padding = aligned_size - size; + if (padding > 0) { + ptr = stgdict->format; + stgdict->format = _ctypes_alloc_format_padding(ptr, padding); + PyMem_Free(ptr); + if (stgdict->format == NULL) { + return -1; + } + } + + ptr = stgdict->format; stgdict->format = _ctypes_alloc_format_string(stgdict->format, "}"); PyMem_Free(ptr); if (stgdict->format == NULL) return -1; } - if (!isStruct) - size = union_size; - - /* Adjust the size according to the alignment requirements */ - size = ((size + total_align - 1) / total_align) * total_align; - stgdict->ffi_type_pointer.alignment = Py_SAFE_DOWNCAST(total_align, Py_ssize_t, unsigned short); - stgdict->ffi_type_pointer.size = size; + stgdict->ffi_type_pointer.size = aligned_size; - stgdict->size = size; + stgdict->size = aligned_size; stgdict->align = total_align; stgdict->length = len; /* ADD ffi_ofs? */ From f7e9fbacb250ad9df95d0024161b40dfdc9cc39e Mon Sep 17 00:00:00 2001 From: mrh1997 Date: Sun, 5 Feb 2023 18:36:57 +0100 Subject: [PATCH 010/247] bpo-33591: Add support for path like objects to `ctypes.CDLL` (#7032) Co-authored-by: Oleg Iarygin --- Doc/library/ctypes.rst | 16 ++++++++++++++++ Lib/ctypes/__init__.py | 2 ++ Lib/test/test_ctypes/test_loading.py | 18 ++++++++++++++---- Misc/ACKS | 1 + ...18-05-21-17-18-00.gh-issue-77772.Fhg84L.rst | 3 +++ 5 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2018-05-21-17-18-00.gh-issue-77772.Fhg84L.rst diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 0642bbe9f99d0d..8fd681286b812d 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -1380,6 +1380,10 @@ way is to instantiate one of the following classes: DLLs and determine which one is not found using Windows debugging and tracing tools. + .. versionchanged:: 3.12 + + The *name* parameter can now be a :term:`path-like object`. + .. seealso:: `Microsoft DUMPBIN tool `_ @@ -1398,6 +1402,10 @@ way is to instantiate one of the following classes: .. versionchanged:: 3.3 :exc:`WindowsError` used to be raised. + .. versionchanged:: 3.12 + + The *name* parameter can now be a :term:`path-like object`. + .. class:: WinDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False, winmode=None) @@ -1405,6 +1413,10 @@ way is to instantiate one of the following classes: functions in these libraries use the ``stdcall`` calling convention, and are assumed to return :c:expr:`int` by default. + .. versionchanged:: 3.12 + + The *name* parameter can now be a :term:`path-like object`. + The Python :term:`global interpreter lock` is released before calling any function exported by these libraries, and reacquired afterwards. @@ -1418,6 +1430,10 @@ function exported by these libraries, and reacquired afterwards. Thus, this is only useful to call Python C api functions directly. + .. versionchanged:: 3.12 + + The *name* parameter can now be a :term:`path-like object`. + All these classes can be instantiated by calling them with at least one argument, the pathname of the shared library. If you have an existing handle to an already loaded shared library, it can be passed as the ``handle`` named diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index 2e9d4c5e7238e9..95353bab26cc71 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -344,6 +344,8 @@ def __init__(self, name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False, winmode=None): + if name: + name = _os.fspath(name) self._name = name flags = self._func_flags_ if use_errno: diff --git a/Lib/test/test_ctypes/test_loading.py b/Lib/test/test_ctypes/test_loading.py index 15e365ed267d9b..f2434926a51714 100644 --- a/Lib/test/test_ctypes/test_loading.py +++ b/Lib/test/test_ctypes/test_loading.py @@ -28,10 +28,20 @@ class LoaderTest(unittest.TestCase): unknowndll = "xxrandomnamexx" def test_load(self): - if libc_name is None: - self.skipTest('could not find libc') - CDLL(libc_name) - CDLL(os.path.basename(libc_name)) + if libc_name is not None: + test_lib = libc_name + else: + if os.name == "nt": + import _ctypes_test + test_lib = _ctypes_test.__file__ + else: + self.skipTest('could not find library to load') + CDLL(test_lib) + CDLL(os.path.basename(test_lib)) + class CTypesTestPathLikeCls: + def __fspath__(self): + return test_lib + CDLL(CTypesTestPathLikeCls()) self.assertRaises(OSError, CDLL, self.unknowndll) def test_load_version(self): diff --git a/Misc/ACKS b/Misc/ACKS index 74abcebe21ea60..d27d60f5b3605e 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -754,6 +754,7 @@ Tim Hochberg Benjamin Hodgson Joerg-Cyril Hoehle Douwe Hoekstra +Robert Hoelzl Gregor Hoffleit Chris Hoffman Tim Hoffmann diff --git a/Misc/NEWS.d/next/Library/2018-05-21-17-18-00.gh-issue-77772.Fhg84L.rst b/Misc/NEWS.d/next/Library/2018-05-21-17-18-00.gh-issue-77772.Fhg84L.rst new file mode 100644 index 00000000000000..3a7c6d45297ba4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-05-21-17-18-00.gh-issue-77772.Fhg84L.rst @@ -0,0 +1,3 @@ +:class:`ctypes.CDLL`, :class:`ctypes.OleDLL`, :class:`ctypes.WinDLL`, +and :class:`ctypes.PyDLL` now accept :term:`path-like objects +` as their ``name`` argument. Patch by Robert Hoelzl. From ffcb8220d7a8c8ca169b467d9e4a752874f68af2 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sun, 5 Feb 2023 09:44:57 -0800 Subject: [PATCH 011/247] gh-101334: Don't force USTAR format in test_tarfile. (GH-101572) That causes the test to fail when run using a high UID as that ancient format cannot represent it. The current default (PAX) and the old default (GNU) both support high UIDs. --- Lib/test/test_tarfile.py | 5 +++++ .../Tests/2023-02-04-17-24-33.gh-issue-101334._yOqwg.rst | 1 + 2 files changed, 6 insertions(+) create mode 100644 Misc/NEWS.d/next/Tests/2023-02-04-17-24-33.gh-issue-101334._yOqwg.rst diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index 213932069201b9..f15a800976681c 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -225,6 +225,11 @@ def test_add_dir_getmember(self): self.add_dir_and_getmember('bar') self.add_dir_and_getmember('a'*101) + @unittest.skipIf( + (hasattr(os, 'getuid') and os.getuid() > 0o777_7777) or + (hasattr(os, 'getgid') and os.getgid() > 0o777_7777), + "uid or gid too high for USTAR format." + ) def add_dir_and_getmember(self, name): with os_helper.temp_cwd(): with tarfile.open(tmpname, 'w') as tar: diff --git a/Misc/NEWS.d/next/Tests/2023-02-04-17-24-33.gh-issue-101334._yOqwg.rst b/Misc/NEWS.d/next/Tests/2023-02-04-17-24-33.gh-issue-101334._yOqwg.rst new file mode 100644 index 00000000000000..2a95fd9ae53c86 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-02-04-17-24-33.gh-issue-101334._yOqwg.rst @@ -0,0 +1 @@ +``test_tarfile`` has been updated to pass when run as a high UID. From d3e2dd6e71bd8e5482973891926d5df19be687eb Mon Sep 17 00:00:00 2001 From: Matty G Date: Sun, 5 Feb 2023 18:55:37 -0800 Subject: [PATCH 012/247] Trivial Change: Remove unhelpful doc in `datetime.timedelta` (#100164) --- Lib/datetime.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Lib/datetime.py b/Lib/datetime.py index 68746de1cabf85..637144637485bc 100644 --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -587,9 +587,12 @@ class timedelta: returning a timedelta, and addition or subtraction of a datetime and a timedelta giving a datetime. - Representation: (days, seconds, microseconds). Why? Because I - felt like it. + Representation: (days, seconds, microseconds). """ + # The representation of (days, seconds, microseconds) was chosen + # arbitrarily; the exact rationale originally specified in the docstring + # was "Because I felt like it." + __slots__ = '_days', '_seconds', '_microseconds', '_hashcode' def __new__(cls, days=0, seconds=0, microseconds=0, From ef7c2bfcf1fd618438f981ace64499a99ae9fae0 Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Sun, 5 Feb 2023 19:29:06 -0800 Subject: [PATCH 013/247] gh-101541: [Enum] create flag psuedo-member without calling original __new__ (GH-101590) --- Lib/enum.py | 5 +-- Lib/test/test_enum.py | 40 +++++++++++++++++++ ...-02-05-14-39-49.gh-issue-101541.Mo3ppp.rst | 1 + 3 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-02-05-14-39-49.gh-issue-101541.Mo3ppp.rst diff --git a/Lib/enum.py b/Lib/enum.py index adb61519abe942..d14e91a9b017d1 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -1429,12 +1429,11 @@ def _missing_(cls, value): % (cls.__name__, value, unknown, bin(unknown)) ) # normal Flag? - __new__ = getattr(cls, '__new_member__', None) - if cls._member_type_ is object and not __new__: + if cls._member_type_ is object: # construct a singleton enum pseudo-member pseudo_member = object.__new__(cls) else: - pseudo_member = (__new__ or cls._member_type_.__new__)(cls, value) + pseudo_member = cls._member_type_.__new__(cls, value) if not hasattr(pseudo_member, '_value_'): pseudo_member._value_ = value if member_value: diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index 1e653e94f6b57a..0a2e0c14d268af 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -2855,6 +2855,46 @@ class NTEnum(Enum): [TTuple(id=0, a=0, blist=[]), TTuple(id=1, a=2, blist=[4]), TTuple(id=2, a=4, blist=[0, 1, 2])], ) + def test_flag_with_custom_new(self): + class FlagFromChar(IntFlag): + def __new__(cls, c): + value = 1 << c + self = int.__new__(cls, value) + self._value_ = value + return self + # + a = ord('a') + # + self.assertEqual(FlagFromChar.a, 158456325028528675187087900672) + self.assertEqual(FlagFromChar.a|1, 158456325028528675187087900673) + # + # + class FlagFromChar(Flag): + def __new__(cls, c): + value = 1 << c + self = object.__new__(cls) + self._value_ = value + return self + # + a = ord('a') + z = 1 + # + self.assertEqual(FlagFromChar.a.value, 158456325028528675187087900672) + self.assertEqual((FlagFromChar.a|FlagFromChar.z).value, 158456325028528675187087900674) + # + # + class FlagFromChar(int, Flag, boundary=KEEP): + def __new__(cls, c): + value = 1 << c + self = int.__new__(cls, value) + self._value_ = value + return self + # + a = ord('a') + # + self.assertEqual(FlagFromChar.a, 158456325028528675187087900672) + self.assertEqual(FlagFromChar.a|1, 158456325028528675187087900673) + class TestOrder(unittest.TestCase): "test usage of the `_order_` attribute" diff --git a/Misc/NEWS.d/next/Library/2023-02-05-14-39-49.gh-issue-101541.Mo3ppp.rst b/Misc/NEWS.d/next/Library/2023-02-05-14-39-49.gh-issue-101541.Mo3ppp.rst new file mode 100644 index 00000000000000..0f149e80dc54a2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-05-14-39-49.gh-issue-101541.Mo3ppp.rst @@ -0,0 +1 @@ +[Enum] - fix psuedo-flag creation From 9ef7e75434587fc8f167d73eee5dd9bdca62714b Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Mon, 6 Feb 2023 13:58:00 +0900 Subject: [PATCH 014/247] =?UTF-8?q?gh-101372:=20Fix=20unicodedata.is=5Fnor?= =?UTF-8?q?malized=20to=20properly=20handle=20the=20UCD=203=E2=80=A6=20(gh?= =?UTF-8?q?-101388)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2023-01-28-20-31-42.gh-issue-101372.8BcpCC.rst | 2 ++ Modules/unicodedata.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-28-20-31-42.gh-issue-101372.8BcpCC.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-28-20-31-42.gh-issue-101372.8BcpCC.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-28-20-31-42.gh-issue-101372.8BcpCC.rst new file mode 100644 index 00000000000000..65a207e3f7e436 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-28-20-31-42.gh-issue-101372.8BcpCC.rst @@ -0,0 +1,2 @@ +Fix :func:`~unicodedata.is_normalized` to properly handle the UCD 3.2.0 +cases. Patch by Dong-hee Na. diff --git a/Modules/unicodedata.c b/Modules/unicodedata.c index 59fccd4b834dd3..c108f14871f946 100644 --- a/Modules/unicodedata.c +++ b/Modules/unicodedata.c @@ -800,7 +800,7 @@ is_normalized_quickcheck(PyObject *self, PyObject *input, bool nfc, bool k, { /* UCD 3.2.0 is requested, quickchecks must be disabled. */ if (UCD_Check(self)) { - return NO; + return MAYBE; } if (PyUnicode_IS_ASCII(input)) { From 46416b9004b687856eaa73e5d48520cd768bbf82 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Mon, 6 Feb 2023 12:25:31 +0000 Subject: [PATCH 015/247] gh-76961: Fix buildbot failures in test_pep3118 (#101587) This PR fixes the buildbot failures introduced by the merge of #5561, by restricting the relevant tests to something that should work on both 32-bit and 64-bit platforms. It also silences some compiler warnings introduced in that PR. --- Lib/test/test_ctypes/test_pep3118.py | 20 ++++++++++---------- Modules/_ctypes/stgdict.c | 6 +++--- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Lib/test/test_ctypes/test_pep3118.py b/Lib/test/test_ctypes/test_pep3118.py index 74fdf29fc9ad05..c8a70e3e335693 100644 --- a/Lib/test/test_ctypes/test_pep3118.py +++ b/Lib/test/test_ctypes/test_pep3118.py @@ -87,14 +87,14 @@ class PackedPoint(Structure): _fields_ = [("x", c_long), ("y", c_long)] class PointMidPad(Structure): - _fields_ = [("x", c_byte), ("y", c_uint64)] + _fields_ = [("x", c_byte), ("y", c_uint)] class PackedPointMidPad(Structure): _pack_ = 2 _fields_ = [("x", c_byte), ("y", c_uint64)] class PointEndPad(Structure): - _fields_ = [("x", c_uint64), ("y", c_byte)] + _fields_ = [("x", c_uint), ("y", c_byte)] class PackedPointEndPad(Structure): _pack_ = 2 @@ -199,14 +199,14 @@ class Complete(Structure): ## structures and unions - (Point2, "T{ Date: Mon, 6 Feb 2023 15:55:32 +0000 Subject: [PATCH 016/247] gh-101543: Ensure Windows registry path is only used when stdlib can't be found (GH-101544) --- ...-02-03-17-53-06.gh-issue-101543.cORAT4.rst | 2 ++ Modules/getpath.py | 31 +++++++------------ 2 files changed, 14 insertions(+), 19 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2023-02-03-17-53-06.gh-issue-101543.cORAT4.rst diff --git a/Misc/NEWS.d/next/Windows/2023-02-03-17-53-06.gh-issue-101543.cORAT4.rst b/Misc/NEWS.d/next/Windows/2023-02-03-17-53-06.gh-issue-101543.cORAT4.rst new file mode 100644 index 00000000000000..d4e2c6f23013b6 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-02-03-17-53-06.gh-issue-101543.cORAT4.rst @@ -0,0 +1,2 @@ +Ensure the install path in the registry is only used when the standard +library hasn't been located in any other way. diff --git a/Modules/getpath.py b/Modules/getpath.py index 6a0883878778a5..9913fcba497d30 100644 --- a/Modules/getpath.py +++ b/Modules/getpath.py @@ -582,7 +582,7 @@ def search_up(prefix, *landmarks, test=isfile): # Detect prefix by searching from our executable location for the stdlib_dir if STDLIB_SUBDIR and STDLIB_LANDMARKS and executable_dir and not prefix: prefix = search_up(executable_dir, *STDLIB_LANDMARKS) - if prefix: + if prefix and not stdlib_dir: stdlib_dir = joinpath(prefix, STDLIB_SUBDIR) if PREFIX and not prefix: @@ -631,20 +631,6 @@ def search_up(prefix, *landmarks, test=isfile): warn('Consider setting $PYTHONHOME to [:]') -# If we haven't set [plat]stdlib_dir already, set them now -if not stdlib_dir: - if prefix: - stdlib_dir = joinpath(prefix, STDLIB_SUBDIR) - else: - stdlib_dir = '' - -if not platstdlib_dir: - if exec_prefix: - platstdlib_dir = joinpath(exec_prefix, PLATSTDLIB_LANDMARK) - else: - platstdlib_dir = '' - - # For a venv, update the main prefix/exec_prefix but leave the base ones unchanged # XXX: We currently do not update prefix here, but it happens in site.py #if venv_prefix: @@ -706,8 +692,9 @@ def search_up(prefix, *landmarks, test=isfile): pythonpath.extend(v.split(DELIM)) i += 1 # Paths from the core key get appended last, but only - # when home was not set and we aren't in a build dir - if not home_was_set and not venv_prefix and not build_prefix: + # when home was not set and we haven't found our stdlib + # some other way. + if not home and not stdlib_dir: v = winreg.QueryValue(key, None) if isinstance(v, str): pythonpath.extend(v.split(DELIM)) @@ -722,6 +709,11 @@ def search_up(prefix, *landmarks, test=isfile): pythonpath.append(joinpath(prefix, p)) # Then add stdlib_dir and platstdlib_dir + if not stdlib_dir and prefix: + stdlib_dir = joinpath(prefix, STDLIB_SUBDIR) + if not platstdlib_dir and exec_prefix: + platstdlib_dir = joinpath(exec_prefix, PLATSTDLIB_LANDMARK) + if os_name == 'nt': # QUIRK: Windows generates paths differently if platstdlib_dir: @@ -792,5 +784,6 @@ def search_up(prefix, *landmarks, test=isfile): config['base_exec_prefix'] = base_exec_prefix or exec_prefix config['platlibdir'] = platlibdir -config['stdlib_dir'] = stdlib_dir -config['platstdlib_dir'] = platstdlib_dir +# test_embed expects empty strings, not None +config['stdlib_dir'] = stdlib_dir or '' +config['platstdlib_dir'] = platstdlib_dir or '' From b96b344f251954bb64aeb13c3e0c460350565c2a Mon Sep 17 00:00:00 2001 From: Eclips4 <80244920+Eclips4@users.noreply.github.com> Date: Mon, 6 Feb 2023 22:28:24 +0300 Subject: [PATCH 017/247] gh-101562: typing: add tests for inheritance with NotRequired & Required in parent fields (#101563) --- Lib/test/test_typing.py | 33 +++++++++++++++++++++++++++++++++ Misc/ACKS | 1 + 2 files changed, 34 insertions(+) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 5aa49bb0e2456d..7a460d94469fe7 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -4892,6 +4892,18 @@ class NontotalMovie(TypedDict, total=False): title: Required[str] year: int +class ParentNontotalMovie(TypedDict, total=False): + title: Required[str] + +class ChildTotalMovie(ParentNontotalMovie): + year: NotRequired[int] + +class ParentDeeplyAnnotatedMovie(TypedDict): + title: Annotated[Annotated[Required[str], "foobar"], "another level"] + +class ChildDeeplyAnnotatedMovie(ParentDeeplyAnnotatedMovie): + year: NotRequired[Annotated[int, 2000]] + class AnnotatedMovie(TypedDict): title: Annotated[Required[str], "foobar"] year: NotRequired[Annotated[int, 2000]] @@ -5221,6 +5233,17 @@ def test_get_type_hints_typeddict(self): 'a': Annotated[Required[int], "a", "b", "c"] }) + self.assertEqual(get_type_hints(ChildTotalMovie), {"title": str, "year": int}) + self.assertEqual(get_type_hints(ChildTotalMovie, include_extras=True), { + "title": Required[str], "year": NotRequired[int] + }) + + self.assertEqual(get_type_hints(ChildDeeplyAnnotatedMovie), {"title": str, "year": int}) + self.assertEqual(get_type_hints(ChildDeeplyAnnotatedMovie, include_extras=True), { + "title": Annotated[Required[str], "foobar", "another level"], + "year": NotRequired[Annotated[int, 2000]] + }) + def test_get_type_hints_collections_abc_callable(self): # https://github.com/python/cpython/issues/91621 P = ParamSpec('P') @@ -6381,6 +6404,16 @@ def test_required_notrequired_keys(self): self.assertEqual(WeirdlyQuotedMovie.__optional_keys__, frozenset({"year"})) + self.assertEqual(ChildTotalMovie.__required_keys__, + frozenset({"title"})) + self.assertEqual(ChildTotalMovie.__optional_keys__, + frozenset({"year"})) + + self.assertEqual(ChildDeeplyAnnotatedMovie.__required_keys__, + frozenset({"title"})) + self.assertEqual(ChildDeeplyAnnotatedMovie.__optional_keys__, + frozenset({"year"})) + def test_multiple_inheritance(self): class One(TypedDict): one: int diff --git a/Misc/ACKS b/Misc/ACKS index d27d60f5b3605e..e12cbea0ebd6ed 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1414,6 +1414,7 @@ Jean-François Piéronne Oleg Plakhotnyuk Anatoliy Platonov Marcel Plch +Kirill Podoprigora Remi Pointel Jon Poler Ariel Poliak From 262003fd3297f7f4ee09cebd1abb225066412ce7 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Tue, 7 Feb 2023 00:05:41 +0300 Subject: [PATCH 018/247] =?UTF-8?q?gh-101609:=20Fix=20"=E2=80=98state?= =?UTF-8?q?=E2=80=99=20may=20be=20used=20uninitialized"=20warning=20in=20`?= =?UTF-8?q?=5Fxxinterpchannelsmodule`=20(GH-101610)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I went with the easiest solution: just removing the offending line. See the issue description with my reasoning. https://github.com/python/cpython/issues/101609 --- Modules/_xxinterpchannelsmodule.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Modules/_xxinterpchannelsmodule.c b/Modules/_xxinterpchannelsmodule.c index 8601a189e87526..60538c31874864 100644 --- a/Modules/_xxinterpchannelsmodule.c +++ b/Modules/_xxinterpchannelsmodule.c @@ -174,7 +174,9 @@ static int clear_module_state(module_state *state) { /* heap types */ - (void)_PyCrossInterpreterData_UnregisterClass(state->ChannelIDType); + if (state->ChannelIDType != NULL) { + (void)_PyCrossInterpreterData_UnregisterClass(state->ChannelIDType); + } Py_CLEAR(state->ChannelIDType); /* exceptions */ @@ -2269,7 +2271,6 @@ module_exec(PyObject *mod) return 0; error: - (void)_PyCrossInterpreterData_UnregisterClass(state->ChannelIDType); _globals_fini(); return -1; } From 132b3f8302c021ac31e9c1797a127d57faa1afee Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 6 Feb 2023 14:39:25 -0700 Subject: [PATCH 019/247] gh-59956: Partial Fix for GILState API Compatibility with Subinterpreters (gh-101431) The GILState API (PEP 311) implementation from 2003 made the assumption that only one thread state would ever be used for any given OS thread, explicitly disregarding the case of subinterpreters. However, PyThreadState_Swap() still facilitated switching between subinterpreters, meaning the "current" thread state (holding the GIL), and the GILState thread state could end up out of sync, causing problems (including crashes). This change addresses the issue by keeping the two in sync in PyThreadState_Swap(). I verified the fix against gh-99040. Note that the other GILState-subinterpreter incompatibility (with autoInterpreterState) is not resolved here. https://github.com/python/cpython/issues/59956 --- ...3-01-30-11-56-09.gh-issue-59956.7xqnC_.rst | 3 +++ Python/pystate.c | 25 +++---------------- 2 files changed, 7 insertions(+), 21 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-30-11-56-09.gh-issue-59956.7xqnC_.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-30-11-56-09.gh-issue-59956.7xqnC_.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-30-11-56-09.gh-issue-59956.7xqnC_.rst new file mode 100644 index 00000000000000..b3c1896b9493e1 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-30-11-56-09.gh-issue-59956.7xqnC_.rst @@ -0,0 +1,3 @@ +The GILState API is now partially compatible with subinterpreters. +Previously, ``PyThreadState_GET()`` and ``PyGILState_GetThisThreadState()`` +would get out of sync, causing inconsistent behavior and crashes. diff --git a/Python/pystate.c b/Python/pystate.c index 8bb49d954a81b6..ed8c2e212a5539 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -266,10 +266,10 @@ unbind_tstate(PyThreadState *tstate) thread state for an OS level thread is when there are multiple interpreters. - The PyGILState_*() APIs don't work with multiple - interpreters (see bpo-10915 and bpo-15751), so this function - sets TSS only once. Thus, the first thread state created for that - given OS level thread will "win", which seems reasonable behaviour. + Before 3.12, the PyGILState_*() APIs didn't work with multiple + interpreters (see bpo-10915 and bpo-15751), so this function used + to set TSS only once. Thus, the first thread state created for that + given OS level thread would "win", which seemed reasonable behaviour. */ static void @@ -286,10 +286,6 @@ bind_gilstate_tstate(PyThreadState *tstate) assert(tstate != tcur); if (tcur != NULL) { - // The original gilstate implementation only respects the - // first thread state set. - // XXX Skipping like this does not play nice with multiple interpreters. - return; tcur->_status.bound_gilstate = 0; } gilstate_tss_set(runtime, tstate); @@ -1738,20 +1734,7 @@ _PyThreadState_Swap(_PyRuntimeState *runtime, PyThreadState *newts) tstate_activate(newts); } - /* It should not be possible for more than one thread state - to be used for a thread. Check this the best we can in debug - builds. - */ - // XXX The above isn't true when multiple interpreters are involved. #if defined(Py_DEBUG) - if (newts && gilstate_tss_initialized(runtime)) { - PyThreadState *check = gilstate_tss_get(runtime); - if (check != newts) { - if (check && check->interp == newts->interp) { - Py_FatalError("Invalid thread state for this thread"); - } - } - } errno = err; #endif return oldts; From 949c58f945b93af5b7bb70c6448e940da669065d Mon Sep 17 00:00:00 2001 From: Mariatta Wijaya Date: Mon, 6 Feb 2023 13:59:45 -0800 Subject: [PATCH 020/247] GH-101616: Mention the Docs Discourse forum in the "reporting docs issues" (GH-101617) Fixes https://github.com/python/cpython/issues/101616 --- Doc/bugs.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Doc/bugs.rst b/Doc/bugs.rst index 69d7c27410d56a..4f30ef19ee4d8a 100644 --- a/Doc/bugs.rst +++ b/Doc/bugs.rst @@ -19,6 +19,9 @@ If you find a bug in this documentation or would like to propose an improvement, please submit a bug report on the :ref:`tracker `. If you have a suggestion on how to fix it, include that as well. +You can also open a discussion item on our +`Documentation Discourse forum `_. + If you're short on time, you can also email documentation bug reports to docs@python.org (behavioral bugs can be sent to python-list@python.org). 'docs@' is a mailing list run by volunteers; your request will be noticed, From 38752760c91c87dd67af16d2cee611a22e647567 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Mon, 6 Feb 2023 22:45:18 +0000 Subject: [PATCH 021/247] gh-98831: rewrite COPY and SWAP in the instruction definition DSL (#101620) --- Python/bytecodes.c | 17 ++++++----------- Python/generated_cases.c.h | 18 +++++++++++------- Python/opcode_metadata.h | 8 ++++---- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 8993567ac82206..0fc0b3b8280f8b 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -3098,11 +3098,9 @@ dummy_func( PUSH(result); } - // stack effect: ( -- __0) - inst(COPY) { - assert(oparg != 0); - PyObject *peek = PEEK(oparg); - PUSH(Py_NewRef(peek)); + inst(COPY, (bottom, unused[oparg-1] -- bottom, unused[oparg-1], top)) { + assert(oparg > 0); + top = Py_NewRef(bottom); } inst(BINARY_OP, (unused/1, lhs, rhs -- res)) { @@ -3126,12 +3124,9 @@ dummy_func( ERROR_IF(res == NULL, error); } - // stack effect: ( -- ) - inst(SWAP) { - assert(oparg != 0); - PyObject *top = TOP(); - SET_TOP(PEEK(oparg)); - PEEK(oparg) = top; + inst(SWAP, (bottom, unused[oparg-2], top -- + top, unused[oparg-2], bottom)) { + assert(oparg >= 2); } inst(EXTENDED_ARG, (--)) { diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index e524bfcb99d470..f0f314a143c2c0 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -3723,9 +3723,12 @@ } TARGET(COPY) { - assert(oparg != 0); - PyObject *peek = PEEK(oparg); - PUSH(Py_NewRef(peek)); + PyObject *bottom = PEEK(1 + (oparg-1)); + PyObject *top; + assert(oparg > 0); + top = Py_NewRef(bottom); + STACK_GROW(1); + POKE(1, top); DISPATCH(); } @@ -3760,10 +3763,11 @@ } TARGET(SWAP) { - assert(oparg != 0); - PyObject *top = TOP(); - SET_TOP(PEEK(oparg)); - PEEK(oparg) = top; + PyObject *top = PEEK(1); + PyObject *bottom = PEEK(2 + (oparg-2)); + assert(oparg >= 2); + POKE(1, bottom); + POKE(2 + (oparg-2), top); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index 857526c35aa5b6..948d17519e2709 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -333,11 +333,11 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { case FORMAT_VALUE: return -1; case COPY: - return -1; + return (oparg-1) + 1; case BINARY_OP: return 2; case SWAP: - return -1; + return (oparg-2) + 2; case EXTENDED_ARG: return 0; case CACHE: @@ -679,11 +679,11 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { case FORMAT_VALUE: return -1; case COPY: - return -1; + return (oparg-1) + 2; case BINARY_OP: return 1; case SWAP: - return -1; + return (oparg-2) + 2; case EXTENDED_ARG: return 0; case CACHE: From 914f8fd9f7fc5e48b54d938a68c932cc618ef3a6 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 6 Feb 2023 15:53:31 -0700 Subject: [PATCH 022/247] gh-59956: Add a Test to Verify GILState Matches the "Current" Thread State (gh-101625) This test should have been in gh-101431. https://github.com/python/cpython/issues/59956 --- Lib/test/test_capi/test_misc.py | 3 +++ Modules/_testcapimodule.c | 37 +++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index dace37c362e569..03e22d7a2d382d 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -1413,6 +1413,9 @@ def callback(): ret = assert_python_ok('-X', 'tracemalloc', '-c', code) self.assertIn(b'callback called', ret.out) + def test_gilstate_matches_current(self): + _testcapi.test_current_tstate_matches() + class Test_testcapi(unittest.TestCase): locals().update((name, getattr(_testcapi, name)) diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index f0d6e404f54a2f..5e47f4975a2d54 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -1534,6 +1534,42 @@ crash_no_current_thread(PyObject *self, PyObject *Py_UNUSED(ignored)) return NULL; } +/* Test that the GILState thread and the "current" thread match. */ +static PyObject * +test_current_tstate_matches(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyThreadState *orig_tstate = PyThreadState_Get(); + + if (orig_tstate != PyGILState_GetThisThreadState()) { + PyErr_SetString(PyExc_RuntimeError, + "current thread state doesn't match GILState"); + return NULL; + } + + const char *err = NULL; + PyThreadState_Swap(NULL); + PyThreadState *substate = Py_NewInterpreter(); + + if (substate != PyThreadState_Get()) { + err = "subinterpreter thread state not current"; + goto finally; + } + if (substate != PyGILState_GetThisThreadState()) { + err = "subinterpreter thread state doesn't match GILState"; + goto finally; + } + +finally: + Py_EndInterpreter(substate); + PyThreadState_Swap(orig_tstate); + + if (err != NULL) { + PyErr_SetString(PyExc_RuntimeError, err); + return NULL; + } + Py_RETURN_NONE; +} + /* To run some code in a sub-interpreter. */ static PyObject * run_in_subinterp(PyObject *self, PyObject *args) @@ -3354,6 +3390,7 @@ static PyMethodDef TestMethods[] = { {"make_memoryview_from_NULL_pointer", make_memoryview_from_NULL_pointer, METH_NOARGS}, {"crash_no_current_thread", crash_no_current_thread, METH_NOARGS}, + {"test_current_tstate_matches", test_current_tstate_matches, METH_NOARGS}, {"run_in_subinterp", run_in_subinterp, METH_VARARGS}, {"run_in_subinterp_with_config", _PyCFunction_CAST(run_in_subinterp_with_config), From 1fcc0efdaa84b3602c236391633b70ff36df149b Mon Sep 17 00:00:00 2001 From: Jonathan Protzenko Date: Mon, 6 Feb 2023 18:11:01 -0800 Subject: [PATCH 023/247] gh-99108: Replace SHA2-224 & 256 with verified code from HACL* (#99109) replacing hashlib primitives (for the non-OpenSSL case) with verified implementations from HACL*. This is the first PR in the series, and focuses specifically on SHA2-256 and SHA2-224. This PR imports Hacl_Streaming_SHA2 into the Python tree. This is the HACL* implementation of SHA2, which combines a core implementation of SHA2 along with a layer of buffer management that allows updating the digest with any number of bytes. This supersedes the previous implementation in the tree. @franziskuskiefer was kind enough to benchmark the changes: in addition to being verified (thus providing significant safety and security improvements), this implementation also provides a sizeable performance boost! ``` --------------------------------------------------------------- Benchmark Time CPU Iterations --------------------------------------------------------------- Sha2_256_Streaming 3163 ns 3160 ns 219353 // this PR LibTomCrypt_Sha2_256 5057 ns 5056 ns 136234 // library used by Python currently ``` The changes in this PR are as follows: - import the subset of HACL* that covers SHA2-256/224 into `Modules/_hacl` - rewire sha256module.c to use the HACL* implementation Co-authored-by: Gregory P. Smith [Google LLC] Co-authored-by: Erlend E. Aasland --- Lib/test/test_hashlib.py | 10 + Makefile.pre.in | 2 +- ...2-11-08-12-06-52.gh-issue-99108.4Wrsuh.rst | 4 + Modules/Setup.stdlib.in | 2 +- Modules/_hacl/Hacl_Streaming_SHA2.c | 682 ++++++++++++++++++ Modules/_hacl/Hacl_Streaming_SHA2.h | 136 ++++ Modules/_hacl/README.md | 29 + .../include/krml/FStar_UInt_8_16_32_64.h | 109 +++ Modules/_hacl/include/krml/internal/target.h | 218 ++++++ .../_hacl/include/krml/lowstar_endianness.h | 230 ++++++ .../_hacl/include/python_hacl_namespaces.h | 28 + Modules/_hacl/internal/Hacl_SHA2_Generic.h | 135 ++++ Modules/_hacl/refresh.sh | 132 ++++ Modules/sha256module.c | 398 ++-------- PCbuild/pythoncore.vcxproj | 3 +- PCbuild/pythoncore.vcxproj.filters | 3 + configure | 3 +- configure.ac | 5 +- 18 files changed, 1779 insertions(+), 350 deletions(-) create mode 100644 Misc/NEWS.d/next/Security/2022-11-08-12-06-52.gh-issue-99108.4Wrsuh.rst create mode 100644 Modules/_hacl/Hacl_Streaming_SHA2.c create mode 100644 Modules/_hacl/Hacl_Streaming_SHA2.h create mode 100644 Modules/_hacl/README.md create mode 100644 Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h create mode 100644 Modules/_hacl/include/krml/internal/target.h create mode 100644 Modules/_hacl/include/krml/lowstar_endianness.h create mode 100644 Modules/_hacl/include/python_hacl_namespaces.h create mode 100644 Modules/_hacl/internal/Hacl_SHA2_Generic.h create mode 100755 Modules/_hacl/refresh.sh diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py index 450dc4933f47f7..9c92b4e9c280dc 100644 --- a/Lib/test/test_hashlib.py +++ b/Lib/test/test_hashlib.py @@ -22,6 +22,7 @@ from test.support import _4G, bigmemtest from test.support.import_helper import import_fresh_module from test.support import os_helper +from test.support import requires_resource from test.support import threading_helper from test.support import warnings_helper from http.client import HTTPException @@ -354,6 +355,15 @@ def test_large_update(self): self.assertEqual(m1.digest(*args), m4_copy.digest(*args)) self.assertEqual(m4.digest(*args), m4_digest) + @requires_resource('cpu') + def test_sha256_update_over_4gb(self): + zero_1mb = b"\0" * 1024 * 1024 + h = hashlib.sha256() + for i in range(0, 4096): + h.update(zero_1mb) + h.update(b"hello world") + self.assertEqual(h.hexdigest(), "a5364f7a52ebe2e25f1838a4ca715a893b6fd7a23f2a0d9e9762120da8b1bf53") + def check(self, name, data, hexdigest, shake=False, **kwargs): length = len(hexdigest)//2 hexdigest = hexdigest.lower() diff --git a/Makefile.pre.in b/Makefile.pre.in index f2f9371e8ac04d..3641c4eeebeee3 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -2612,7 +2612,7 @@ MODULE__HASHLIB_DEPS=$(srcdir)/Modules/hashlib.h MODULE__IO_DEPS=$(srcdir)/Modules/_io/_iomodule.h MODULE__MD5_DEPS=$(srcdir)/Modules/hashlib.h MODULE__SHA1_DEPS=$(srcdir)/Modules/hashlib.h -MODULE__SHA256_DEPS=$(srcdir)/Modules/hashlib.h +MODULE__SHA256_DEPS=$(srcdir)/Modules/hashlib.h $(srcdir)/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h $(srcdir)/Modules/_hacl/include/krml/lowstar_endianness.h $(srcdir)/Modules/_hacl/include/krml/internal/target.h $(srcdir)/Modules/_hacl/Hacl_Streaming_SHA2.h MODULE__SHA3_DEPS=$(srcdir)/Modules/_sha3/sha3.c $(srcdir)/Modules/_sha3/sha3.h $(srcdir)/Modules/hashlib.h MODULE__SHA512_DEPS=$(srcdir)/Modules/hashlib.h MODULE__SOCKET_DEPS=$(srcdir)/Modules/socketmodule.h $(srcdir)/Modules/addrinfo.h $(srcdir)/Modules/getaddrinfo.c $(srcdir)/Modules/getnameinfo.c diff --git a/Misc/NEWS.d/next/Security/2022-11-08-12-06-52.gh-issue-99108.4Wrsuh.rst b/Misc/NEWS.d/next/Security/2022-11-08-12-06-52.gh-issue-99108.4Wrsuh.rst new file mode 100644 index 00000000000000..64acc09c482ecb --- /dev/null +++ b/Misc/NEWS.d/next/Security/2022-11-08-12-06-52.gh-issue-99108.4Wrsuh.rst @@ -0,0 +1,4 @@ +Replace the builtin :mod:`hashlib` implementations of SHA2-224 and SHA2-256 +originally from LibTomCrypt with formally verified, side-channel resistant +code from the `HACL* `_ project. The +builtins remain a fallback only used when OpenSSL does not provide them. diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index 3fd591c70d493f..f72783810f9415 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -79,7 +79,7 @@ # hashing builtins, can be disabled with --without-builtin-hashlib-hashes @MODULE__MD5_TRUE@_md5 md5module.c @MODULE__SHA1_TRUE@_sha1 sha1module.c -@MODULE__SHA256_TRUE@_sha256 sha256module.c +@MODULE__SHA256_TRUE@_sha256 sha256module.c _hacl/Hacl_Streaming_SHA2.c @MODULE__SHA512_TRUE@_sha512 sha512module.c @MODULE__SHA3_TRUE@_sha3 _sha3/sha3module.c @MODULE__BLAKE2_TRUE@_blake2 _blake2/blake2module.c _blake2/blake2b_impl.c _blake2/blake2s_impl.c diff --git a/Modules/_hacl/Hacl_Streaming_SHA2.c b/Modules/_hacl/Hacl_Streaming_SHA2.c new file mode 100644 index 00000000000000..84566571792a3c --- /dev/null +++ b/Modules/_hacl/Hacl_Streaming_SHA2.c @@ -0,0 +1,682 @@ +/* MIT License + * + * Copyright (c) 2016-2022 INRIA, CMU and Microsoft Corporation + * Copyright (c) 2022-2023 HACL* Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +#include "Hacl_Streaming_SHA2.h" + +#include "internal/Hacl_SHA2_Generic.h" + + +static inline void sha256_init(uint32_t *hash) +{ + KRML_MAYBE_FOR8(i, + (uint32_t)0U, + (uint32_t)8U, + (uint32_t)1U, + uint32_t *os = hash; + uint32_t x = Hacl_Impl_SHA2_Generic_h256[i]; + os[i] = x;); +} + +static inline void sha256_update0(uint8_t *b, uint32_t *hash) +{ + uint32_t hash_old[8U] = { 0U }; + uint32_t ws[16U] = { 0U }; + memcpy(hash_old, hash, (uint32_t)8U * sizeof (uint32_t)); + uint8_t *b10 = b; + uint32_t u = load32_be(b10); + ws[0U] = u; + uint32_t u0 = load32_be(b10 + (uint32_t)4U); + ws[1U] = u0; + uint32_t u1 = load32_be(b10 + (uint32_t)8U); + ws[2U] = u1; + uint32_t u2 = load32_be(b10 + (uint32_t)12U); + ws[3U] = u2; + uint32_t u3 = load32_be(b10 + (uint32_t)16U); + ws[4U] = u3; + uint32_t u4 = load32_be(b10 + (uint32_t)20U); + ws[5U] = u4; + uint32_t u5 = load32_be(b10 + (uint32_t)24U); + ws[6U] = u5; + uint32_t u6 = load32_be(b10 + (uint32_t)28U); + ws[7U] = u6; + uint32_t u7 = load32_be(b10 + (uint32_t)32U); + ws[8U] = u7; + uint32_t u8 = load32_be(b10 + (uint32_t)36U); + ws[9U] = u8; + uint32_t u9 = load32_be(b10 + (uint32_t)40U); + ws[10U] = u9; + uint32_t u10 = load32_be(b10 + (uint32_t)44U); + ws[11U] = u10; + uint32_t u11 = load32_be(b10 + (uint32_t)48U); + ws[12U] = u11; + uint32_t u12 = load32_be(b10 + (uint32_t)52U); + ws[13U] = u12; + uint32_t u13 = load32_be(b10 + (uint32_t)56U); + ws[14U] = u13; + uint32_t u14 = load32_be(b10 + (uint32_t)60U); + ws[15U] = u14; + KRML_MAYBE_FOR4(i0, + (uint32_t)0U, + (uint32_t)4U, + (uint32_t)1U, + KRML_MAYBE_FOR16(i, + (uint32_t)0U, + (uint32_t)16U, + (uint32_t)1U, + uint32_t k_t = Hacl_Impl_SHA2_Generic_k224_256[(uint32_t)16U * i0 + i]; + uint32_t ws_t = ws[i]; + uint32_t a0 = hash[0U]; + uint32_t b0 = hash[1U]; + uint32_t c0 = hash[2U]; + uint32_t d0 = hash[3U]; + uint32_t e0 = hash[4U]; + uint32_t f0 = hash[5U]; + uint32_t g0 = hash[6U]; + uint32_t h02 = hash[7U]; + uint32_t k_e_t = k_t; + uint32_t + t1 = + h02 + + + ((e0 << (uint32_t)26U | e0 >> (uint32_t)6U) + ^ + ((e0 << (uint32_t)21U | e0 >> (uint32_t)11U) + ^ (e0 << (uint32_t)7U | e0 >> (uint32_t)25U))) + + ((e0 & f0) ^ (~e0 & g0)) + + k_e_t + + ws_t; + uint32_t + t2 = + ((a0 << (uint32_t)30U | a0 >> (uint32_t)2U) + ^ + ((a0 << (uint32_t)19U | a0 >> (uint32_t)13U) + ^ (a0 << (uint32_t)10U | a0 >> (uint32_t)22U))) + + ((a0 & b0) ^ ((a0 & c0) ^ (b0 & c0))); + uint32_t a1 = t1 + t2; + uint32_t b1 = a0; + uint32_t c1 = b0; + uint32_t d1 = c0; + uint32_t e1 = d0 + t1; + uint32_t f1 = e0; + uint32_t g1 = f0; + uint32_t h12 = g0; + hash[0U] = a1; + hash[1U] = b1; + hash[2U] = c1; + hash[3U] = d1; + hash[4U] = e1; + hash[5U] = f1; + hash[6U] = g1; + hash[7U] = h12;); + if (i0 < (uint32_t)3U) + { + KRML_MAYBE_FOR16(i, + (uint32_t)0U, + (uint32_t)16U, + (uint32_t)1U, + uint32_t t16 = ws[i]; + uint32_t t15 = ws[(i + (uint32_t)1U) % (uint32_t)16U]; + uint32_t t7 = ws[(i + (uint32_t)9U) % (uint32_t)16U]; + uint32_t t2 = ws[(i + (uint32_t)14U) % (uint32_t)16U]; + uint32_t + s1 = + (t2 << (uint32_t)15U | t2 >> (uint32_t)17U) + ^ ((t2 << (uint32_t)13U | t2 >> (uint32_t)19U) ^ t2 >> (uint32_t)10U); + uint32_t + s0 = + (t15 << (uint32_t)25U | t15 >> (uint32_t)7U) + ^ ((t15 << (uint32_t)14U | t15 >> (uint32_t)18U) ^ t15 >> (uint32_t)3U); + ws[i] = s1 + t7 + s0 + t16;); + }); + KRML_MAYBE_FOR8(i, + (uint32_t)0U, + (uint32_t)8U, + (uint32_t)1U, + uint32_t *os = hash; + uint32_t x = hash[i] + hash_old[i]; + os[i] = x;); +} + +static inline void sha256_update_nblocks(uint32_t len, uint8_t *b, uint32_t *st) +{ + uint32_t blocks = len / (uint32_t)64U; + for (uint32_t i = (uint32_t)0U; i < blocks; i++) + { + uint8_t *b0 = b; + uint8_t *mb = b0 + i * (uint32_t)64U; + sha256_update0(mb, st); + } +} + +static inline void +sha256_update_last(uint64_t totlen, uint32_t len, uint8_t *b, uint32_t *hash) +{ + uint32_t blocks; + if (len + (uint32_t)8U + (uint32_t)1U <= (uint32_t)64U) + { + blocks = (uint32_t)1U; + } + else + { + blocks = (uint32_t)2U; + } + uint32_t fin = blocks * (uint32_t)64U; + uint8_t last[128U] = { 0U }; + uint8_t totlen_buf[8U] = { 0U }; + uint64_t total_len_bits = totlen << (uint32_t)3U; + store64_be(totlen_buf, total_len_bits); + uint8_t *b0 = b; + memcpy(last, b0, len * sizeof (uint8_t)); + last[len] = (uint8_t)0x80U; + memcpy(last + fin - (uint32_t)8U, totlen_buf, (uint32_t)8U * sizeof (uint8_t)); + uint8_t *last00 = last; + uint8_t *last10 = last + (uint32_t)64U; + uint8_t *l0 = last00; + uint8_t *l1 = last10; + uint8_t *lb0 = l0; + uint8_t *lb1 = l1; + uint8_t *last0 = lb0; + uint8_t *last1 = lb1; + sha256_update0(last0, hash); + if (blocks > (uint32_t)1U) + { + sha256_update0(last1, hash); + return; + } +} + +static inline void sha256_finish(uint32_t *st, uint8_t *h) +{ + uint8_t hbuf[32U] = { 0U }; + KRML_MAYBE_FOR8(i, + (uint32_t)0U, + (uint32_t)8U, + (uint32_t)1U, + store32_be(hbuf + i * (uint32_t)4U, st[i]);); + memcpy(h, hbuf, (uint32_t)32U * sizeof (uint8_t)); +} + +static inline void sha224_init(uint32_t *hash) +{ + KRML_MAYBE_FOR8(i, + (uint32_t)0U, + (uint32_t)8U, + (uint32_t)1U, + uint32_t *os = hash; + uint32_t x = Hacl_Impl_SHA2_Generic_h224[i]; + os[i] = x;); +} + +static inline void sha224_update_nblocks(uint32_t len, uint8_t *b, uint32_t *st) +{ + sha256_update_nblocks(len, b, st); +} + +static void sha224_update_last(uint64_t totlen, uint32_t len, uint8_t *b, uint32_t *st) +{ + sha256_update_last(totlen, len, b, st); +} + +static inline void sha224_finish(uint32_t *st, uint8_t *h) +{ + uint8_t hbuf[32U] = { 0U }; + KRML_MAYBE_FOR8(i, + (uint32_t)0U, + (uint32_t)8U, + (uint32_t)1U, + store32_be(hbuf + i * (uint32_t)4U, st[i]);); + memcpy(h, hbuf, (uint32_t)28U * sizeof (uint8_t)); +} + +/** +Allocate initial state for the SHA2_256 hash. The state is to be freed by +calling `free_256`. +*/ +Hacl_Streaming_SHA2_state_sha2_224 *Hacl_Streaming_SHA2_create_in_256(void) +{ + uint8_t *buf = (uint8_t *)KRML_HOST_CALLOC((uint32_t)64U, sizeof (uint8_t)); + uint32_t *block_state = (uint32_t *)KRML_HOST_CALLOC((uint32_t)8U, sizeof (uint32_t)); + Hacl_Streaming_SHA2_state_sha2_224 + s = { .block_state = block_state, .buf = buf, .total_len = (uint64_t)(uint32_t)0U }; + Hacl_Streaming_SHA2_state_sha2_224 + *p = + (Hacl_Streaming_SHA2_state_sha2_224 *)KRML_HOST_MALLOC(sizeof ( + Hacl_Streaming_SHA2_state_sha2_224 + )); + p[0U] = s; + sha256_init(block_state); + return p; +} + +/** +Copies the state passed as argument into a newly allocated state (deep copy). +The state is to be freed by calling `free_256`. Cloning the state this way is +useful, for instance, if your control-flow diverges and you need to feed +more (different) data into the hash in each branch. +*/ +Hacl_Streaming_SHA2_state_sha2_224 +*Hacl_Streaming_SHA2_copy_256(Hacl_Streaming_SHA2_state_sha2_224 *s0) +{ + Hacl_Streaming_SHA2_state_sha2_224 scrut = *s0; + uint32_t *block_state0 = scrut.block_state; + uint8_t *buf0 = scrut.buf; + uint64_t total_len0 = scrut.total_len; + uint8_t *buf = (uint8_t *)KRML_HOST_CALLOC((uint32_t)64U, sizeof (uint8_t)); + memcpy(buf, buf0, (uint32_t)64U * sizeof (uint8_t)); + uint32_t *block_state = (uint32_t *)KRML_HOST_CALLOC((uint32_t)8U, sizeof (uint32_t)); + memcpy(block_state, block_state0, (uint32_t)8U * sizeof (uint32_t)); + Hacl_Streaming_SHA2_state_sha2_224 + s = { .block_state = block_state, .buf = buf, .total_len = total_len0 }; + Hacl_Streaming_SHA2_state_sha2_224 + *p = + (Hacl_Streaming_SHA2_state_sha2_224 *)KRML_HOST_MALLOC(sizeof ( + Hacl_Streaming_SHA2_state_sha2_224 + )); + p[0U] = s; + return p; +} + +/** +Reset an existing state to the initial hash state with empty data. +*/ +void Hacl_Streaming_SHA2_init_256(Hacl_Streaming_SHA2_state_sha2_224 *s) +{ + Hacl_Streaming_SHA2_state_sha2_224 scrut = *s; + uint8_t *buf = scrut.buf; + uint32_t *block_state = scrut.block_state; + sha256_init(block_state); + Hacl_Streaming_SHA2_state_sha2_224 + tmp = { .block_state = block_state, .buf = buf, .total_len = (uint64_t)(uint32_t)0U }; + s[0U] = tmp; +} + +static inline uint32_t +update_224_256(Hacl_Streaming_SHA2_state_sha2_224 *p, uint8_t *data, uint32_t len) +{ + Hacl_Streaming_SHA2_state_sha2_224 s = *p; + uint64_t total_len = s.total_len; + if ((uint64_t)len > (uint64_t)2305843009213693951U - total_len) + { + return (uint32_t)1U; + } + uint32_t sz; + if (total_len % (uint64_t)(uint32_t)64U == (uint64_t)0U && total_len > (uint64_t)0U) + { + sz = (uint32_t)64U; + } + else + { + sz = (uint32_t)(total_len % (uint64_t)(uint32_t)64U); + } + if (len <= (uint32_t)64U - sz) + { + Hacl_Streaming_SHA2_state_sha2_224 s1 = *p; + uint32_t *block_state1 = s1.block_state; + uint8_t *buf = s1.buf; + uint64_t total_len1 = s1.total_len; + uint32_t sz1; + if (total_len1 % (uint64_t)(uint32_t)64U == (uint64_t)0U && total_len1 > (uint64_t)0U) + { + sz1 = (uint32_t)64U; + } + else + { + sz1 = (uint32_t)(total_len1 % (uint64_t)(uint32_t)64U); + } + uint8_t *buf2 = buf + sz1; + memcpy(buf2, data, len * sizeof (uint8_t)); + uint64_t total_len2 = total_len1 + (uint64_t)len; + *p + = + ( + (Hacl_Streaming_SHA2_state_sha2_224){ + .block_state = block_state1, + .buf = buf, + .total_len = total_len2 + } + ); + } + else if (sz == (uint32_t)0U) + { + Hacl_Streaming_SHA2_state_sha2_224 s1 = *p; + uint32_t *block_state1 = s1.block_state; + uint8_t *buf = s1.buf; + uint64_t total_len1 = s1.total_len; + uint32_t sz1; + if (total_len1 % (uint64_t)(uint32_t)64U == (uint64_t)0U && total_len1 > (uint64_t)0U) + { + sz1 = (uint32_t)64U; + } + else + { + sz1 = (uint32_t)(total_len1 % (uint64_t)(uint32_t)64U); + } + if (!(sz1 == (uint32_t)0U)) + { + sha256_update_nblocks((uint32_t)64U, buf, block_state1); + } + uint32_t ite; + if ((uint64_t)len % (uint64_t)(uint32_t)64U == (uint64_t)0U && (uint64_t)len > (uint64_t)0U) + { + ite = (uint32_t)64U; + } + else + { + ite = (uint32_t)((uint64_t)len % (uint64_t)(uint32_t)64U); + } + uint32_t n_blocks = (len - ite) / (uint32_t)64U; + uint32_t data1_len = n_blocks * (uint32_t)64U; + uint32_t data2_len = len - data1_len; + uint8_t *data1 = data; + uint8_t *data2 = data + data1_len; + sha256_update_nblocks(data1_len, data1, block_state1); + uint8_t *dst = buf; + memcpy(dst, data2, data2_len * sizeof (uint8_t)); + *p + = + ( + (Hacl_Streaming_SHA2_state_sha2_224){ + .block_state = block_state1, + .buf = buf, + .total_len = total_len1 + (uint64_t)len + } + ); + } + else + { + uint32_t diff = (uint32_t)64U - sz; + uint8_t *data1 = data; + uint8_t *data2 = data + diff; + Hacl_Streaming_SHA2_state_sha2_224 s1 = *p; + uint32_t *block_state10 = s1.block_state; + uint8_t *buf0 = s1.buf; + uint64_t total_len10 = s1.total_len; + uint32_t sz10; + if (total_len10 % (uint64_t)(uint32_t)64U == (uint64_t)0U && total_len10 > (uint64_t)0U) + { + sz10 = (uint32_t)64U; + } + else + { + sz10 = (uint32_t)(total_len10 % (uint64_t)(uint32_t)64U); + } + uint8_t *buf2 = buf0 + sz10; + memcpy(buf2, data1, diff * sizeof (uint8_t)); + uint64_t total_len2 = total_len10 + (uint64_t)diff; + *p + = + ( + (Hacl_Streaming_SHA2_state_sha2_224){ + .block_state = block_state10, + .buf = buf0, + .total_len = total_len2 + } + ); + Hacl_Streaming_SHA2_state_sha2_224 s10 = *p; + uint32_t *block_state1 = s10.block_state; + uint8_t *buf = s10.buf; + uint64_t total_len1 = s10.total_len; + uint32_t sz1; + if (total_len1 % (uint64_t)(uint32_t)64U == (uint64_t)0U && total_len1 > (uint64_t)0U) + { + sz1 = (uint32_t)64U; + } + else + { + sz1 = (uint32_t)(total_len1 % (uint64_t)(uint32_t)64U); + } + if (!(sz1 == (uint32_t)0U)) + { + sha256_update_nblocks((uint32_t)64U, buf, block_state1); + } + uint32_t ite; + if + ( + (uint64_t)(len - diff) + % (uint64_t)(uint32_t)64U + == (uint64_t)0U + && (uint64_t)(len - diff) > (uint64_t)0U + ) + { + ite = (uint32_t)64U; + } + else + { + ite = (uint32_t)((uint64_t)(len - diff) % (uint64_t)(uint32_t)64U); + } + uint32_t n_blocks = (len - diff - ite) / (uint32_t)64U; + uint32_t data1_len = n_blocks * (uint32_t)64U; + uint32_t data2_len = len - diff - data1_len; + uint8_t *data11 = data2; + uint8_t *data21 = data2 + data1_len; + sha256_update_nblocks(data1_len, data11, block_state1); + uint8_t *dst = buf; + memcpy(dst, data21, data2_len * sizeof (uint8_t)); + *p + = + ( + (Hacl_Streaming_SHA2_state_sha2_224){ + .block_state = block_state1, + .buf = buf, + .total_len = total_len1 + (uint64_t)(len - diff) + } + ); + } + return (uint32_t)0U; +} + +/** +Feed an arbitrary amount of data into the hash. This function returns 0 for +success, or 1 if the combined length of all of the data passed to `update_256` +(since the last call to `init_256`) exceeds 2^61-1 bytes. + +This function is identical to the update function for SHA2_224. +*/ +uint32_t +Hacl_Streaming_SHA2_update_256( + Hacl_Streaming_SHA2_state_sha2_224 *p, + uint8_t *input, + uint32_t input_len +) +{ + return update_224_256(p, input, input_len); +} + +/** +Write the resulting hash into `dst`, an array of 32 bytes. The state remains +valid after a call to `finish_256`, meaning the user may feed more data into +the hash via `update_256`. (The finish_256 function operates on an internal copy of +the state and therefore does not invalidate the client-held state `p`.) +*/ +void Hacl_Streaming_SHA2_finish_256(Hacl_Streaming_SHA2_state_sha2_224 *p, uint8_t *dst) +{ + Hacl_Streaming_SHA2_state_sha2_224 scrut = *p; + uint32_t *block_state = scrut.block_state; + uint8_t *buf_ = scrut.buf; + uint64_t total_len = scrut.total_len; + uint32_t r; + if (total_len % (uint64_t)(uint32_t)64U == (uint64_t)0U && total_len > (uint64_t)0U) + { + r = (uint32_t)64U; + } + else + { + r = (uint32_t)(total_len % (uint64_t)(uint32_t)64U); + } + uint8_t *buf_1 = buf_; + uint32_t tmp_block_state[8U] = { 0U }; + memcpy(tmp_block_state, block_state, (uint32_t)8U * sizeof (uint32_t)); + uint32_t ite; + if (r % (uint32_t)64U == (uint32_t)0U && r > (uint32_t)0U) + { + ite = (uint32_t)64U; + } + else + { + ite = r % (uint32_t)64U; + } + uint8_t *buf_last = buf_1 + r - ite; + uint8_t *buf_multi = buf_1; + sha256_update_nblocks((uint32_t)0U, buf_multi, tmp_block_state); + uint64_t prev_len_last = total_len - (uint64_t)r; + sha256_update_last(prev_len_last + (uint64_t)r, r, buf_last, tmp_block_state); + sha256_finish(tmp_block_state, dst); +} + +/** +Free a state allocated with `create_in_256`. + +This function is identical to the free function for SHA2_224. +*/ +void Hacl_Streaming_SHA2_free_256(Hacl_Streaming_SHA2_state_sha2_224 *s) +{ + Hacl_Streaming_SHA2_state_sha2_224 scrut = *s; + uint8_t *buf = scrut.buf; + uint32_t *block_state = scrut.block_state; + KRML_HOST_FREE(block_state); + KRML_HOST_FREE(buf); + KRML_HOST_FREE(s); +} + +/** +Hash `input`, of len `input_len`, into `dst`, an array of 32 bytes. +*/ +void Hacl_Streaming_SHA2_sha256(uint8_t *input, uint32_t input_len, uint8_t *dst) +{ + uint8_t *ib = input; + uint8_t *rb = dst; + uint32_t st[8U] = { 0U }; + sha256_init(st); + uint32_t rem = input_len % (uint32_t)64U; + uint64_t len_ = (uint64_t)input_len; + sha256_update_nblocks(input_len, ib, st); + uint32_t rem1 = input_len % (uint32_t)64U; + uint8_t *b0 = ib; + uint8_t *lb = b0 + input_len - rem1; + sha256_update_last(len_, rem, lb, st); + sha256_finish(st, rb); +} + +Hacl_Streaming_SHA2_state_sha2_224 *Hacl_Streaming_SHA2_create_in_224(void) +{ + uint8_t *buf = (uint8_t *)KRML_HOST_CALLOC((uint32_t)64U, sizeof (uint8_t)); + uint32_t *block_state = (uint32_t *)KRML_HOST_CALLOC((uint32_t)8U, sizeof (uint32_t)); + Hacl_Streaming_SHA2_state_sha2_224 + s = { .block_state = block_state, .buf = buf, .total_len = (uint64_t)(uint32_t)0U }; + Hacl_Streaming_SHA2_state_sha2_224 + *p = + (Hacl_Streaming_SHA2_state_sha2_224 *)KRML_HOST_MALLOC(sizeof ( + Hacl_Streaming_SHA2_state_sha2_224 + )); + p[0U] = s; + sha224_init(block_state); + return p; +} + +void Hacl_Streaming_SHA2_init_224(Hacl_Streaming_SHA2_state_sha2_224 *s) +{ + Hacl_Streaming_SHA2_state_sha2_224 scrut = *s; + uint8_t *buf = scrut.buf; + uint32_t *block_state = scrut.block_state; + sha224_init(block_state); + Hacl_Streaming_SHA2_state_sha2_224 + tmp = { .block_state = block_state, .buf = buf, .total_len = (uint64_t)(uint32_t)0U }; + s[0U] = tmp; +} + +uint32_t +Hacl_Streaming_SHA2_update_224( + Hacl_Streaming_SHA2_state_sha2_224 *p, + uint8_t *input, + uint32_t input_len +) +{ + return update_224_256(p, input, input_len); +} + +/** +Write the resulting hash into `dst`, an array of 28 bytes. The state remains +valid after a call to `finish_224`, meaning the user may feed more data into +the hash via `update_224`. +*/ +void Hacl_Streaming_SHA2_finish_224(Hacl_Streaming_SHA2_state_sha2_224 *p, uint8_t *dst) +{ + Hacl_Streaming_SHA2_state_sha2_224 scrut = *p; + uint32_t *block_state = scrut.block_state; + uint8_t *buf_ = scrut.buf; + uint64_t total_len = scrut.total_len; + uint32_t r; + if (total_len % (uint64_t)(uint32_t)64U == (uint64_t)0U && total_len > (uint64_t)0U) + { + r = (uint32_t)64U; + } + else + { + r = (uint32_t)(total_len % (uint64_t)(uint32_t)64U); + } + uint8_t *buf_1 = buf_; + uint32_t tmp_block_state[8U] = { 0U }; + memcpy(tmp_block_state, block_state, (uint32_t)8U * sizeof (uint32_t)); + uint32_t ite; + if (r % (uint32_t)64U == (uint32_t)0U && r > (uint32_t)0U) + { + ite = (uint32_t)64U; + } + else + { + ite = r % (uint32_t)64U; + } + uint8_t *buf_last = buf_1 + r - ite; + uint8_t *buf_multi = buf_1; + sha224_update_nblocks((uint32_t)0U, buf_multi, tmp_block_state); + uint64_t prev_len_last = total_len - (uint64_t)r; + sha224_update_last(prev_len_last + (uint64_t)r, r, buf_last, tmp_block_state); + sha224_finish(tmp_block_state, dst); +} + +void Hacl_Streaming_SHA2_free_224(Hacl_Streaming_SHA2_state_sha2_224 *p) +{ + Hacl_Streaming_SHA2_free_256(p); +} + +/** +Hash `input`, of len `input_len`, into `dst`, an array of 28 bytes. +*/ +void Hacl_Streaming_SHA2_sha224(uint8_t *input, uint32_t input_len, uint8_t *dst) +{ + uint8_t *ib = input; + uint8_t *rb = dst; + uint32_t st[8U] = { 0U }; + sha224_init(st); + uint32_t rem = input_len % (uint32_t)64U; + uint64_t len_ = (uint64_t)input_len; + sha224_update_nblocks(input_len, ib, st); + uint32_t rem1 = input_len % (uint32_t)64U; + uint8_t *b0 = ib; + uint8_t *lb = b0 + input_len - rem1; + sha224_update_last(len_, rem, lb, st); + sha224_finish(st, rb); +} + diff --git a/Modules/_hacl/Hacl_Streaming_SHA2.h b/Modules/_hacl/Hacl_Streaming_SHA2.h new file mode 100644 index 00000000000000..c83a835afe70fd --- /dev/null +++ b/Modules/_hacl/Hacl_Streaming_SHA2.h @@ -0,0 +1,136 @@ +/* MIT License + * + * Copyright (c) 2016-2022 INRIA, CMU and Microsoft Corporation + * Copyright (c) 2022-2023 HACL* Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +#ifndef __Hacl_Streaming_SHA2_H +#define __Hacl_Streaming_SHA2_H + +#if defined(__cplusplus) +extern "C" { +#endif + +#include +#include "python_hacl_namespaces.h" +#include "krml/FStar_UInt_8_16_32_64.h" +#include "krml/lowstar_endianness.h" +#include "krml/internal/target.h" + + + + +typedef struct Hacl_Streaming_SHA2_state_sha2_224_s +{ + uint32_t *block_state; + uint8_t *buf; + uint64_t total_len; +} +Hacl_Streaming_SHA2_state_sha2_224; + +typedef Hacl_Streaming_SHA2_state_sha2_224 Hacl_Streaming_SHA2_state_sha2_256; + +/** +Allocate initial state for the SHA2_256 hash. The state is to be freed by +calling `free_256`. +*/ +Hacl_Streaming_SHA2_state_sha2_224 *Hacl_Streaming_SHA2_create_in_256(void); + +/** +Copies the state passed as argument into a newly allocated state (deep copy). +The state is to be freed by calling `free_256`. Cloning the state this way is +useful, for instance, if your control-flow diverges and you need to feed +more (different) data into the hash in each branch. +*/ +Hacl_Streaming_SHA2_state_sha2_224 +*Hacl_Streaming_SHA2_copy_256(Hacl_Streaming_SHA2_state_sha2_224 *s0); + +/** +Reset an existing state to the initial hash state with empty data. +*/ +void Hacl_Streaming_SHA2_init_256(Hacl_Streaming_SHA2_state_sha2_224 *s); + +/** +Feed an arbitrary amount of data into the hash. This function returns 0 for +success, or 1 if the combined length of all of the data passed to `update_256` +(since the last call to `init_256`) exceeds 2^61-1 bytes. + +This function is identical to the update function for SHA2_224. +*/ +uint32_t +Hacl_Streaming_SHA2_update_256( + Hacl_Streaming_SHA2_state_sha2_224 *p, + uint8_t *input, + uint32_t input_len +); + +/** +Write the resulting hash into `dst`, an array of 32 bytes. The state remains +valid after a call to `finish_256`, meaning the user may feed more data into +the hash via `update_256`. (The finish_256 function operates on an internal copy of +the state and therefore does not invalidate the client-held state `p`.) +*/ +void Hacl_Streaming_SHA2_finish_256(Hacl_Streaming_SHA2_state_sha2_224 *p, uint8_t *dst); + +/** +Free a state allocated with `create_in_256`. + +This function is identical to the free function for SHA2_224. +*/ +void Hacl_Streaming_SHA2_free_256(Hacl_Streaming_SHA2_state_sha2_224 *s); + +/** +Hash `input`, of len `input_len`, into `dst`, an array of 32 bytes. +*/ +void Hacl_Streaming_SHA2_sha256(uint8_t *input, uint32_t input_len, uint8_t *dst); + +Hacl_Streaming_SHA2_state_sha2_224 *Hacl_Streaming_SHA2_create_in_224(void); + +void Hacl_Streaming_SHA2_init_224(Hacl_Streaming_SHA2_state_sha2_224 *s); + +uint32_t +Hacl_Streaming_SHA2_update_224( + Hacl_Streaming_SHA2_state_sha2_224 *p, + uint8_t *input, + uint32_t input_len +); + +/** +Write the resulting hash into `dst`, an array of 28 bytes. The state remains +valid after a call to `finish_224`, meaning the user may feed more data into +the hash via `update_224`. +*/ +void Hacl_Streaming_SHA2_finish_224(Hacl_Streaming_SHA2_state_sha2_224 *p, uint8_t *dst); + +void Hacl_Streaming_SHA2_free_224(Hacl_Streaming_SHA2_state_sha2_224 *p); + +/** +Hash `input`, of len `input_len`, into `dst`, an array of 28 bytes. +*/ +void Hacl_Streaming_SHA2_sha224(uint8_t *input, uint32_t input_len, uint8_t *dst); + +#if defined(__cplusplus) +} +#endif + +#define __Hacl_Streaming_SHA2_H_DEFINED +#endif diff --git a/Modules/_hacl/README.md b/Modules/_hacl/README.md new file mode 100644 index 00000000000000..e6a156a54b3cee --- /dev/null +++ b/Modules/_hacl/README.md @@ -0,0 +1,29 @@ +# Algorithm implementations used by the `hashlib` module. + +This code comes from the +[HACL\*](https://github.com/hacl-star/hacl-star/) project. + +HACL\* is a cryptographic library that has been formally verified for memory +safety, functional correctness, and secret independence. + +## Updating HACL* + +Use the `refresh.sh` script in this directory to pull in a new upstream code +version. The upstream git hash used for the most recent code pull is recorded +in the script. Modify the script as needed to bring in more if changes are +needed based on upstream code refactoring. + +Never manually edit HACL\* files. Always add transformation shell code to the +`refresh.sh` script to perform any necessary edits. If there are serious code +changes needed, work with the upstream repository. + +## Local files + +1. `./include/python_hacl_namespaces.h` +1. `./README.md` +1. `./refresh.sh` + +## ACKS + +* Jonathan Protzenko aka [@msprotz on Github](https://github.com/msprotz) +contributed our HACL\* based builtin code. diff --git a/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h b/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h new file mode 100644 index 00000000000000..3e2e4b32b22f96 --- /dev/null +++ b/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h @@ -0,0 +1,109 @@ +/* + Copyright (c) INRIA and Microsoft Corporation. All rights reserved. + Licensed under the Apache 2.0 License. +*/ + + +#ifndef __FStar_UInt_8_16_32_64_H +#define __FStar_UInt_8_16_32_64_H + + + + +#include +#include + +#include "krml/lowstar_endianness.h" +#include "krml/FStar_UInt_8_16_32_64.h" +#include "krml/internal/target.h" +static inline uint64_t FStar_UInt64_eq_mask(uint64_t a, uint64_t b) +{ + uint64_t x = a ^ b; + uint64_t minus_x = ~x + (uint64_t)1U; + uint64_t x_or_minus_x = x | minus_x; + uint64_t xnx = x_or_minus_x >> (uint32_t)63U; + return xnx - (uint64_t)1U; +} + +static inline uint64_t FStar_UInt64_gte_mask(uint64_t a, uint64_t b) +{ + uint64_t x = a; + uint64_t y = b; + uint64_t x_xor_y = x ^ y; + uint64_t x_sub_y = x - y; + uint64_t x_sub_y_xor_y = x_sub_y ^ y; + uint64_t q = x_xor_y | x_sub_y_xor_y; + uint64_t x_xor_q = x ^ q; + uint64_t x_xor_q_ = x_xor_q >> (uint32_t)63U; + return x_xor_q_ - (uint64_t)1U; +} + +static inline uint32_t FStar_UInt32_eq_mask(uint32_t a, uint32_t b) +{ + uint32_t x = a ^ b; + uint32_t minus_x = ~x + (uint32_t)1U; + uint32_t x_or_minus_x = x | minus_x; + uint32_t xnx = x_or_minus_x >> (uint32_t)31U; + return xnx - (uint32_t)1U; +} + +static inline uint32_t FStar_UInt32_gte_mask(uint32_t a, uint32_t b) +{ + uint32_t x = a; + uint32_t y = b; + uint32_t x_xor_y = x ^ y; + uint32_t x_sub_y = x - y; + uint32_t x_sub_y_xor_y = x_sub_y ^ y; + uint32_t q = x_xor_y | x_sub_y_xor_y; + uint32_t x_xor_q = x ^ q; + uint32_t x_xor_q_ = x_xor_q >> (uint32_t)31U; + return x_xor_q_ - (uint32_t)1U; +} + +static inline uint16_t FStar_UInt16_eq_mask(uint16_t a, uint16_t b) +{ + uint16_t x = a ^ b; + uint16_t minus_x = ~x + (uint16_t)1U; + uint16_t x_or_minus_x = x | minus_x; + uint16_t xnx = x_or_minus_x >> (uint32_t)15U; + return xnx - (uint16_t)1U; +} + +static inline uint16_t FStar_UInt16_gte_mask(uint16_t a, uint16_t b) +{ + uint16_t x = a; + uint16_t y = b; + uint16_t x_xor_y = x ^ y; + uint16_t x_sub_y = x - y; + uint16_t x_sub_y_xor_y = x_sub_y ^ y; + uint16_t q = x_xor_y | x_sub_y_xor_y; + uint16_t x_xor_q = x ^ q; + uint16_t x_xor_q_ = x_xor_q >> (uint32_t)15U; + return x_xor_q_ - (uint16_t)1U; +} + +static inline uint8_t FStar_UInt8_eq_mask(uint8_t a, uint8_t b) +{ + uint8_t x = a ^ b; + uint8_t minus_x = ~x + (uint8_t)1U; + uint8_t x_or_minus_x = x | minus_x; + uint8_t xnx = x_or_minus_x >> (uint32_t)7U; + return xnx - (uint8_t)1U; +} + +static inline uint8_t FStar_UInt8_gte_mask(uint8_t a, uint8_t b) +{ + uint8_t x = a; + uint8_t y = b; + uint8_t x_xor_y = x ^ y; + uint8_t x_sub_y = x - y; + uint8_t x_sub_y_xor_y = x_sub_y ^ y; + uint8_t q = x_xor_y | x_sub_y_xor_y; + uint8_t x_xor_q = x ^ q; + uint8_t x_xor_q_ = x_xor_q >> (uint32_t)7U; + return x_xor_q_ - (uint8_t)1U; +} + + +#define __FStar_UInt_8_16_32_64_H_DEFINED +#endif diff --git a/Modules/_hacl/include/krml/internal/target.h b/Modules/_hacl/include/krml/internal/target.h new file mode 100644 index 00000000000000..9ef59859a554b5 --- /dev/null +++ b/Modules/_hacl/include/krml/internal/target.h @@ -0,0 +1,218 @@ +/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. + Licensed under the Apache 2.0 License. */ + +#ifndef __KRML_TARGET_H +#define __KRML_TARGET_H + +#include +#include +#include +#include +#include +#include +#include + +/* Since KaRaMeL emits the inline keyword unconditionally, we follow the + * guidelines at https://gcc.gnu.org/onlinedocs/gcc/Inline.html and make this + * __inline__ to ensure the code compiles with -std=c90 and earlier. */ +#ifdef __GNUC__ +# define inline __inline__ +#endif + +#ifndef KRML_HOST_MALLOC +# define KRML_HOST_MALLOC malloc +#endif + +#ifndef KRML_HOST_CALLOC +# define KRML_HOST_CALLOC calloc +#endif + +#ifndef KRML_HOST_FREE +# define KRML_HOST_FREE free +#endif + +/* Macros for prettier unrolling of loops */ +#define KRML_LOOP1(i, n, x) { \ + x \ + i += n; \ +} + +#define KRML_LOOP2(i, n, x) \ + KRML_LOOP1(i, n, x) \ + KRML_LOOP1(i, n, x) + +#define KRML_LOOP3(i, n, x) \ + KRML_LOOP2(i, n, x) \ + KRML_LOOP1(i, n, x) + +#define KRML_LOOP4(i, n, x) \ + KRML_LOOP2(i, n, x) \ + KRML_LOOP2(i, n, x) + +#define KRML_LOOP5(i, n, x) \ + KRML_LOOP4(i, n, x) \ + KRML_LOOP1(i, n, x) + +#define KRML_LOOP6(i, n, x) \ + KRML_LOOP4(i, n, x) \ + KRML_LOOP2(i, n, x) + +#define KRML_LOOP7(i, n, x) \ + KRML_LOOP4(i, n, x) \ + KRML_LOOP3(i, n, x) + +#define KRML_LOOP8(i, n, x) \ + KRML_LOOP4(i, n, x) \ + KRML_LOOP4(i, n, x) + +#define KRML_LOOP9(i, n, x) \ + KRML_LOOP8(i, n, x) \ + KRML_LOOP1(i, n, x) + +#define KRML_LOOP10(i, n, x) \ + KRML_LOOP8(i, n, x) \ + KRML_LOOP2(i, n, x) + +#define KRML_LOOP11(i, n, x) \ + KRML_LOOP8(i, n, x) \ + KRML_LOOP3(i, n, x) + +#define KRML_LOOP12(i, n, x) \ + KRML_LOOP8(i, n, x) \ + KRML_LOOP4(i, n, x) + +#define KRML_LOOP13(i, n, x) \ + KRML_LOOP8(i, n, x) \ + KRML_LOOP5(i, n, x) + +#define KRML_LOOP14(i, n, x) \ + KRML_LOOP8(i, n, x) \ + KRML_LOOP6(i, n, x) + +#define KRML_LOOP15(i, n, x) \ + KRML_LOOP8(i, n, x) \ + KRML_LOOP7(i, n, x) + +#define KRML_LOOP16(i, n, x) \ + KRML_LOOP8(i, n, x) \ + KRML_LOOP8(i, n, x) + +#define KRML_UNROLL_FOR(i, z, n, k, x) do { \ + uint32_t i = z; \ + KRML_LOOP##n(i, k, x) \ +} while (0) + +#define KRML_ACTUAL_FOR(i, z, n, k, x) \ + do { \ + for (uint32_t i = z; i < n; i += k) { \ + x \ + } \ + } while (0) + +#ifndef KRML_UNROLL_MAX +#define KRML_UNROLL_MAX 16 +#endif + +/* 1 is the number of loop iterations, i.e. (n - z)/k as evaluated by krml */ +#if 0 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR0(i, z, n, k, x) +#else +#define KRML_MAYBE_FOR0(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif + +#if 1 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR1(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 1, k, x) +#else +#define KRML_MAYBE_FOR1(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif + +#if 2 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR2(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 2, k, x) +#else +#define KRML_MAYBE_FOR2(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif + +#if 3 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR3(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 3, k, x) +#else +#define KRML_MAYBE_FOR3(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif + +#if 4 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR4(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 4, k, x) +#else +#define KRML_MAYBE_FOR4(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif + +#if 5 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR5(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 5, k, x) +#else +#define KRML_MAYBE_FOR5(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif + +#if 6 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR6(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 6, k, x) +#else +#define KRML_MAYBE_FOR6(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif + +#if 7 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR7(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 7, k, x) +#else +#define KRML_MAYBE_FOR7(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif + +#if 8 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR8(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 8, k, x) +#else +#define KRML_MAYBE_FOR8(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif + +#if 9 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR9(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 9, k, x) +#else +#define KRML_MAYBE_FOR9(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif + +#if 10 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR10(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 10, k, x) +#else +#define KRML_MAYBE_FOR10(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif + +#if 11 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR11(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 11, k, x) +#else +#define KRML_MAYBE_FOR11(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif + +#if 12 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR12(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 12, k, x) +#else +#define KRML_MAYBE_FOR12(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif + +#if 13 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR13(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 13, k, x) +#else +#define KRML_MAYBE_FOR13(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif + +#if 14 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR14(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 14, k, x) +#else +#define KRML_MAYBE_FOR14(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif + +#if 15 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR15(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 15, k, x) +#else +#define KRML_MAYBE_FOR15(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif + +#if 16 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR16(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 16, k, x) +#else +#define KRML_MAYBE_FOR16(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif +#endif diff --git a/Modules/_hacl/include/krml/lowstar_endianness.h b/Modules/_hacl/include/krml/lowstar_endianness.h new file mode 100644 index 00000000000000..32a7391e817ebb --- /dev/null +++ b/Modules/_hacl/include/krml/lowstar_endianness.h @@ -0,0 +1,230 @@ +/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. + Licensed under the Apache 2.0 License. */ + +#ifndef __LOWSTAR_ENDIANNESS_H +#define __LOWSTAR_ENDIANNESS_H + +#include +#include + +/******************************************************************************/ +/* Implementing C.fst (part 2: endian-ness macros) */ +/******************************************************************************/ + +/* ... for Linux */ +#if defined(__linux__) || defined(__CYGWIN__) || defined (__USE_SYSTEM_ENDIAN_H__) || defined(__GLIBC__) +# include + +/* ... for OSX */ +#elif defined(__APPLE__) +# include +# define htole64(x) OSSwapHostToLittleInt64(x) +# define le64toh(x) OSSwapLittleToHostInt64(x) +# define htobe64(x) OSSwapHostToBigInt64(x) +# define be64toh(x) OSSwapBigToHostInt64(x) + +# define htole16(x) OSSwapHostToLittleInt16(x) +# define le16toh(x) OSSwapLittleToHostInt16(x) +# define htobe16(x) OSSwapHostToBigInt16(x) +# define be16toh(x) OSSwapBigToHostInt16(x) + +# define htole32(x) OSSwapHostToLittleInt32(x) +# define le32toh(x) OSSwapLittleToHostInt32(x) +# define htobe32(x) OSSwapHostToBigInt32(x) +# define be32toh(x) OSSwapBigToHostInt32(x) + +/* ... for Solaris */ +#elif defined(__sun__) +# include +# define htole64(x) LE_64(x) +# define le64toh(x) LE_64(x) +# define htobe64(x) BE_64(x) +# define be64toh(x) BE_64(x) + +# define htole16(x) LE_16(x) +# define le16toh(x) LE_16(x) +# define htobe16(x) BE_16(x) +# define be16toh(x) BE_16(x) + +# define htole32(x) LE_32(x) +# define le32toh(x) LE_32(x) +# define htobe32(x) BE_32(x) +# define be32toh(x) BE_32(x) + +/* ... for the BSDs */ +#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) +# include +#elif defined(__OpenBSD__) +# include + +/* ... for Windows (MSVC)... not targeting XBOX 360! */ +#elif defined(_MSC_VER) + +# include +# define htobe16(x) _byteswap_ushort(x) +# define htole16(x) (x) +# define be16toh(x) _byteswap_ushort(x) +# define le16toh(x) (x) + +# define htobe32(x) _byteswap_ulong(x) +# define htole32(x) (x) +# define be32toh(x) _byteswap_ulong(x) +# define le32toh(x) (x) + +# define htobe64(x) _byteswap_uint64(x) +# define htole64(x) (x) +# define be64toh(x) _byteswap_uint64(x) +# define le64toh(x) (x) + +/* ... for Windows (GCC-like, e.g. mingw or clang) */ +#elif (defined(_WIN32) || defined(_WIN64)) && \ + (defined(__GNUC__) || defined(__clang__)) + +# define htobe16(x) __builtin_bswap16(x) +# define htole16(x) (x) +# define be16toh(x) __builtin_bswap16(x) +# define le16toh(x) (x) + +# define htobe32(x) __builtin_bswap32(x) +# define htole32(x) (x) +# define be32toh(x) __builtin_bswap32(x) +# define le32toh(x) (x) + +# define htobe64(x) __builtin_bswap64(x) +# define htole64(x) (x) +# define be64toh(x) __builtin_bswap64(x) +# define le64toh(x) (x) + +/* ... generic big-endian fallback code */ +#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + +/* byte swapping code inspired by: + * https://github.com/rweather/arduinolibs/blob/master/libraries/Crypto/utility/EndianUtil.h + * */ + +# define htobe32(x) (x) +# define be32toh(x) (x) +# define htole32(x) \ + (__extension__({ \ + uint32_t _temp = (x); \ + ((_temp >> 24) & 0x000000FF) | ((_temp >> 8) & 0x0000FF00) | \ + ((_temp << 8) & 0x00FF0000) | ((_temp << 24) & 0xFF000000); \ + })) +# define le32toh(x) (htole32((x))) + +# define htobe64(x) (x) +# define be64toh(x) (x) +# define htole64(x) \ + (__extension__({ \ + uint64_t __temp = (x); \ + uint32_t __low = htobe32((uint32_t)__temp); \ + uint32_t __high = htobe32((uint32_t)(__temp >> 32)); \ + (((uint64_t)__low) << 32) | __high; \ + })) +# define le64toh(x) (htole64((x))) + +/* ... generic little-endian fallback code */ +#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + +# define htole32(x) (x) +# define le32toh(x) (x) +# define htobe32(x) \ + (__extension__({ \ + uint32_t _temp = (x); \ + ((_temp >> 24) & 0x000000FF) | ((_temp >> 8) & 0x0000FF00) | \ + ((_temp << 8) & 0x00FF0000) | ((_temp << 24) & 0xFF000000); \ + })) +# define be32toh(x) (htobe32((x))) + +# define htole64(x) (x) +# define le64toh(x) (x) +# define htobe64(x) \ + (__extension__({ \ + uint64_t __temp = (x); \ + uint32_t __low = htobe32((uint32_t)__temp); \ + uint32_t __high = htobe32((uint32_t)(__temp >> 32)); \ + (((uint64_t)__low) << 32) | __high; \ + })) +# define be64toh(x) (htobe64((x))) + +/* ... couldn't determine endian-ness of the target platform */ +#else +# error "Please define __BYTE_ORDER__!" + +#endif /* defined(__linux__) || ... */ + +/* Loads and stores. These avoid undefined behavior due to unaligned memory + * accesses, via memcpy. */ + +inline static uint16_t load16(uint8_t *b) { + uint16_t x; + memcpy(&x, b, 2); + return x; +} + +inline static uint32_t load32(uint8_t *b) { + uint32_t x; + memcpy(&x, b, 4); + return x; +} + +inline static uint64_t load64(uint8_t *b) { + uint64_t x; + memcpy(&x, b, 8); + return x; +} + +inline static void store16(uint8_t *b, uint16_t i) { + memcpy(b, &i, 2); +} + +inline static void store32(uint8_t *b, uint32_t i) { + memcpy(b, &i, 4); +} + +inline static void store64(uint8_t *b, uint64_t i) { + memcpy(b, &i, 8); +} + +/* Legacy accessors so that this header can serve as an implementation of + * C.Endianness */ +#define load16_le(b) (le16toh(load16(b))) +#define store16_le(b, i) (store16(b, htole16(i))) +#define load16_be(b) (be16toh(load16(b))) +#define store16_be(b, i) (store16(b, htobe16(i))) + +#define load32_le(b) (le32toh(load32(b))) +#define store32_le(b, i) (store32(b, htole32(i))) +#define load32_be(b) (be32toh(load32(b))) +#define store32_be(b, i) (store32(b, htobe32(i))) + +#define load64_le(b) (le64toh(load64(b))) +#define store64_le(b, i) (store64(b, htole64(i))) +#define load64_be(b) (be64toh(load64(b))) +#define store64_be(b, i) (store64(b, htobe64(i))) + +/* Co-existence of LowStar.Endianness and FStar.Endianness generates name + * conflicts, because of course both insist on having no prefixes. Until a + * prefix is added, or until we truly retire FStar.Endianness, solve this issue + * in an elegant way. */ +#define load16_le0 load16_le +#define store16_le0 store16_le +#define load16_be0 load16_be +#define store16_be0 store16_be + +#define load32_le0 load32_le +#define store32_le0 store32_le +#define load32_be0 load32_be +#define store32_be0 store32_be + +#define load64_le0 load64_le +#define store64_le0 store64_le +#define load64_be0 load64_be +#define store64_be0 store64_be + +#define load128_le0 load128_le +#define store128_le0 store128_le +#define load128_be0 load128_be +#define store128_be0 store128_be + +#endif diff --git a/Modules/_hacl/include/python_hacl_namespaces.h b/Modules/_hacl/include/python_hacl_namespaces.h new file mode 100644 index 00000000000000..af390459311fe8 --- /dev/null +++ b/Modules/_hacl/include/python_hacl_namespaces.h @@ -0,0 +1,28 @@ +#ifndef _PYTHON_HACL_NAMESPACES_H +#define _PYTHON_HACL_NAMESPACES_H + +/* + * C's excuse for namespaces: Use globally unique names to avoid linkage + * conflicts with builds linking or dynamically loading other code potentially + * using HACL* libraries. + */ + +#define Hacl_Streaming_SHA2_state_sha2_224_s python_hashlib_Hacl_Streaming_SHA2_state_sha2_224_s +#define Hacl_Streaming_SHA2_state_sha2_224 python_hashlib_Hacl_Streaming_SHA2_state_sha2_224 +#define Hacl_Streaming_SHA2_state_sha2_256 python_hashlib_Hacl_Streaming_SHA2_state_sha2_256 +#define Hacl_Streaming_SHA2_create_in_256 python_hashlib_Hacl_Streaming_SHA2_create_in_256 +#define Hacl_Streaming_SHA2_create_in_224 python_hashlib_Hacl_Streaming_SHA2_create_in_224 +#define Hacl_Streaming_SHA2_copy_256 python_hashlib_Hacl_Streaming_SHA2_copy_256 +#define Hacl_Streaming_SHA2_copy_224 python_hashlib_Hacl_Streaming_SHA2_copy_224 +#define Hacl_Streaming_SHA2_init_256 python_hashlib_Hacl_Streaming_SHA2_init_256 +#define Hacl_Streaming_SHA2_init_224 python_hashlib_Hacl_Streaming_SHA2_init_224 +#define Hacl_Streaming_SHA2_update_256 python_hashlib_Hacl_Streaming_SHA2_update_256 +#define Hacl_Streaming_SHA2_update_224 python_hashlib_Hacl_Streaming_SHA2_update_224 +#define Hacl_Streaming_SHA2_finish_256 python_hashlib_Hacl_Streaming_SHA2_finish_256 +#define Hacl_Streaming_SHA2_finish_224 python_hashlib_Hacl_Streaming_SHA2_finish_224 +#define Hacl_Streaming_SHA2_free_256 python_hashlib_Hacl_Streaming_SHA2_free_256 +#define Hacl_Streaming_SHA2_free_224 python_hashlib_Hacl_Streaming_SHA2_free_224 +#define Hacl_Streaming_SHA2_sha256 python_hashlib_Hacl_Streaming_SHA2_sha256 +#define Hacl_Streaming_SHA2_sha224 python_hashlib_Hacl_Streaming_SHA2_sha224 + +#endif // _PYTHON_HACL_NAMESPACES_H diff --git a/Modules/_hacl/internal/Hacl_SHA2_Generic.h b/Modules/_hacl/internal/Hacl_SHA2_Generic.h new file mode 100644 index 00000000000000..23f7cea1eb3884 --- /dev/null +++ b/Modules/_hacl/internal/Hacl_SHA2_Generic.h @@ -0,0 +1,135 @@ +/* MIT License + * + * Copyright (c) 2016-2022 INRIA, CMU and Microsoft Corporation + * Copyright (c) 2022-2023 HACL* Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +#ifndef __internal_Hacl_SHA2_Generic_H +#define __internal_Hacl_SHA2_Generic_H + +#if defined(__cplusplus) +extern "C" { +#endif + +#include +#include "krml/FStar_UInt_8_16_32_64.h" +#include "krml/lowstar_endianness.h" +#include "krml/internal/target.h" + + + + +static const +uint32_t +Hacl_Impl_SHA2_Generic_h224[8U] = + { + (uint32_t)0xc1059ed8U, (uint32_t)0x367cd507U, (uint32_t)0x3070dd17U, (uint32_t)0xf70e5939U, + (uint32_t)0xffc00b31U, (uint32_t)0x68581511U, (uint32_t)0x64f98fa7U, (uint32_t)0xbefa4fa4U + }; + +static const +uint32_t +Hacl_Impl_SHA2_Generic_h256[8U] = + { + (uint32_t)0x6a09e667U, (uint32_t)0xbb67ae85U, (uint32_t)0x3c6ef372U, (uint32_t)0xa54ff53aU, + (uint32_t)0x510e527fU, (uint32_t)0x9b05688cU, (uint32_t)0x1f83d9abU, (uint32_t)0x5be0cd19U + }; + +static const +uint64_t +Hacl_Impl_SHA2_Generic_h384[8U] = + { + (uint64_t)0xcbbb9d5dc1059ed8U, (uint64_t)0x629a292a367cd507U, (uint64_t)0x9159015a3070dd17U, + (uint64_t)0x152fecd8f70e5939U, (uint64_t)0x67332667ffc00b31U, (uint64_t)0x8eb44a8768581511U, + (uint64_t)0xdb0c2e0d64f98fa7U, (uint64_t)0x47b5481dbefa4fa4U + }; + +static const +uint64_t +Hacl_Impl_SHA2_Generic_h512[8U] = + { + (uint64_t)0x6a09e667f3bcc908U, (uint64_t)0xbb67ae8584caa73bU, (uint64_t)0x3c6ef372fe94f82bU, + (uint64_t)0xa54ff53a5f1d36f1U, (uint64_t)0x510e527fade682d1U, (uint64_t)0x9b05688c2b3e6c1fU, + (uint64_t)0x1f83d9abfb41bd6bU, (uint64_t)0x5be0cd19137e2179U + }; + +static const +uint32_t +Hacl_Impl_SHA2_Generic_k224_256[64U] = + { + (uint32_t)0x428a2f98U, (uint32_t)0x71374491U, (uint32_t)0xb5c0fbcfU, (uint32_t)0xe9b5dba5U, + (uint32_t)0x3956c25bU, (uint32_t)0x59f111f1U, (uint32_t)0x923f82a4U, (uint32_t)0xab1c5ed5U, + (uint32_t)0xd807aa98U, (uint32_t)0x12835b01U, (uint32_t)0x243185beU, (uint32_t)0x550c7dc3U, + (uint32_t)0x72be5d74U, (uint32_t)0x80deb1feU, (uint32_t)0x9bdc06a7U, (uint32_t)0xc19bf174U, + (uint32_t)0xe49b69c1U, (uint32_t)0xefbe4786U, (uint32_t)0x0fc19dc6U, (uint32_t)0x240ca1ccU, + (uint32_t)0x2de92c6fU, (uint32_t)0x4a7484aaU, (uint32_t)0x5cb0a9dcU, (uint32_t)0x76f988daU, + (uint32_t)0x983e5152U, (uint32_t)0xa831c66dU, (uint32_t)0xb00327c8U, (uint32_t)0xbf597fc7U, + (uint32_t)0xc6e00bf3U, (uint32_t)0xd5a79147U, (uint32_t)0x06ca6351U, (uint32_t)0x14292967U, + (uint32_t)0x27b70a85U, (uint32_t)0x2e1b2138U, (uint32_t)0x4d2c6dfcU, (uint32_t)0x53380d13U, + (uint32_t)0x650a7354U, (uint32_t)0x766a0abbU, (uint32_t)0x81c2c92eU, (uint32_t)0x92722c85U, + (uint32_t)0xa2bfe8a1U, (uint32_t)0xa81a664bU, (uint32_t)0xc24b8b70U, (uint32_t)0xc76c51a3U, + (uint32_t)0xd192e819U, (uint32_t)0xd6990624U, (uint32_t)0xf40e3585U, (uint32_t)0x106aa070U, + (uint32_t)0x19a4c116U, (uint32_t)0x1e376c08U, (uint32_t)0x2748774cU, (uint32_t)0x34b0bcb5U, + (uint32_t)0x391c0cb3U, (uint32_t)0x4ed8aa4aU, (uint32_t)0x5b9cca4fU, (uint32_t)0x682e6ff3U, + (uint32_t)0x748f82eeU, (uint32_t)0x78a5636fU, (uint32_t)0x84c87814U, (uint32_t)0x8cc70208U, + (uint32_t)0x90befffaU, (uint32_t)0xa4506cebU, (uint32_t)0xbef9a3f7U, (uint32_t)0xc67178f2U + }; + +static const +uint64_t +Hacl_Impl_SHA2_Generic_k384_512[80U] = + { + (uint64_t)0x428a2f98d728ae22U, (uint64_t)0x7137449123ef65cdU, (uint64_t)0xb5c0fbcfec4d3b2fU, + (uint64_t)0xe9b5dba58189dbbcU, (uint64_t)0x3956c25bf348b538U, (uint64_t)0x59f111f1b605d019U, + (uint64_t)0x923f82a4af194f9bU, (uint64_t)0xab1c5ed5da6d8118U, (uint64_t)0xd807aa98a3030242U, + (uint64_t)0x12835b0145706fbeU, (uint64_t)0x243185be4ee4b28cU, (uint64_t)0x550c7dc3d5ffb4e2U, + (uint64_t)0x72be5d74f27b896fU, (uint64_t)0x80deb1fe3b1696b1U, (uint64_t)0x9bdc06a725c71235U, + (uint64_t)0xc19bf174cf692694U, (uint64_t)0xe49b69c19ef14ad2U, (uint64_t)0xefbe4786384f25e3U, + (uint64_t)0x0fc19dc68b8cd5b5U, (uint64_t)0x240ca1cc77ac9c65U, (uint64_t)0x2de92c6f592b0275U, + (uint64_t)0x4a7484aa6ea6e483U, (uint64_t)0x5cb0a9dcbd41fbd4U, (uint64_t)0x76f988da831153b5U, + (uint64_t)0x983e5152ee66dfabU, (uint64_t)0xa831c66d2db43210U, (uint64_t)0xb00327c898fb213fU, + (uint64_t)0xbf597fc7beef0ee4U, (uint64_t)0xc6e00bf33da88fc2U, (uint64_t)0xd5a79147930aa725U, + (uint64_t)0x06ca6351e003826fU, (uint64_t)0x142929670a0e6e70U, (uint64_t)0x27b70a8546d22ffcU, + (uint64_t)0x2e1b21385c26c926U, (uint64_t)0x4d2c6dfc5ac42aedU, (uint64_t)0x53380d139d95b3dfU, + (uint64_t)0x650a73548baf63deU, (uint64_t)0x766a0abb3c77b2a8U, (uint64_t)0x81c2c92e47edaee6U, + (uint64_t)0x92722c851482353bU, (uint64_t)0xa2bfe8a14cf10364U, (uint64_t)0xa81a664bbc423001U, + (uint64_t)0xc24b8b70d0f89791U, (uint64_t)0xc76c51a30654be30U, (uint64_t)0xd192e819d6ef5218U, + (uint64_t)0xd69906245565a910U, (uint64_t)0xf40e35855771202aU, (uint64_t)0x106aa07032bbd1b8U, + (uint64_t)0x19a4c116b8d2d0c8U, (uint64_t)0x1e376c085141ab53U, (uint64_t)0x2748774cdf8eeb99U, + (uint64_t)0x34b0bcb5e19b48a8U, (uint64_t)0x391c0cb3c5c95a63U, (uint64_t)0x4ed8aa4ae3418acbU, + (uint64_t)0x5b9cca4f7763e373U, (uint64_t)0x682e6ff3d6b2b8a3U, (uint64_t)0x748f82ee5defb2fcU, + (uint64_t)0x78a5636f43172f60U, (uint64_t)0x84c87814a1f0ab72U, (uint64_t)0x8cc702081a6439ecU, + (uint64_t)0x90befffa23631e28U, (uint64_t)0xa4506cebde82bde9U, (uint64_t)0xbef9a3f7b2c67915U, + (uint64_t)0xc67178f2e372532bU, (uint64_t)0xca273eceea26619cU, (uint64_t)0xd186b8c721c0c207U, + (uint64_t)0xeada7dd6cde0eb1eU, (uint64_t)0xf57d4f7fee6ed178U, (uint64_t)0x06f067aa72176fbaU, + (uint64_t)0x0a637dc5a2c898a6U, (uint64_t)0x113f9804bef90daeU, (uint64_t)0x1b710b35131c471bU, + (uint64_t)0x28db77f523047d84U, (uint64_t)0x32caab7b40c72493U, (uint64_t)0x3c9ebe0a15c9bebcU, + (uint64_t)0x431d67c49c100d4cU, (uint64_t)0x4cc5d4becb3e42b6U, (uint64_t)0x597f299cfc657e2aU, + (uint64_t)0x5fcb6fab3ad6faecU, (uint64_t)0x6c44198c4a475817U + }; + +#if defined(__cplusplus) +} +#endif + +#define __internal_Hacl_SHA2_Generic_H_DEFINED +#endif diff --git a/Modules/_hacl/refresh.sh b/Modules/_hacl/refresh.sh new file mode 100755 index 00000000000000..594873862a2db0 --- /dev/null +++ b/Modules/_hacl/refresh.sh @@ -0,0 +1,132 @@ +#!/usr/bin/env bash +# +# Use this script to update the HACL generated hash algorithm implementation +# code from a local checkout of the upstream hacl-star repository. +# + +set -e +set -o pipefail + +if [[ "${BASH_VERSINFO[0]}" -lt 4 ]]; then + echo "A bash version >= 4 required. Got: $BASH_VERSION" >&2 + exit 1 +fi + +if [[ $1 == "" ]]; then + echo "Usage: $0 path-to-hacl-directory" + echo "" + echo " path-to-hacl-directory should be a local git checkout of a" + echo " https://github.com/hacl-star/hacl-star/ repo." + exit 1 +fi + +# Update this when updating to a new version after verifying that the changes +# the update brings in are good. +expected_hacl_star_rev=94aabbb4cf71347d3779a8db486c761403c6d036 + +hacl_dir="$(realpath "$1")" +cd "$(dirname "$0")" +actual_rev=$(cd "$hacl_dir" && git rev-parse HEAD) + +if [[ "$actual_rev" != "$expected_hacl_star_rev" ]]; then + echo "WARNING: HACL* in '$hacl_dir' is at revision:" >&2 + echo " $actual_rev" >&2 + echo "but expected revision:" >&2 + echo " $expected_hacl_star_rev" >&2 + echo "Edit the expected rev if the changes pulled in are what you want." +fi + +# Step 1: copy files + +declare -a dist_files +dist_files=( + Hacl_Streaming_SHA2.h + internal/Hacl_SHA2_Generic.h + Hacl_Streaming_SHA2.c +) + +declare -a include_files +include_files=( + include/krml/lowstar_endianness.h + include/krml/internal/target.h +) + +declare -a lib_files +lib_files=( + krmllib/dist/minimal/FStar_UInt_8_16_32_64.h +) + +# C files for the algorithms themselves: current directory +(cd "$hacl_dir/dist/gcc-compatible" && tar cf - "${dist_files[@]}") | tar xf - + +# Support header files (e.g. endianness macros): stays in include/ +(cd "$hacl_dir/dist/karamel" && tar cf - "${include_files[@]}") | tar xf - + +# Special treatment: we don't bother with an extra directory and move krmllib +# files to the same include directory +for f in "${lib_files[@]}"; do + cp "$hacl_dir/dist/karamel/$f" include/krml/ +done + +# Step 2: some in-place modifications to keep things simple and minimal + +# This is basic, but refreshes of the vendored HACL code are infrequent, so +# let's not over-engineer this. +if [[ $(uname) == "Darwin" ]]; then + # You're already running with homebrew or macports to satisfy the + # bash>=4 requirement, so requiring GNU sed is entirely reasonable. + sed=gsed +else + sed=sed +fi + +readarray -t all_files < <(find . -name '*.h' -or -name '*.c') + +# types.h is a simple wrapper that defines the uint128 type then proceeds to +# include FStar_UInt_8_16_32_64.h; we jump the types.h step since our current +# selection of algorithms does not necessitate the use of uint128 +$sed -i 's!#include.*types.h"!#include "krml/FStar_UInt_8_16_32_64.h"!g' "${all_files[@]}" +$sed -i 's!#include.*compat.h"!!g' "${all_files[@]}" + +# FStar_UInt_8_16_32_64 contains definitions useful in the general case, but not +# for us; trim! +$sed -i -z 's!\(extern\|typedef\)[^;]*;\n\n!!g' include/krml/FStar_UInt_8_16_32_64.h + +# This contains static inline prototypes that are defined in +# FStar_UInt_8_16_32_64; they are by default repeated for safety of separate +# compilation, but this is not necessary. +$sed -i 's!#include.*Hacl_Krmllib.h"!!g' "${all_files[@]}" + +# This header is useful for *other* algorithms that refer to SHA2, e.g. Ed25519 +# which needs to compute a digest of a message before signing it. Here, since no +# other algorithm builds upon SHA2, this internal header is useless (and is not +# included in $dist_files). +$sed -i 's!#include.*internal/Hacl_Streaming_SHA2.h"!#include "Hacl_Streaming_SHA2.h"!g' "${all_files[@]}" + +# The SHA2 file contains all variants of SHA2. We strip 384 and 512 for the time +# being, to be included later. +# This regexp matches a separator (two new lines), followed by: +# +# * +# ... 384 or 512 ... { +# * +# } +# +# The first non-empty lines are the comment block. The second ... may spill over +# the next following lines if the arguments are printed in one-per-line mode. +$sed -i -z 's/\n\n\([^\n]\+\n\)*[^\n]*\(384\|512\)[^{]*{\n\?\( [^\n]*\n\)*}//g' Hacl_Streaming_SHA2.c + +# Same thing with function prototypes +$sed -i -z 's/\n\n\([^\n]\+\n\)*[^\n]*\(384\|512\)[^;]*;//g' Hacl_Streaming_SHA2.h + +# Use globally unique names for the Hacl_ C APIs to avoid linkage conflicts. +$sed -i -z 's!#include \n!#include \n#include "python_hacl_namespaces.h"\n!' Hacl_Streaming_SHA2.h + +# Finally, we remove a bunch of ifdefs from target.h that are, again, useful in +# the general case, but not exercised by the subset of HACL* that we vendor. +$sed -z -i 's!#ifndef KRML_\(HOST_PRINTF\|HOST_EXIT\|PRE_ALIGN\|POST_ALIGN\|ALIGNED_MALLOC\|ALIGNED_FREE\|HOST_TIME\)\n\(\n\|# [^\n]*\n\|[^#][^\n]*\n\)*#endif\n\n!!g' include/krml/internal/target.h +$sed -z -i 's!\n\n\([^#][^\n]*\n\)*#define KRML_\(EABORT\|EXIT\|CHECK_SIZE\)[^\n]*\(\n [^\n]*\)*!!g' include/krml/internal/target.h +$sed -z -i 's!\n\n\([^#][^\n]*\n\)*#if [^\n]*\n\( [^\n]*\n\)*#define KRML_\(EABORT\|EXIT\|CHECK_SIZE\)[^\n]*\(\n [^\n]*\)*!!g' include/krml/internal/target.h +$sed -z -i 's!\n\n\([^#][^\n]*\n\)*#if [^\n]*\n\( [^\n]*\n\)*# define _\?KRML_\(DEPRECATED\|CHECK_SIZE_PRAGMA\|HOST_EPRINTF\|HOST_SNPRINTF\)[^\n]*\n\([^#][^\n]*\n\|#el[^\n]*\n\|# [^\n]*\n\)*#endif!!g' include/krml/internal/target.h + +echo "Updated; verify all is okay using git diff and git status." diff --git a/Modules/sha256module.c b/Modules/sha256module.c index 17ee86683b7a89..630e4bf03bbe96 100644 --- a/Modules/sha256module.c +++ b/Modules/sha256module.c @@ -31,29 +31,33 @@ class SHA256Type "SHAobject *" "&PyType_Type" [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=71a39174d4f0a744]*/ -/* Some useful types */ -typedef unsigned char SHA_BYTE; -typedef uint32_t SHA_INT32; /* 32-bit integer */ - -/* The SHA block size and message digest sizes, in bytes */ +/* The SHA block size and maximum message digest sizes, in bytes */ #define SHA_BLOCKSIZE 64 -#define SHA_DIGESTSIZE 32 +#define SHA_DIGESTSIZE 32 + +/* The SHA2-224 and SHA2-256 implementations defer to the HACL* verified + * library. */ -/* The structure for storing SHA info */ +#include "_hacl/Hacl_Streaming_SHA2.h" typedef struct { - PyObject_HEAD - SHA_INT32 digest[8]; /* Message digest */ - SHA_INT32 count_lo, count_hi; /* 64-bit bit count */ - SHA_BYTE data[SHA_BLOCKSIZE]; /* SHA data buffer */ - int local; /* unprocessed amount in data */ - int digestsize; + PyObject_HEAD + // Even though one could conceivably perform run-type checks to tell apart a + // sha224_type from a sha256_type (and thus deduce the digest size), we must + // keep this field because it's exposed as a member field on the underlying + // python object. + // TODO: could we transform this into a getter and get rid of the redundant + // field? + int digestsize; + Hacl_Streaming_SHA2_state_sha2_256 *state; } SHAobject; #include "clinic/sha256module.c.h" +/* We shall use run-time type information in the remainder of this module to + * tell apart SHA2-224 and SHA2-256 */ typedef struct { PyTypeObject* sha224_type; PyTypeObject* sha256_type; @@ -67,321 +71,12 @@ _sha256_get_state(PyObject *module) return (_sha256_state *)state; } -/* When run on a little-endian CPU we need to perform byte reversal on an - array of longwords. */ - -#if PY_LITTLE_ENDIAN -static void longReverse(SHA_INT32 *buffer, int byteCount) -{ - byteCount /= sizeof(*buffer); - for (; byteCount--; buffer++) { - *buffer = _Py_bswap32(*buffer); - } -} -#endif - static void SHAcopy(SHAobject *src, SHAobject *dest) { - dest->local = src->local; dest->digestsize = src->digestsize; - dest->count_lo = src->count_lo; - dest->count_hi = src->count_hi; - memcpy(dest->digest, src->digest, sizeof(src->digest)); - memcpy(dest->data, src->data, sizeof(src->data)); -} - - -/* ------------------------------------------------------------------------ - * - * This code for the SHA-256 algorithm was noted as public domain. The - * original headers are pasted below. - * - * Several changes have been made to make it more compatible with the - * Python environment and desired interface. - * - */ - -/* LibTomCrypt, modular cryptographic library -- Tom St Denis - * - * LibTomCrypt is a library that provides various cryptographic - * algorithms in a highly modular and flexible manner. - * - * The library is free for all purposes without any express - * guarantee it works. - * - * Tom St Denis, tomstdenis@iahu.ca, https://www.libtom.net - */ - - -/* SHA256 by Tom St Denis */ - -/* Various logical functions */ -#define ROR(x, y)\ -( ((((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)((y)&31)) | \ -((unsigned long)(x)<<(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL) -#define Ch(x,y,z) (z ^ (x & (y ^ z))) -#define Maj(x,y,z) (((x | y) & z) | (x & y)) -#define S(x, n) ROR((x),(n)) -#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n)) -#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22)) -#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25)) -#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3)) -#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10)) - - -static void -sha_transform(SHAobject *sha_info) -{ - int i; - SHA_INT32 S[8], W[64], t0, t1; - - memcpy(W, sha_info->data, sizeof(sha_info->data)); -#if PY_LITTLE_ENDIAN - longReverse(W, (int)sizeof(sha_info->data)); -#endif - - for (i = 16; i < 64; ++i) { - W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; - } - for (i = 0; i < 8; ++i) { - S[i] = sha_info->digest[i]; - } - - /* Compress */ -#define RND(a,b,c,d,e,f,g,h,i,ki) \ - t0 = h + Sigma1(e) + Ch(e, f, g) + ki + W[i]; \ - t1 = Sigma0(a) + Maj(a, b, c); \ - d += t0; \ - h = t0 + t1; - - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],0,0x428a2f98); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],1,0x71374491); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],2,0xb5c0fbcf); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],3,0xe9b5dba5); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],4,0x3956c25b); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],5,0x59f111f1); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],6,0x923f82a4); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],7,0xab1c5ed5); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],8,0xd807aa98); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],9,0x12835b01); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],10,0x243185be); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],11,0x550c7dc3); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],12,0x72be5d74); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],13,0x80deb1fe); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],14,0x9bdc06a7); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],15,0xc19bf174); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],16,0xe49b69c1); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],17,0xefbe4786); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],18,0x0fc19dc6); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],19,0x240ca1cc); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],20,0x2de92c6f); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],21,0x4a7484aa); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],22,0x5cb0a9dc); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],23,0x76f988da); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],24,0x983e5152); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],25,0xa831c66d); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],26,0xb00327c8); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],27,0xbf597fc7); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],28,0xc6e00bf3); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],29,0xd5a79147); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],30,0x06ca6351); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],31,0x14292967); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],32,0x27b70a85); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],33,0x2e1b2138); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],34,0x4d2c6dfc); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],35,0x53380d13); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],36,0x650a7354); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],37,0x766a0abb); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],38,0x81c2c92e); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],39,0x92722c85); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],40,0xa2bfe8a1); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],41,0xa81a664b); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],42,0xc24b8b70); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],43,0xc76c51a3); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],44,0xd192e819); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],45,0xd6990624); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],46,0xf40e3585); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],47,0x106aa070); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],48,0x19a4c116); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],49,0x1e376c08); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],50,0x2748774c); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],51,0x34b0bcb5); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],52,0x391c0cb3); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],53,0x4ed8aa4a); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],54,0x5b9cca4f); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],55,0x682e6ff3); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],56,0x748f82ee); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],57,0x78a5636f); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],58,0x84c87814); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],59,0x8cc70208); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],60,0x90befffa); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],61,0xa4506ceb); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],62,0xbef9a3f7); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],63,0xc67178f2); - -#undef RND - - /* feedback */ - for (i = 0; i < 8; i++) { - sha_info->digest[i] = sha_info->digest[i] + S[i]; - } - -} - - - -/* initialize the SHA digest */ - -static void -sha_init(SHAobject *sha_info) -{ - sha_info->digest[0] = 0x6A09E667L; - sha_info->digest[1] = 0xBB67AE85L; - sha_info->digest[2] = 0x3C6EF372L; - sha_info->digest[3] = 0xA54FF53AL; - sha_info->digest[4] = 0x510E527FL; - sha_info->digest[5] = 0x9B05688CL; - sha_info->digest[6] = 0x1F83D9ABL; - sha_info->digest[7] = 0x5BE0CD19L; - sha_info->count_lo = 0L; - sha_info->count_hi = 0L; - sha_info->local = 0; - sha_info->digestsize = 32; + dest->state = Hacl_Streaming_SHA2_copy_256(src->state); } -static void -sha224_init(SHAobject *sha_info) -{ - sha_info->digest[0] = 0xc1059ed8L; - sha_info->digest[1] = 0x367cd507L; - sha_info->digest[2] = 0x3070dd17L; - sha_info->digest[3] = 0xf70e5939L; - sha_info->digest[4] = 0xffc00b31L; - sha_info->digest[5] = 0x68581511L; - sha_info->digest[6] = 0x64f98fa7L; - sha_info->digest[7] = 0xbefa4fa4L; - sha_info->count_lo = 0L; - sha_info->count_hi = 0L; - sha_info->local = 0; - sha_info->digestsize = 28; -} - - -/* update the SHA digest */ - -static void -sha_update(SHAobject *sha_info, SHA_BYTE *buffer, Py_ssize_t count) -{ - Py_ssize_t i; - SHA_INT32 clo; - - clo = sha_info->count_lo + ((SHA_INT32) count << 3); - if (clo < sha_info->count_lo) { - ++sha_info->count_hi; - } - sha_info->count_lo = clo; - sha_info->count_hi += (SHA_INT32) count >> 29; - if (sha_info->local) { - i = SHA_BLOCKSIZE - sha_info->local; - if (i > count) { - i = count; - } - memcpy(((SHA_BYTE *) sha_info->data) + sha_info->local, buffer, i); - count -= i; - buffer += i; - sha_info->local += (int)i; - if (sha_info->local == SHA_BLOCKSIZE) { - sha_transform(sha_info); - } - else { - return; - } - } - while (count >= SHA_BLOCKSIZE) { - memcpy(sha_info->data, buffer, SHA_BLOCKSIZE); - buffer += SHA_BLOCKSIZE; - count -= SHA_BLOCKSIZE; - sha_transform(sha_info); - } - memcpy(sha_info->data, buffer, count); - sha_info->local = (int)count; -} - -/* finish computing the SHA digest */ - -static void -sha_final(unsigned char digest[SHA_DIGESTSIZE], SHAobject *sha_info) -{ - int count; - SHA_INT32 lo_bit_count, hi_bit_count; - - lo_bit_count = sha_info->count_lo; - hi_bit_count = sha_info->count_hi; - count = (int) ((lo_bit_count >> 3) & 0x3f); - ((SHA_BYTE *) sha_info->data)[count++] = 0x80; - if (count > SHA_BLOCKSIZE - 8) { - memset(((SHA_BYTE *) sha_info->data) + count, 0, - SHA_BLOCKSIZE - count); - sha_transform(sha_info); - memset((SHA_BYTE *) sha_info->data, 0, SHA_BLOCKSIZE - 8); - } - else { - memset(((SHA_BYTE *) sha_info->data) + count, 0, - SHA_BLOCKSIZE - 8 - count); - } - - /* GJS: note that we add the hi/lo in big-endian. sha_transform will - swap these values into host-order. */ - sha_info->data[56] = (hi_bit_count >> 24) & 0xff; - sha_info->data[57] = (hi_bit_count >> 16) & 0xff; - sha_info->data[58] = (hi_bit_count >> 8) & 0xff; - sha_info->data[59] = (hi_bit_count >> 0) & 0xff; - sha_info->data[60] = (lo_bit_count >> 24) & 0xff; - sha_info->data[61] = (lo_bit_count >> 16) & 0xff; - sha_info->data[62] = (lo_bit_count >> 8) & 0xff; - sha_info->data[63] = (lo_bit_count >> 0) & 0xff; - sha_transform(sha_info); - digest[ 0] = (unsigned char) ((sha_info->digest[0] >> 24) & 0xff); - digest[ 1] = (unsigned char) ((sha_info->digest[0] >> 16) & 0xff); - digest[ 2] = (unsigned char) ((sha_info->digest[0] >> 8) & 0xff); - digest[ 3] = (unsigned char) ((sha_info->digest[0] ) & 0xff); - digest[ 4] = (unsigned char) ((sha_info->digest[1] >> 24) & 0xff); - digest[ 5] = (unsigned char) ((sha_info->digest[1] >> 16) & 0xff); - digest[ 6] = (unsigned char) ((sha_info->digest[1] >> 8) & 0xff); - digest[ 7] = (unsigned char) ((sha_info->digest[1] ) & 0xff); - digest[ 8] = (unsigned char) ((sha_info->digest[2] >> 24) & 0xff); - digest[ 9] = (unsigned char) ((sha_info->digest[2] >> 16) & 0xff); - digest[10] = (unsigned char) ((sha_info->digest[2] >> 8) & 0xff); - digest[11] = (unsigned char) ((sha_info->digest[2] ) & 0xff); - digest[12] = (unsigned char) ((sha_info->digest[3] >> 24) & 0xff); - digest[13] = (unsigned char) ((sha_info->digest[3] >> 16) & 0xff); - digest[14] = (unsigned char) ((sha_info->digest[3] >> 8) & 0xff); - digest[15] = (unsigned char) ((sha_info->digest[3] ) & 0xff); - digest[16] = (unsigned char) ((sha_info->digest[4] >> 24) & 0xff); - digest[17] = (unsigned char) ((sha_info->digest[4] >> 16) & 0xff); - digest[18] = (unsigned char) ((sha_info->digest[4] >> 8) & 0xff); - digest[19] = (unsigned char) ((sha_info->digest[4] ) & 0xff); - digest[20] = (unsigned char) ((sha_info->digest[5] >> 24) & 0xff); - digest[21] = (unsigned char) ((sha_info->digest[5] >> 16) & 0xff); - digest[22] = (unsigned char) ((sha_info->digest[5] >> 8) & 0xff); - digest[23] = (unsigned char) ((sha_info->digest[5] ) & 0xff); - digest[24] = (unsigned char) ((sha_info->digest[6] >> 24) & 0xff); - digest[25] = (unsigned char) ((sha_info->digest[6] >> 16) & 0xff); - digest[26] = (unsigned char) ((sha_info->digest[6] >> 8) & 0xff); - digest[27] = (unsigned char) ((sha_info->digest[6] ) & 0xff); - digest[28] = (unsigned char) ((sha_info->digest[7] >> 24) & 0xff); - digest[29] = (unsigned char) ((sha_info->digest[7] >> 16) & 0xff); - digest[30] = (unsigned char) ((sha_info->digest[7] >> 8) & 0xff); - digest[31] = (unsigned char) ((sha_info->digest[7] ) & 0xff); -} - -/* - * End of copied SHA code. - * - * ------------------------------------------------------------------------ - */ - - static SHAobject * newSHA224object(_sha256_state *state) { @@ -409,14 +104,31 @@ SHA_traverse(PyObject *ptr, visitproc visit, void *arg) } static void -SHA_dealloc(PyObject *ptr) +SHA_dealloc(SHAobject *ptr) { + Hacl_Streaming_SHA2_free_256(ptr->state); PyTypeObject *tp = Py_TYPE(ptr); PyObject_GC_UnTrack(ptr); PyObject_GC_Del(ptr); Py_DECREF(tp); } +/* HACL* takes a uint32_t for the length of its parameter, but Py_ssize_t can be + * 64 bits. */ +static void update_256(Hacl_Streaming_SHA2_state_sha2_256 *state, uint8_t *buf, Py_ssize_t len) { + /* Note: we explicitly ignore the error code on the basis that it would take > + * 1 billion years to overflow the maximum admissible length for SHA2-256 + * (namely, 2^61-1 bytes). */ + while (len > UINT32_MAX) { + Hacl_Streaming_SHA2_update_256(state, buf, UINT32_MAX); + len -= UINT32_MAX; + buf += UINT32_MAX; + } + /* Cast to uint32_t is safe: upon exiting the loop, len <= UINT32_MAX, and + * therefore fits in a uint32_t */ + Hacl_Streaming_SHA2_update_256(state, buf, (uint32_t) len); +} + /* External methods for a hash object */ @@ -458,11 +170,10 @@ static PyObject * SHA256Type_digest_impl(SHAobject *self) /*[clinic end generated code: output=46616a5e909fbc3d input=f1f4cfea5cbde35c]*/ { - unsigned char digest[SHA_DIGESTSIZE]; - SHAobject temp; - - SHAcopy(self, &temp); - sha_final(digest, &temp); + uint8_t digest[SHA_DIGESTSIZE]; + // HACL performs copies under the hood so that self->state remains valid + // after this call. + Hacl_Streaming_SHA2_finish_256(self->state, digest); return PyBytes_FromStringAndSize((const char *)digest, self->digestsize); } @@ -476,13 +187,8 @@ static PyObject * SHA256Type_hexdigest_impl(SHAobject *self) /*[clinic end generated code: output=725f8a7041ae97f3 input=0cc4c714693010d1]*/ { - unsigned char digest[SHA_DIGESTSIZE]; - SHAobject temp; - - /* Get the raw (binary) digest value */ - SHAcopy(self, &temp); - sha_final(digest, &temp); - + uint8_t digest[SHA_DIGESTSIZE]; + Hacl_Streaming_SHA2_finish_256(self->state, digest); return _Py_strhex((const char *)digest, self->digestsize); } @@ -503,7 +209,7 @@ SHA256Type_update(SHAobject *self, PyObject *obj) GET_BUFFER_VIEW_OR_ERROUT(obj, &buf); - sha_update(self, buf.buf, buf.len); + update_256(self->state, buf.buf, buf.len); PyBuffer_Release(&buf); Py_RETURN_NONE; @@ -524,12 +230,12 @@ SHA256_get_block_size(PyObject *self, void *closure) } static PyObject * -SHA256_get_name(PyObject *self, void *closure) +SHA256_get_name(SHAobject *self, void *closure) { - if (((SHAobject *)self)->digestsize == 32) - return PyUnicode_FromStringAndSize("sha256", 6); - else + if (self->digestsize == 28) { return PyUnicode_FromStringAndSize("sha224", 6); + } + return PyUnicode_FromStringAndSize("sha256", 6); } static PyGetSetDef SHA_getseters[] = { @@ -606,7 +312,8 @@ _sha256_sha256_impl(PyObject *module, PyObject *string, int usedforsecurity) return NULL; } - sha_init(new); + new->state = Hacl_Streaming_SHA2_create_in_256(); + new->digestsize = 32; if (PyErr_Occurred()) { Py_DECREF(new); @@ -616,7 +323,7 @@ _sha256_sha256_impl(PyObject *module, PyObject *string, int usedforsecurity) return NULL; } if (string) { - sha_update(new, buf.buf, buf.len); + update_256(new->state, buf.buf, buf.len); PyBuffer_Release(&buf); } @@ -651,7 +358,8 @@ _sha256_sha224_impl(PyObject *module, PyObject *string, int usedforsecurity) return NULL; } - sha224_init(new); + new->state = Hacl_Streaming_SHA2_create_in_224(); + new->digestsize = 28; if (PyErr_Occurred()) { Py_DECREF(new); @@ -661,7 +369,7 @@ _sha256_sha224_impl(PyObject *module, PyObject *string, int usedforsecurity) return NULL; } if (string) { - sha_update(new, buf.buf, buf.len); + update_256(new->state, buf.buf, buf.len); PyBuffer_Release(&buf); } diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 397d22abe23503..e8e9ff01e306bc 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -100,7 +100,7 @@ /Zm200 %(AdditionalOptions) - $(PySourcePath)Python;%(AdditionalIncludeDirectories) + $(PySourcePath)Modules\_hacl\include;$(PySourcePath)Modules\_hacl\internal;$(PySourcePath)Python;%(AdditionalIncludeDirectories) $(zlibDir);%(AdditionalIncludeDirectories) _USRDLL;Py_BUILD_CORE;Py_BUILD_CORE_BUILTIN;Py_ENABLE_SHARED;MS_DLL_ID="$(SysWinVer)";%(PreprocessorDefinitions) _Py_HAVE_ZLIB;%(PreprocessorDefinitions) @@ -407,6 +407,7 @@ + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index bcbedcc3235b3e..4820db6f2c32dc 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -866,6 +866,9 @@ Modules + + Modules + Modules diff --git a/configure b/configure index aef8103085bc20..97694c602d1cc8 100755 --- a/configure +++ b/configure @@ -24426,6 +24426,7 @@ SRCDIRS="\ Modules/_ctypes \ Modules/_decimal \ Modules/_decimal/libmpdec \ + Modules/_hacl \ Modules/_io \ Modules/_multiprocessing \ Modules/_sha3 \ @@ -26966,7 +26967,7 @@ fi as_fn_append MODULE_BLOCK "MODULE__SHA256_STATE=$py_cv_module__sha256$as_nl" if test "x$py_cv_module__sha256" = xyes; then : - + as_fn_append MODULE_BLOCK "MODULE__SHA256_CFLAGS=-I\$(srcdir)/Modules/_hacl/include -I\$(srcdir)/Modules/_hacl/internal -D_BSD_SOURCE -D_DEFAULT_SOURCE$as_nl" fi diff --git a/configure.ac b/configure.ac index 010bca855f0059..09369b985b33f6 100644 --- a/configure.ac +++ b/configure.ac @@ -6508,6 +6508,7 @@ SRCDIRS="\ Modules/_ctypes \ Modules/_decimal \ Modules/_decimal/libmpdec \ + Modules/_hacl \ Modules/_io \ Modules/_multiprocessing \ Modules/_sha3 \ @@ -7197,7 +7198,9 @@ dnl By default we always compile these even when OpenSSL is available dnl (issue #14693). The modules are small. PY_STDLIB_MOD([_md5], [test "$with_builtin_md5" = yes]) PY_STDLIB_MOD([_sha1], [test "$with_builtin_sha1" = yes]) -PY_STDLIB_MOD([_sha256], [test "$with_builtin_sha256" = yes]) +PY_STDLIB_MOD([_sha256], + [test "$with_builtin_sha256" = yes], [], + [-I\$(srcdir)/Modules/_hacl/include -I\$(srcdir)/Modules/_hacl/internal -D_BSD_SOURCE -D_DEFAULT_SOURCE]) PY_STDLIB_MOD([_sha512], [test "$with_builtin_sha512" = yes]) PY_STDLIB_MOD([_sha3], [test "$with_builtin_sha3" = yes]) PY_STDLIB_MOD([_blake2], From 694e346a01f407c7f78138331db006dea79f82a8 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 6 Feb 2023 21:03:58 -0800 Subject: [PATCH 024/247] gh-98831: Move DSL documentation here from ideas repo (#101629) --- Tools/cases_generator/README.md | 9 +- .../cases_generator/interpreter_definition.md | 409 ++++++++++++++++++ 2 files changed, 415 insertions(+), 3 deletions(-) create mode 100644 Tools/cases_generator/interpreter_definition.md diff --git a/Tools/cases_generator/README.md b/Tools/cases_generator/README.md index dc055ead1941cd..c595a932ac4470 100644 --- a/Tools/cases_generator/README.md +++ b/Tools/cases_generator/README.md @@ -1,5 +1,8 @@ # Tooling to generate interpreters +Documentation for the instruction definitions in `Python/bytecodes.c` +("the DSL") is [here](interpreter_definition.md). + What's currently here: - `lexer.py`: lexer for C, originally written by Mark Shannon @@ -7,10 +10,10 @@ What's currently here: - `parser.py`: Parser for instruction definition DSL; main class `Parser` - `generate_cases.py`: driver script to read `Python/bytecodes.c` and write `Python/generated_cases.c.h` +- `test_generator.py`: tests, require manual running using `pytest` -The DSL for the instruction definitions in `Python/bytecodes.c` is described -[here](https://github.com/faster-cpython/ideas/blob/main/3.12/interpreter_definition.md). -Note that there is some dummy C code at the top and bottom of the file +Note that there is some dummy C code at the top and bottom of +`Python/bytecodes.c` to fool text editors like VS Code into believing this is valid C code. ## A bit about the parser diff --git a/Tools/cases_generator/interpreter_definition.md b/Tools/cases_generator/interpreter_definition.md new file mode 100644 index 00000000000000..c7bd38d32ff411 --- /dev/null +++ b/Tools/cases_generator/interpreter_definition.md @@ -0,0 +1,409 @@ +# A higher level definition of the bytecode interpreter + +## Abstract + +The CPython interpreter is defined in C, meaning that the semantics of the +bytecode instructions, the dispatching mechanism, error handling, and +tracing and instrumentation are all intermixed. + +This document proposes defining a custom C-like DSL for defining the +instruction semantics and tools for generating the code deriving from +the instruction definitions. + +These tools would be used to: +* Generate the main interpreter (done) +* Generate the tier 2 interpreter +* Generate documentation for instructions +* Generate metadata about instructions, such as stack use (done). + +Having a single definition file ensures that there is a single source +of truth for bytecode semantics. + +Other tools that operate on bytecodes, like `frame.setlineno` +and the `dis` module, will be derived from the common semantic +definition, reducing errors. + +## Motivation + +The bytecode interpreter of CPython has traditionally been defined as standard +C code, but with a lot of macros. +The presence of these macros and the nature of bytecode interpreters means +that the interpreter is effectively defined in a domain specific language (DSL). + +Rather than using an ad-hoc DSL embedded in the C code for the interpreter, +a custom DSL should be defined and the semantics of the bytecode instructions, +and the instructions defined in that DSL. + +Generating the interpreter decouples low-level details of dispatching +and error handling from the semantics of the instructions, resulting +in more maintainable code and a potentially faster interpreter. + +It also provides the ability to create and check optimizers and optimization +passes from the semantic definition, reducing errors. + +## Rationale + +As we improve the performance of CPython, we need to optimize larger regions +of code, use more complex optimizations and, ultimately, translate to machine +code. + +All of these steps introduce the possibility of more bugs, and require more code +to be written. One way to mitigate this is through the use of code generators. +Code generators decouple the debugging of the code (the generator) from checking +the correctness (the DSL input). + +For example, we are likely to want a new interpreter for the tier 2 optimizer +to be added in 3.12. That interpreter will have a different API, a different +set of instructions and potentially different dispatching mechanism. +But the instructions it will interpret will be built from the same building +blocks as the instructions for the tier 1 (PEP 659) interpreter. + +Rewriting all the instructions is tedious and error-prone, and changing the +instructions is a maintenance headache as both versions need to be kept in sync. + +By using a code generator and using a common source for the instructions, or +parts of instructions, we can reduce the potential for errors considerably. + + +## Specification + +This specification is at an early stage and is likely to change considerably. + +Syntax +------ + +Each op definition has a kind, a name, a stack and instruction stream effect, +and a piece of C code describing its semantics:: + +``` + file: + (definition | family)+ + + definition: + "inst" "(" NAME ["," stack_effect] ")" "{" C-code "}" + | + "op" "(" NAME "," stack_effect ")" "{" C-code "}" + | + "macro" "(" NAME ")" "=" uop ("+" uop)* ";" + | + "super" "(" NAME ")" "=" NAME ("+" NAME)* ";" + + stack_effect: + "(" [inputs] "--" [outputs] ")" + + inputs: + input ("," input)* + + outputs: + output ("," output)* + + input: + object | stream | array + + output: + object | array + + uop: + NAME | stream + + object: + NAME [":" type] [ "if" "(" C-expression ")" ] + + type: + NAME + + stream: + NAME "/" size + + size: + INTEGER + + array: + object "[" C-expression "]" + + family: + "family" "(" NAME ")" = "{" NAME ("," NAME)+ "}" ";" +``` + +The following definitions may occur: + +* `inst`: A normal instruction, as previously defined by `TARGET(NAME)` in `ceval.c`. +* `op`: A part instruction from which macros can be constructed. +* `macro`: A bytecode instruction constructed from ops and cache effects. +* `super`: A super-instruction, such as `LOAD_FAST__LOAD_FAST`, constructed from + normal or macro instructions. + +`NAME` can be any ASCII identifier that is a C identifier and not a C or Python keyword. +`foo_1` is legal. `$` is not legal, nor is `struct` or `class`. + +The optional `type` in an `object` is the C type. It defaults to `PyObject *`. +The objects before the "--" are the objects on top of the the stack at the start +of the instruction. Those after the "--" are the objects on top of the the stack +at the end of the instruction. + +An `inst` without `stack_effect` is a transitional form to allow the original C code +definitions to be copied. It lacks information to generate anything other than the +interpreter, but is useful for initial porting of code. + +Stack effect names may be `unused`, indicating the space is to be reserved +but no use of it will be made in the instruction definition. +This is useful to ensure that all instructions in a family have the same +stack effect. + +The number in a `stream` define how many codeunits are consumed from the +instruction stream. It returns a 16, 32 or 64 bit value. +If the name is `unused` the size can be any value and that many codeunits +will be skipped in the instruction stream. + +By convention cache effects (`stream`) must precede the input effects. + +The name `oparg` is pre-defined as a 32 bit value fetched from the instruction stream. + +The C code may include special functions that are understood by the tools as +part of the DSL. + +Those functions include: + +* `DEOPT_IF(cond, instruction)`. Deoptimize if `cond` is met. +* `ERROR_IF(cond, label)`. Jump to error handler if `cond` is true. +* `DECREF_INPUTS()`. Generate `Py_DECREF()` calls for the input stack effects. + +Variables can either be defined in the input, output, or in the C code. +Variables defined in the input may not be assigned in the C code. +If an `ERROR_IF` occurs, all values will be removed from the stack; +they must already be `DECREF`'ed by the code block. +If a `DEOPT_IF` occurs, no values will be removed from the stack or +the instruction stream; no values must have been `DECREF`'ed or created. + +These requirements result in the following constraints on the use of +`DEOPT_IF` and `ERROR_IF` in any instruction's code block: + +1. Until the last `DEOPT_IF`, no objects may be allocated, `INCREF`ed, + or `DECREF`ed. +2. Before the first `ERROR_IF`, all input values must be `DECREF`ed, + and no objects may be allocated or `INCREF`ed, with the exception + of attempting to create an object and checking for success using + `ERROR_IF(result == NULL, label)`. (TODO: Unclear what to do with + intermediate results.) +3. No `DEOPT_IF` may follow an `ERROR_IF` in the same block. + +Semantics +--------- + +The underlying execution model is a stack machine. +Operations pop values from the stack, and push values to the stack. +They also can look at, and consume, values from the instruction stream. + +All members of a family must have the same stack and instruction stream effect. + +Examples +-------- + +(Another source of examples can be found in the [tests](test_generator.py).) + +Some examples: + +### Output stack effect +```C + inst ( LOAD_FAST, (-- value) ) { + value = frame->f_localsplus[oparg]; + Py_INCREF(value); + } +``` +This would generate: +```C + TARGET(LOAD_FAST) { + PyObject *value; + value = frame->f_localsplus[oparg]; + Py_INCREF(value); + PUSH(value); + DISPATCH(); + } +``` + +### Input stack effect +```C + inst ( STORE_FAST, (value --) ) { + SETLOCAL(oparg, value); + } +``` +This would generate: +```C + TARGET(STORE_FAST) { + PyObject *value = PEEK(1); + SETLOCAL(oparg, value); + STACK_SHRINK(1); + DISPATCH(); + } +``` + +### Super-instruction definition + +```C + super ( LOAD_FAST__LOAD_FAST ) = LOAD_FAST + LOAD_FAST ; +``` +This might get translated into the following: +```C + TARGET(LOAD_FAST__LOAD_FAST) { + PyObject *value; + value = frame->f_localsplus[oparg]; + Py_INCREF(value); + PUSH(value); + NEXTOPARG(); + next_instr++; + value = frame->f_localsplus[oparg]; + Py_INCREF(value); + PUSH(value); + DISPATCH(); + } +``` + +### Input stack effect and cache effect +```C + op ( CHECK_OBJECT_TYPE, (owner, type_version/2 -- owner) ) { + PyTypeObject *tp = Py_TYPE(owner); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version); + } +``` +This might become (if it was an instruction): +```C + TARGET(CHECK_OBJECT_TYPE) { + PyObject *owner = PEEK(1); + uint32 type_version = read32(next_instr); + PyTypeObject *tp = Py_TYPE(owner); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version); + next_instr += 2; + DISPATCH(); + } +``` + +### More examples + +For explanations see "Generating the interpreter" below.) +```C + op ( CHECK_HAS_INSTANCE_VALUES, (owner -- owner) ) { + PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); + DEOPT_IF(!_PyDictOrValues_IsValues(dorv)); + } +``` +```C + op ( LOAD_INSTANCE_VALUE, (owner, index/1 -- null if (oparg & 1), res) ) { + res = _PyDictOrValues_GetValues(dorv)->values[index]; + DEOPT_IF(res == NULL); + Py_INCREF(res); + null = NULL; + Py_DECREF(owner); + } +``` +```C + macro ( LOAD_ATTR_INSTANCE_VALUE ) = + counter/1 + CHECK_OBJECT_TYPE + CHECK_HAS_INSTANCE_VALUES + + LOAD_INSTANCE_VALUE + unused/4 ; +``` +```C + op ( LOAD_SLOT, (owner, index/1 -- null if (oparg & 1), res) ) { + char *addr = (char *)owner + index; + res = *(PyObject **)addr; + DEOPT_IF(res == NULL); + Py_INCREF(res); + null = NULL; + Py_DECREF(owner); + } +``` +```C + macro ( LOAD_ATTR_SLOT ) = counter/1 + CHECK_OBJECT_TYPE + LOAD_SLOT + unused/4; +``` +```C + inst ( BUILD_TUPLE, (items[oparg] -- tuple) ) { + tuple = _PyTuple_FromArraySteal(items, oparg); + ERROR_IF(tuple == NULL, error); + } +``` +```C + inst ( PRINT_EXPR ) { + PyObject *value = POP(); + PyObject *hook = _PySys_GetAttr(tstate, &_Py_ID(displayhook)); + PyObject *res; + if (hook == NULL) { + _PyErr_SetString(tstate, PyExc_RuntimeError, + "lost sys.displayhook"); + Py_DECREF(value); + goto error; + } + res = PyObject_CallOneArg(hook, value); + Py_DECREF(value); + ERROR_IF(res == NULL); + Py_DECREF(res); + } +``` + +### Define an instruction family +These opcodes all share the same instruction format): +```C + family(load_attr) = { LOAD_ATTR, LOAD_ATTR_INSTANCE_VALUE, LOAD_SLOT } ; +``` + +Generating the interpreter +========================== + +The generated C code for a single instruction includes a preamble and dispatch at the end +which can be easily inserted. What is more complex is ensuring the correct stack effects +and not generating excess pops and pushes. + +For example, in `CHECK_HAS_INSTANCE_VALUES`, `owner` occurs in the input, so it cannot be +redefined. Thus it doesn't need to written and can be read without adjusting the stack pointer. +The C code generated for `CHECK_HAS_INSTANCE_VALUES` would look something like: + +```C + { + PyObject *owner = stack_pointer[-1]; + PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); + DEOPT_IF(!_PyDictOrValues_IsValues(dorv)); + } +``` + +When combining ops together to form instructions, temporary values should be used, +rather than popping and pushing, such that `LOAD_ATTR_SLOT` would look something like: + +```C + case LOAD_ATTR_SLOT: { + PyObject *s1 = stack_pointer[-1]; + /* CHECK_OBJECT_TYPE */ + { + PyObject *owner = s1; + uint32_t type_version = read32(next_instr + 1); + PyTypeObject *tp = Py_TYPE(owner); + assert(type_version != 0); + if (tp->tp_version_tag != type_version) goto deopt; + } + /* LOAD_SLOT */ + { + PyObject *owner = s1; + uint16_t index = *(next_instr + 1 + 2); + char *addr = (char *)owner + index; + PyObject *null; + PyObject *res = *(PyObject **)addr; + if (res == NULL) goto deopt; + Py_INCREF(res); + null = NULL; + Py_DECREF(owner); + if (oparg & 1) { + stack_pointer[0] = null; + stack_pointer += 1; + } + s1 = res; + } + next_instr += (1 + 1 + 2 + 1 + 4); + stack_pointer[-1] = s1; + DISPATCH(); + } +``` + +Other tools +=========== + +From the instruction definitions we can generate the stack marking code used in `frame.set_lineno()`, +and the tables for use by disassemblers. + From c4de6b1d52304a0a9cdfafc1dad5098993710404 Mon Sep 17 00:00:00 2001 From: Brian Skinn Date: Tue, 7 Feb 2023 00:25:42 -0500 Subject: [PATCH 025/247] gh-85747: Active voice & suggested edits, 'running/stopping loop' & 'callbacks' subsections of asyncio-eventloop.rst (#100270) Co-authored-by: C.A.M. Gerlach Co-authored-by: Terry Jan Reedy --- Doc/library/asyncio-eventloop.rst | 42 ++++++++++++++++++------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index db63a5dd11ad6e..f86e784288029c 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -186,19 +186,24 @@ Running and stopping the loop .. coroutinemethod:: loop.shutdown_default_executor(timeout=None) Schedule the closure of the default executor and wait for it to join all of - the threads in the :class:`ThreadPoolExecutor`. After calling this method, a - :exc:`RuntimeError` will be raised if :meth:`loop.run_in_executor` is called - while using the default executor. + the threads in the :class:`~concurrent.futures.ThreadPoolExecutor`. + Once this method has been called, + using the default executor with :meth:`loop.run_in_executor` + will raise a :exc:`RuntimeError`. - The *timeout* parameter specifies the amount of time the executor will - be given to finish joining. The default value is ``None``, which means the - executor will be given an unlimited amount of time. + The *timeout* parameter specifies the amount of time + (in :class:`float` seconds) the executor will be given to finish joining. + With the default, ``None``, + the executor is allowed an unlimited amount of time. - If the timeout duration is reached, a warning is emitted and executor is - terminated without waiting for its threads to finish joining. + If the *timeout* is reached, a :exc:`RuntimeWarning` is emitted + and the default executor is terminated + without waiting for its threads to finish joining. - Note that there is no need to call this function when - :func:`asyncio.run` is used. + .. note:: + + Do not call this method when using :func:`asyncio.run`, + as the latter handles default executor shutdown automatically. .. versionadded:: 3.9 @@ -213,22 +218,23 @@ Scheduling callbacks Schedule the *callback* :term:`callback` to be called with *args* arguments at the next iteration of the event loop. + Return an instance of :class:`asyncio.Handle`, + which can be used later to cancel the callback. + Callbacks are called in the order in which they are registered. Each callback will be called exactly once. - An optional keyword-only *context* argument allows specifying a + The optional keyword-only *context* argument specifies a custom :class:`contextvars.Context` for the *callback* to run in. - The current context is used when no *context* is provided. - - An instance of :class:`asyncio.Handle` is returned, which can be - used later to cancel the callback. + Callbacks use the current context when no *context* is provided. - This method is not thread-safe. + Unlike :meth:`call_soon_threadsafe`, this method is not thread-safe. .. method:: loop.call_soon_threadsafe(callback, *args, context=None) - A thread-safe variant of :meth:`call_soon`. Must be used to - schedule callbacks *from another thread*. + A thread-safe variant of :meth:`call_soon`. When scheduling callbacks from + another thread, this function *must* be used, since :meth:`call_soon` is not + thread-safe. Raises :exc:`RuntimeError` if called on a loop that's been closed. This can happen on a secondary thread when the main application is From ae62bddaf81176a7e55f95f18d55621c9c46c23d Mon Sep 17 00:00:00 2001 From: Matthieu Dartiailh Date: Tue, 7 Feb 2023 10:34:21 +0100 Subject: [PATCH 026/247] gh-101072: support default and kw default in PyEval_EvalCodeEx for 3.11+ (#101127) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Łukasz Langa --- Lib/test/test_capi/test_eval_code_ex.py | 56 +++++++ ...3-02-06-20-13-36.gh-issue-92173.RQE0mk.rst | 8 + Modules/_testcapimodule.c | 141 +++++++++++++++++- Objects/funcobject.c | 4 +- Python/ceval.c | 3 - 5 files changed, 206 insertions(+), 6 deletions(-) create mode 100644 Lib/test/test_capi/test_eval_code_ex.py create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-06-20-13-36.gh-issue-92173.RQE0mk.rst diff --git a/Lib/test/test_capi/test_eval_code_ex.py b/Lib/test/test_capi/test_eval_code_ex.py new file mode 100644 index 00000000000000..2d28e5289eff94 --- /dev/null +++ b/Lib/test/test_capi/test_eval_code_ex.py @@ -0,0 +1,56 @@ +import unittest + +from test.support import import_helper + + +# Skip this test if the _testcapi module isn't available. +_testcapi = import_helper.import_module('_testcapi') + + +class PyEval_EvalCodeExTests(unittest.TestCase): + + def test_simple(self): + def f(): + return a + + self.assertEqual(_testcapi.eval_code_ex(f.__code__, dict(a=1)), 1) + + # Need to force the compiler to use LOAD_NAME + # def test_custom_locals(self): + # def f(): + # return + + def test_with_args(self): + def f(a, b, c): + return a + + self.assertEqual(_testcapi.eval_code_ex(f.__code__, {}, {}, (1, 2, 3)), 1) + + def test_with_kwargs(self): + def f(a, b, c): + return a + + self.assertEqual(_testcapi.eval_code_ex(f.__code__, {}, {}, (), dict(a=1, b=2, c=3)), 1) + + def test_with_default(self): + def f(a): + return a + + self.assertEqual(_testcapi.eval_code_ex(f.__code__, {}, {}, (), {}, (1,)), 1) + + def test_with_kwarg_default(self): + def f(*, a): + return a + + self.assertEqual(_testcapi.eval_code_ex(f.__code__, {}, {}, (), {}, (), dict(a=1)), 1) + + def test_with_closure(self): + a = 1 + def f(): + return a + + self.assertEqual(_testcapi.eval_code_ex(f.__code__, {}, {}, (), {}, (), {}, f.__closure__), 1) + + +if __name__ == "__main__": + unittest.main() diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-06-20-13-36.gh-issue-92173.RQE0mk.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-06-20-13-36.gh-issue-92173.RQE0mk.rst new file mode 100644 index 00000000000000..2d991f6ca21b6f --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-06-20-13-36.gh-issue-92173.RQE0mk.rst @@ -0,0 +1,8 @@ +macOS #.. section: IDLE #.. section: Tools/Demos #.. section: C API + +# Write your Misc/NEWS entry below. It should be a simple ReST paragraph. # +Don't start with "- Issue #: " or "- gh-issue-: " or that sort of +stuff. +########################################################################### + +Fix the ``defs`` and ``kwdefs`` arguments to :c:func:`PyEval_EvalCodeEx`. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 5e47f4975a2d54..5a6097ef0ac5a6 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -2237,7 +2237,7 @@ dict_get_version(PyObject *self, PyObject *args) return NULL; _Py_COMP_DIAG_PUSH - _Py_COMP_DIAG_IGNORE_DEPR_DECLS + _Py_COMP_DIAG_IGNORE_DEPR_DECLS version = dict->ma_version_tag; _Py_COMP_DIAG_POP @@ -3064,6 +3064,144 @@ eval_get_func_desc(PyObject *self, PyObject *func) return PyUnicode_FromString(PyEval_GetFuncDesc(func)); } +static PyObject * +eval_eval_code_ex(PyObject *mod, PyObject *pos_args) +{ + PyObject *result = NULL; + PyObject *code; + PyObject *globals; + PyObject *locals = NULL; + PyObject *args = NULL; + PyObject *kwargs = NULL; + PyObject *defaults = NULL; + PyObject *kw_defaults = NULL; + PyObject *closure = NULL; + + PyObject **c_kwargs = NULL; + + if (!PyArg_UnpackTuple(pos_args, + "eval_code_ex", + 2, + 8, + &code, + &globals, + &locals, + &args, + &kwargs, + &defaults, + &kw_defaults, + &closure)) + { + goto exit; + } + + if (!PyCode_Check(code)) { + PyErr_SetString(PyExc_TypeError, + "code must be a Python code object"); + goto exit; + } + + if (!PyDict_Check(globals)) { + PyErr_SetString(PyExc_TypeError, "globals must be a dict"); + goto exit; + } + + if (locals && !PyMapping_Check(locals)) { + PyErr_SetString(PyExc_TypeError, "locals must be a mapping"); + goto exit; + } + if (locals == Py_None) { + locals = NULL; + } + + PyObject **c_args = NULL; + Py_ssize_t c_args_len = 0; + + if (args) + { + if (!PyTuple_Check(args)) { + PyErr_SetString(PyExc_TypeError, "args must be a tuple"); + goto exit; + } else { + c_args = &PyTuple_GET_ITEM(args, 0); + c_args_len = PyTuple_Size(args); + } + } + + Py_ssize_t c_kwargs_len = 0; + + if (kwargs) + { + if (!PyDict_Check(kwargs)) { + PyErr_SetString(PyExc_TypeError, "keywords must be a dict"); + goto exit; + } else { + c_kwargs_len = PyDict_Size(kwargs); + if (c_kwargs_len > 0) { + c_kwargs = PyMem_NEW(PyObject*, 2 * c_kwargs_len); + if (!c_kwargs) { + PyErr_NoMemory(); + goto exit; + } + + Py_ssize_t i = 0; + Py_ssize_t pos = 0; + + while (PyDict_Next(kwargs, + &pos, + &c_kwargs[i], + &c_kwargs[i + 1])) + { + i += 2; + } + c_kwargs_len = i / 2; + /* XXX This is broken if the caller deletes dict items! */ + } + } + } + + + PyObject **c_defaults = NULL; + Py_ssize_t c_defaults_len = 0; + + if (defaults && PyTuple_Check(defaults)) { + c_defaults = &PyTuple_GET_ITEM(defaults, 0); + c_defaults_len = PyTuple_Size(defaults); + } + + if (kw_defaults && !PyDict_Check(kw_defaults)) { + PyErr_SetString(PyExc_TypeError, "kw_defaults must be a dict"); + goto exit; + } + + if (closure && !PyTuple_Check(closure)) { + PyErr_SetString(PyExc_TypeError, "closure must be a tuple of cells"); + goto exit; + } + + + result = PyEval_EvalCodeEx( + code, + globals, + locals, + c_args, + c_args_len, + c_kwargs, + c_kwargs_len, + c_defaults, + c_defaults_len, + kw_defaults, + closure + ); + +exit: + if (c_kwargs) { + PyMem_DEL(c_kwargs); + } + + return result; +} + static PyObject * get_feature_macros(PyObject *self, PyObject *Py_UNUSED(args)) { @@ -3385,6 +3523,7 @@ static PyMethodDef TestMethods[] = { {"set_exc_info", test_set_exc_info, METH_VARARGS}, {"argparsing", argparsing, METH_VARARGS}, {"code_newempty", code_newempty, METH_VARARGS}, + {"eval_code_ex", eval_eval_code_ex, METH_VARARGS}, {"make_exception_with_doc", _PyCFunction_CAST(make_exception_with_doc), METH_VARARGS | METH_KEYWORDS}, {"make_memoryview_from_NULL_pointer", make_memoryview_from_NULL_pointer, diff --git a/Objects/funcobject.c b/Objects/funcobject.c index baa360381a7724..91a6b3dd40a232 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -87,8 +87,8 @@ _PyFunction_FromConstructor(PyFrameConstructor *constr) op->func_name = Py_NewRef(constr->fc_name); op->func_qualname = Py_NewRef(constr->fc_qualname); op->func_code = Py_NewRef(constr->fc_code); - op->func_defaults = NULL; - op->func_kwdefaults = NULL; + op->func_defaults = Py_XNewRef(constr->fc_defaults); + op->func_kwdefaults = Py_XNewRef(constr->fc_kwdefaults); op->func_closure = Py_XNewRef(constr->fc_closure); op->func_doc = Py_NewRef(Py_None); op->func_dict = NULL; diff --git a/Python/ceval.c b/Python/ceval.c index 2e6fed580dede4..ecb5bf9655553e 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1761,9 +1761,6 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals, } allargs = newargs; } - for (int i = 0; i < kwcount; i++) { - PyTuple_SET_ITEM(kwnames, i, Py_NewRef(kws[2*i])); - } PyFrameConstructor constr = { .fc_globals = globals, .fc_builtins = builtins, From 79903240480429a6e545177416a7b782b0e5b9bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Tue, 7 Feb 2023 10:50:39 +0100 Subject: [PATCH 027/247] [gh-101072] Fix Blurb for GH-101127 --- .../2023-02-06-20-13-36.gh-issue-92173.RQE0mk.rst | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-06-20-13-36.gh-issue-92173.RQE0mk.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-06-20-13-36.gh-issue-92173.RQE0mk.rst index 2d991f6ca21b6f..6b98aac2a46545 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2023-02-06-20-13-36.gh-issue-92173.RQE0mk.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-06-20-13-36.gh-issue-92173.RQE0mk.rst @@ -1,8 +1,2 @@ -macOS #.. section: IDLE #.. section: Tools/Demos #.. section: C API - -# Write your Misc/NEWS entry below. It should be a simple ReST paragraph. # -Don't start with "- Issue #: " or "- gh-issue-: " or that sort of -stuff. -########################################################################### - -Fix the ``defs`` and ``kwdefs`` arguments to :c:func:`PyEval_EvalCodeEx`. +Fix the ``defs`` and ``kwdefs`` arguments to :c:func:`PyEval_EvalCodeEx` +and a reference leak in that function. From 3c67ec394faac79d260804d569a18fab43018af0 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Tue, 7 Feb 2023 13:17:26 +0100 Subject: [PATCH 028/247] Python 3.12.0a5 --- Include/patchlevel.h | 4 +- Lib/pydoc_data/topics.py | 14 +- Misc/NEWS.d/3.12.0a5.rst | 664 ++++++++++++++++++ ...2-08-30-10-16-31.gh-issue-96305.274i8B.rst | 2 - ...2-10-25-11-53-55.gh-issue-98636.e0RPAr.rst | 2 - ...2-10-27-09-57-12.gh-issue-98705.H11XmR.rst | 2 - ...-01-15-11-22-15.gh-issue-101060.0mYk9E.rst | 3 - ...-01-17-21-32-51.gh-issue-100340.i9zRGM.rst | 2 - ...-01-21-10-31-35.gh-issue-101152.xvM8pL.rst | 3 - ...3-01-26-19-02-11.gh-issue-77532.cXD8bg.rst | 1 - ...-02-02-23-43-46.gh-issue-101522.lnUDta.rst | 2 - ...-02-04-06-59-07.gh-issue-101282.7sQz5l.rst | 2 - .../2018-02-05-21-54-46.bpo-32780.Dtiz8z.rst | 3 - ...-01-03-14-33-23.gh-issue-100712.po6xyB.rst | 1 - ...-01-03-20-59-20.gh-issue-100726.W9huFl.rst | 1 - ...3-01-06-09-22-21.gh-issue-91351.iq2vZ_.rst | 5 - ...-01-10-14-11-17.gh-issue-100892.qfBVYI.rst | 1 - ...-01-10-16-59-33.gh-issue-100923.ypJAX-.rst | 2 - ...-01-11-22-52-19.gh-issue-100942.ontOy_.rst | 2 - ...-01-12-13-46-49.gh-issue-100982.mJ234s.rst | 4 - ...-01-13-12-56-20.gh-issue-100762.YvHaQJ.rst | 3 - ...-01-14-17-03-08.gh-issue-101037.9ATNuf.rst | 2 - ...-01-15-03-26-04.gh-issue-101046.g2CM4S.rst | 2 - ...-01-24-17-13-32.gh-issue-101291.Yr6u_c.rst | 2 - ...-01-28-13-11-52.gh-issue-101266.AxV3OF.rst | 1 - ...-01-28-20-31-42.gh-issue-101372.8BcpCC.rst | 2 - ...-01-30-08-59-47.gh-issue-101400.Di_ZFm.rst | 2 - ...3-01-30-11-56-09.gh-issue-59956.7xqnC_.rst | 3 - ...3-02-06-20-13-36.gh-issue-92173.RQE0mk.rst | 2 - ...2-06-19-22-04-47.gh-issue-88324.GHhSQ1.rst | 3 - ...8-05-21-17-18-00.gh-issue-77772.Fhg84L.rst | 3 - .../2020-04-18-17-45-03.bpo-29847.Uxtbq0.rst | 1 - .../2020-11-20-21-06-08.bpo-40077.M-iZq3.rst | 1 - .../2022-02-05-12-01-58.bpo-38941.8IhvyG.rst | 4 - ...2-07-22-13-38-37.gh-issue-94518._ZP0cz.rst | 2 - ...2-09-26-21-18-47.gh-issue-60580.0hBgde.rst | 3 - ...2-11-14-03-06-03.gh-issue-88597.EYJA-Q.rst | 1 - ...2-11-15-23-30-39.gh-issue-86682.gK9i1N.rst | 2 - ...2-11-24-21-52-31.gh-issue-99266.88GcV9.rst | 1 - ...2-12-10-15-30-17.gh-issue-67790.P9YUZM.rst | 2 - ...2-12-11-14-38-59.gh-issue-99952.IYGLzr.rst | 2 - ...2-12-19-23-19-26.gh-issue-96290.qFjsi6.rst | 5 - ...-12-21-17-49-50.gh-issue-100160.N0NHRj.rst | 3 - ...-01-04-14-42-59.gh-issue-100750.iFJs5Y.rst | 1 - ...3-01-08-00-12-44.gh-issue-39615.gn4PhB.rst | 3 - ...-01-12-01-18-13.gh-issue-100573.KDskqo.rst | 1 - ...-01-12-21-22-20.gh-issue-101000.wz4Xgc.rst | 3 - ...-01-14-12-58-21.gh-issue-101015.stWFid.rst | 2 - ...3-01-15-09-11-30.gh-issue-94518.jvxtxm.rst | 3 - ...-01-18-17-58-50.gh-issue-101144.FHd8Un.rst | 4 - ...-01-20-10-46-59.gh-issue-101143.hJo8hu.rst | 2 - ...-01-21-16-50-22.gh-issue-100795.NPMZf7.rst | 3 - ...3-01-24-12-53-59.gh-issue-92123.jf6TO5.rst | 2 - ...-01-25-18-07-20.gh-issue-101326.KL4SFv.rst | 1 - ...-01-26-01-25-56.gh-issue-101317.vWaS1x.rst | 2 - ...-01-26-06-44-35.gh-issue-101323.h8Hk11.rst | 2 - ...-02-04-21-01-49.gh-issue-101570.lbtUsD.rst | 1 - ...-02-05-14-39-49.gh-issue-101541.Mo3ppp.rst | 1 - ...2-11-08-12-06-52.gh-issue-99108.4Wrsuh.rst | 4 - ...-02-04-17-24-33.gh-issue-101334._yOqwg.rst | 1 - ...-01-11-14-42-11.gh-issue-100247.YfEmSz.rst | 2 - ...-01-11-16-28-09.gh-issue-100320.2DU2it.rst | 3 - ...3-01-17-18-17-58.gh-issue-82052.mWyysT.rst | 1 - ...-01-18-18-25-18.gh-issue-101135.HF9VlG.rst | 3 - ...3-01-25-00-23-31.gh-issue-99834.WN41lc.rst | 1 - ...-01-31-16-50-07.gh-issue-101467.ye9t-L.rst | 3 - ...-02-03-17-53-06.gh-issue-101543.cORAT4.rst | 2 - README.rst | 2 +- 68 files changed, 680 insertions(+), 145 deletions(-) create mode 100644 Misc/NEWS.d/3.12.0a5.rst delete mode 100644 Misc/NEWS.d/next/Build/2022-08-30-10-16-31.gh-issue-96305.274i8B.rst delete mode 100644 Misc/NEWS.d/next/Build/2022-10-25-11-53-55.gh-issue-98636.e0RPAr.rst delete mode 100644 Misc/NEWS.d/next/Build/2022-10-27-09-57-12.gh-issue-98705.H11XmR.rst delete mode 100644 Misc/NEWS.d/next/Build/2023-01-15-11-22-15.gh-issue-101060.0mYk9E.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-01-21-10-31-35.gh-issue-101152.xvM8pL.rst delete mode 100644 Misc/NEWS.d/next/Build/2023-01-26-19-02-11.gh-issue-77532.cXD8bg.rst delete mode 100644 Misc/NEWS.d/next/Build/2023-02-02-23-43-46.gh-issue-101522.lnUDta.rst delete mode 100644 Misc/NEWS.d/next/Build/2023-02-04-06-59-07.gh-issue-101282.7sQz5l.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2018-02-05-21-54-46.bpo-32780.Dtiz8z.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-03-14-33-23.gh-issue-100712.po6xyB.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-03-20-59-20.gh-issue-100726.W9huFl.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-06-09-22-21.gh-issue-91351.iq2vZ_.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-10-14-11-17.gh-issue-100892.qfBVYI.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-10-16-59-33.gh-issue-100923.ypJAX-.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-11-22-52-19.gh-issue-100942.ontOy_.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-12-13-46-49.gh-issue-100982.mJ234s.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-13-12-56-20.gh-issue-100762.YvHaQJ.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-14-17-03-08.gh-issue-101037.9ATNuf.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-15-03-26-04.gh-issue-101046.g2CM4S.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-24-17-13-32.gh-issue-101291.Yr6u_c.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-28-13-11-52.gh-issue-101266.AxV3OF.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-28-20-31-42.gh-issue-101372.8BcpCC.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-30-08-59-47.gh-issue-101400.Di_ZFm.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-30-11-56-09.gh-issue-59956.7xqnC_.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-06-20-13-36.gh-issue-92173.RQE0mk.rst delete mode 100644 Misc/NEWS.d/next/Documentation/2022-06-19-22-04-47.gh-issue-88324.GHhSQ1.rst delete mode 100644 Misc/NEWS.d/next/Library/2018-05-21-17-18-00.gh-issue-77772.Fhg84L.rst delete mode 100644 Misc/NEWS.d/next/Library/2020-04-18-17-45-03.bpo-29847.Uxtbq0.rst delete mode 100644 Misc/NEWS.d/next/Library/2020-11-20-21-06-08.bpo-40077.M-iZq3.rst delete mode 100644 Misc/NEWS.d/next/Library/2022-02-05-12-01-58.bpo-38941.8IhvyG.rst delete mode 100644 Misc/NEWS.d/next/Library/2022-07-22-13-38-37.gh-issue-94518._ZP0cz.rst delete mode 100644 Misc/NEWS.d/next/Library/2022-09-26-21-18-47.gh-issue-60580.0hBgde.rst delete mode 100644 Misc/NEWS.d/next/Library/2022-11-14-03-06-03.gh-issue-88597.EYJA-Q.rst delete mode 100644 Misc/NEWS.d/next/Library/2022-11-15-23-30-39.gh-issue-86682.gK9i1N.rst delete mode 100644 Misc/NEWS.d/next/Library/2022-11-24-21-52-31.gh-issue-99266.88GcV9.rst delete mode 100644 Misc/NEWS.d/next/Library/2022-12-10-15-30-17.gh-issue-67790.P9YUZM.rst delete mode 100644 Misc/NEWS.d/next/Library/2022-12-11-14-38-59.gh-issue-99952.IYGLzr.rst delete mode 100644 Misc/NEWS.d/next/Library/2022-12-19-23-19-26.gh-issue-96290.qFjsi6.rst delete mode 100644 Misc/NEWS.d/next/Library/2022-12-21-17-49-50.gh-issue-100160.N0NHRj.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-01-04-14-42-59.gh-issue-100750.iFJs5Y.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-01-08-00-12-44.gh-issue-39615.gn4PhB.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-01-12-21-22-20.gh-issue-101000.wz4Xgc.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-01-14-12-58-21.gh-issue-101015.stWFid.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-01-15-09-11-30.gh-issue-94518.jvxtxm.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-01-18-17-58-50.gh-issue-101144.FHd8Un.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-01-20-10-46-59.gh-issue-101143.hJo8hu.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-01-21-16-50-22.gh-issue-100795.NPMZf7.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-01-24-12-53-59.gh-issue-92123.jf6TO5.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-01-25-18-07-20.gh-issue-101326.KL4SFv.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-01-26-01-25-56.gh-issue-101317.vWaS1x.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-01-26-06-44-35.gh-issue-101323.h8Hk11.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-02-04-21-01-49.gh-issue-101570.lbtUsD.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-02-05-14-39-49.gh-issue-101541.Mo3ppp.rst delete mode 100644 Misc/NEWS.d/next/Security/2022-11-08-12-06-52.gh-issue-99108.4Wrsuh.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-02-04-17-24-33.gh-issue-101334._yOqwg.rst delete mode 100644 Misc/NEWS.d/next/Windows/2023-01-11-14-42-11.gh-issue-100247.YfEmSz.rst delete mode 100644 Misc/NEWS.d/next/Windows/2023-01-11-16-28-09.gh-issue-100320.2DU2it.rst delete mode 100644 Misc/NEWS.d/next/Windows/2023-01-17-18-17-58.gh-issue-82052.mWyysT.rst delete mode 100644 Misc/NEWS.d/next/Windows/2023-01-18-18-25-18.gh-issue-101135.HF9VlG.rst delete mode 100644 Misc/NEWS.d/next/Windows/2023-01-25-00-23-31.gh-issue-99834.WN41lc.rst delete mode 100644 Misc/NEWS.d/next/Windows/2023-01-31-16-50-07.gh-issue-101467.ye9t-L.rst delete mode 100644 Misc/NEWS.d/next/Windows/2023-02-03-17-53-06.gh-issue-101543.cORAT4.rst diff --git a/Include/patchlevel.h b/Include/patchlevel.h index 3a3e40c2e09229..df6098be8bbcc9 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -20,10 +20,10 @@ #define PY_MINOR_VERSION 12 #define PY_MICRO_VERSION 0 #define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_ALPHA -#define PY_RELEASE_SERIAL 4 +#define PY_RELEASE_SERIAL 5 /* Version as a string */ -#define PY_VERSION "3.12.0a4+" +#define PY_VERSION "3.12.0a5" /*--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 11b75037e78b46..e7f403d3ffbf12 100644 --- a/Lib/pydoc_data/topics.py +++ b/Lib/pydoc_data/topics.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Autogenerated by Sphinx on Tue Jan 10 13:08:32 2023 +# Autogenerated by Sphinx on Tue Feb 7 13:18:04 2023 topics = {'assert': 'The "assert" statement\n' '**********************\n' '\n' @@ -4647,6 +4647,18 @@ 'the source. The extension interface uses the modules "bdb" and ' '"cmd".\n' '\n' + 'See also:\n' + '\n' + ' Module "faulthandler"\n' + ' Used to dump Python tracebacks explicitly, on a fault, ' + 'after a\n' + ' timeout, or on a user signal.\n' + '\n' + ' Module "traceback"\n' + ' Standard interface to extract, format and print stack ' + 'traces of\n' + ' Python programs.\n' + '\n' 'The debugger’s prompt is "(Pdb)". Typical usage to run a program ' 'under\n' 'control of the debugger is:\n' diff --git a/Misc/NEWS.d/3.12.0a5.rst b/Misc/NEWS.d/3.12.0a5.rst new file mode 100644 index 00000000000000..f6f8de46cf70d9 --- /dev/null +++ b/Misc/NEWS.d/3.12.0a5.rst @@ -0,0 +1,664 @@ +.. date: 2022-11-08-12-06-52 +.. gh-issue: 99108 +.. nonce: 4Wrsuh +.. release date: 2023-02-07 +.. section: Security + +Replace the builtin :mod:`hashlib` implementations of SHA2-224 and SHA2-256 +originally from LibTomCrypt with formally verified, side-channel resistant +code from the `HACL* `_ project. +The builtins remain a fallback only used when OpenSSL does not provide them. + +.. + +.. date: 2023-02-06-20-13-36 +.. gh-issue: 92173 +.. nonce: RQE0mk +.. section: Core and Builtins + +Fix the ``defs`` and ``kwdefs`` arguments to :c:func:`PyEval_EvalCodeEx` and +a reference leak in that function. + +.. + +.. date: 2023-01-30-11-56-09 +.. gh-issue: 59956 +.. nonce: 7xqnC_ +.. section: Core and Builtins + +The GILState API is now partially compatible with subinterpreters. +Previously, ``PyThreadState_GET()`` and ``PyGILState_GetThisThreadState()`` +would get out of sync, causing inconsistent behavior and crashes. + +.. + +.. date: 2023-01-30-08-59-47 +.. gh-issue: 101400 +.. nonce: Di_ZFm +.. 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. + +.. + +.. date: 2023-01-28-20-31-42 +.. gh-issue: 101372 +.. nonce: 8BcpCC +.. section: Core and Builtins + +Fix :func:`~unicodedata.is_normalized` to properly handle the UCD 3.2.0 +cases. Patch by Dong-hee Na. + +.. + +.. date: 2023-01-28-13-11-52 +.. gh-issue: 101266 +.. nonce: AxV3OF +.. section: Core and Builtins + +Fix :func:`sys.getsizeof` reporting for :class:`int` subclasses. + +.. + +.. date: 2023-01-24-17-13-32 +.. gh-issue: 101291 +.. nonce: Yr6u_c +.. section: Core and Builtins + +Refactor the ``PyLongObject`` struct into a normal Python object header and +a ``PyLongValue`` struct. + +.. + +.. date: 2023-01-15-03-26-04 +.. gh-issue: 101046 +.. nonce: g2CM4S +.. section: Core and Builtins + +Fix a possible memory leak in the parser when raising :exc:`MemoryError`. +Patch by Pablo Galindo + +.. + +.. date: 2023-01-14-17-03-08 +.. gh-issue: 101037 +.. nonce: 9ATNuf +.. section: Core and Builtins + +Fix potential memory underallocation issue for instances of :class:`int` +subclasses with value zero. + +.. + +.. date: 2023-01-13-12-56-20 +.. gh-issue: 100762 +.. nonce: YvHaQJ +.. section: Core and Builtins + +Record the (virtual) exception block depth in the oparg of +:opcode:`YIELD_VALUE`. Use this to avoid the expensive ``throw()`` when +closing generators (and coroutines) that can be closed trivially. + +.. + +.. date: 2023-01-12-13-46-49 +.. gh-issue: 100982 +.. nonce: mJ234s +.. section: Core and Builtins + +Adds a new :opcode:`COMPARE_AND_BRANCH` instruction. This is a bit more +efficient when performing a comparison immediately followed by a branch, and +restores the design intent of PEP 659 that specializations are local to a +single instruction. + +.. + +.. date: 2023-01-11-22-52-19 +.. gh-issue: 100942 +.. nonce: ontOy_ +.. section: Core and Builtins + +Fixed segfault in property.getter/setter/deleter that occurred when a +property subclass overrode the ``__new__`` method to return a non-property +instance. + +.. + +.. date: 2023-01-10-16-59-33 +.. gh-issue: 100923 +.. nonce: ypJAX- +.. section: Core and Builtins + +Remove the ``mask`` cache entry for the :opcode:`COMPARE_OP` instruction and +embed the mask into the oparg. + +.. + +.. date: 2023-01-10-14-11-17 +.. gh-issue: 100892 +.. nonce: qfBVYI +.. section: Core and Builtins + +Fix race while iterating over thread states in clearing +:class:`threading.local`. Patch by Kumar Aditya. + +.. + +.. date: 2023-01-06-09-22-21 +.. gh-issue: 91351 +.. nonce: iq2vZ_ +.. section: Core and Builtins + +Fix a case where re-entrant imports could corrupt the import deadlock +detection code and cause a :exc:`KeyError` to be raised out of +:mod:`importlib/_bootstrap`. In addition to the straightforward cases, this +could also happen when garbage collection leads to a warning being emitted +-- as happens when it collects an open socket or file) + +.. + +.. date: 2023-01-03-20-59-20 +.. gh-issue: 100726 +.. nonce: W9huFl +.. section: Core and Builtins + +Optimize construction of ``range`` object for medium size integers. + +.. + +.. date: 2023-01-03-14-33-23 +.. gh-issue: 100712 +.. nonce: po6xyB +.. section: Core and Builtins + +Added option to build cpython with specialization disabled, by setting +``ENABLE_SPECIALIZATION=False`` in :mod:`opcode`, followed by ``make +regen-all``. + +.. + +.. bpo: 32780 +.. date: 2018-02-05-21-54-46 +.. nonce: Dtiz8z +.. section: Core and Builtins + +Inter-field padding is now inserted into the PEP3118 format strings obtained +from :class:`ctypes.Structure` objects, reflecting their true representation +in memory. + +.. + +.. date: 2023-02-05-14-39-49 +.. gh-issue: 101541 +.. nonce: Mo3ppp +.. section: Library + +[Enum] - fix psuedo-flag creation + +.. + +.. date: 2023-02-04-21-01-49 +.. gh-issue: 101570 +.. nonce: lbtUsD +.. section: Library + +Upgrade pip wheel bundled with ensurepip (pip 23.0) + +.. + +.. date: 2023-01-26-06-44-35 +.. gh-issue: 101323 +.. nonce: h8Hk11 +.. section: Library + +Fix a bug where errors where not thrown by zlib._ZlibDecompressor if +encountered during decompressing. + +.. + +.. date: 2023-01-26-01-25-56 +.. gh-issue: 101317 +.. nonce: vWaS1x +.. section: Library + +Add *ssl_shutdown_timeout* parameter for +:meth:`asyncio.StreamWriter.start_tls`. + +.. + +.. date: 2023-01-25-18-07-20 +.. gh-issue: 101326 +.. nonce: KL4SFv +.. section: Library + +Fix regression when passing ``None`` as second or third argument to +``FutureIter.throw``. + +.. + +.. date: 2023-01-24-12-53-59 +.. gh-issue: 92123 +.. nonce: jf6TO5 +.. section: Library + +Adapt the ``_elementtree`` extension module to multi-phase init +(:pep:`489`). Patches by Erlend E. Aasland. + +.. + +.. date: 2023-01-21-16-50-22 +.. gh-issue: 100795 +.. nonce: NPMZf7 +.. section: Library + +Avoid potential unexpected ``freeaddrinfo`` call (double free) in +:mod:`socket` when when a libc ``getaddrinfo()`` implementation leaves +garbage in an output pointer when returning an error. Original patch by +Sergey G. Brester. + +.. + +.. date: 2023-01-20-10-46-59 +.. gh-issue: 101143 +.. nonce: hJo8hu +.. section: Library + +Remove unused references to :class:`~asyncio.TimerHandle` in +``asyncio.base_events.BaseEventLoop._add_callback``. + +.. + +.. date: 2023-01-18-17-58-50 +.. gh-issue: 101144 +.. nonce: FHd8Un +.. section: Library + +Make :func:`zipfile.Path.open` and :func:`zipfile.Path.read_text` also +accept ``encoding`` as a positional argument. This was the behavior in +Python 3.9 and earlier. 3.10 introduced a regression where supplying it as +a positional argument would lead to a :exc:`TypeError`. + +.. + +.. date: 2023-01-15-09-11-30 +.. gh-issue: 94518 +.. nonce: jvxtxm +.. section: Library + +Group-related variables of ``_posixsubprocess`` module are renamed to stress +that supplimentary group affinity is added to a fork, not replace the +inherited ones. Patch by Oleg Iarygin. + +.. + +.. date: 2023-01-14-12-58-21 +.. gh-issue: 101015 +.. nonce: stWFid +.. section: Library + +Fix :func:`typing.get_type_hints` on ``'*tuple[...]'`` and ``*tuple[...]``. +It must not drop the ``Unpack`` part. + +.. + +.. date: 2023-01-12-21-22-20 +.. gh-issue: 101000 +.. nonce: wz4Xgc +.. section: Library + +Add :func:`os.path.splitroot()`, which splits a path into a 3-item tuple +``(drive, root, tail)``. This new function is used by :mod:`pathlib` to +improve the performance of path construction by up to a third. + +.. + +.. date: 2023-01-12-01-18-13 +.. gh-issue: 100573 +.. nonce: KDskqo +.. section: Library + +Fix a Windows :mod:`asyncio` bug with named pipes where a client doing +``os.stat()`` on the pipe would cause an error in the server that disabled +serving future requests. + +.. + +.. date: 2023-01-08-00-12-44 +.. gh-issue: 39615 +.. nonce: gn4PhB +.. section: Library + +:func:`warnings.warn` now has the ability to skip stack frames based on code +filename prefix rather than only a numeric ``stacklevel`` via the new +``skip_file_prefixes`` keyword argument. + +.. + +.. date: 2023-01-04-14-42-59 +.. gh-issue: 100750 +.. nonce: iFJs5Y +.. section: Library + +pass encoding kwarg to subprocess in platform + +.. + +.. date: 2022-12-21-17-49-50 +.. gh-issue: 100160 +.. nonce: N0NHRj +.. section: Library + +Emit a deprecation warning in +:meth:`asyncio.DefaultEventLoopPolicy.get_event_loop` if there is no current +event loop set and it decides to create one. + +.. + +.. date: 2022-12-19-23-19-26 +.. gh-issue: 96290 +.. nonce: qFjsi6 +.. section: Library + +Fix handling of partial and invalid UNC drives in ``ntpath.splitdrive()``, +and in ``ntpath.normpath()`` on non-Windows systems. Paths such as +'\\server' and '\\' are now considered by ``splitdrive()`` to contain only a +drive, and consequently are not modified by ``normpath()`` on non-Windows +systems. The behaviour of ``normpath()`` on Windows systems is unaffected, +as native OS APIs are used. Patch by Eryk Sun, with contributions by Barney +Gale. + +.. + +.. date: 2022-12-11-14-38-59 +.. gh-issue: 99952 +.. nonce: IYGLzr +.. section: Library + +Fix a reference undercounting issue in :class:`ctypes.Structure` with +``from_param()`` results larger than a C pointer. + +.. + +.. date: 2022-12-10-15-30-17 +.. gh-issue: 67790 +.. nonce: P9YUZM +.. section: Library + +Add float-style formatting support for :class:`fractions.Fraction` +instances. + +.. + +.. date: 2022-11-24-21-52-31 +.. gh-issue: 99266 +.. nonce: 88GcV9 +.. section: Library + +Preserve more detailed error messages in :mod:`ctypes`. + +.. + +.. date: 2022-11-15-23-30-39 +.. gh-issue: 86682 +.. nonce: gK9i1N +.. section: Library + +Ensure runtime-created collections have the correct module name using the +newly added (internal) :func:`sys._getframemodulename`. + +.. + +.. date: 2022-11-14-03-06-03 +.. gh-issue: 88597 +.. nonce: EYJA-Q +.. section: Library + +:mod:`uuid` now has a command line interface. Try ``python -m uuid -h``. + +.. + +.. date: 2022-09-26-21-18-47 +.. gh-issue: 60580 +.. nonce: 0hBgde +.. section: Library + +:data:`ctypes.wintypes.BYTE` definition changed from :data:`~ctypes.c_byte` +to :data:`~ctypes.c_ubyte` to match Windows SDK. Patch by Anatoly Techtonik +and Oleg Iarygin. + +.. + +.. date: 2022-07-22-13-38-37 +.. gh-issue: 94518 +.. nonce: _ZP0cz +.. section: Library + +``_posixsubprocess`` now initializes all UID and GID variables using a +reserved ``-1`` value instead of a separate flag. Patch by Oleg Iarygin. + +.. + +.. bpo: 38941 +.. date: 2022-02-05-12-01-58 +.. nonce: 8IhvyG +.. section: Library + +The :mod:`xml.etree.ElementTree` module now emits :exc:`DeprecationWarning` +when testing the truth value of an :class:`xml.etree.ElementTree.Element`. +Before, the Python implementation emitted :exc:`FutureWarning`, and the C +implementation emitted nothing. + +.. + +.. bpo: 40077 +.. date: 2020-11-20-21-06-08 +.. nonce: M-iZq3 +.. section: Library + +Convert :mod:`elementtree` types to heap types. Patch by Erlend E. Aasland. + +.. + +.. bpo: 29847 +.. date: 2020-04-18-17-45-03 +.. nonce: Uxtbq0 +.. section: Library + +Fix a bug where :class:`pathlib.Path` accepted and ignored keyword +arguments. Patch provided by Yurii Karabas. + +.. + +.. date: 2018-05-21-17-18-00 +.. gh-issue: 77772 +.. nonce: Fhg84L +.. section: Library + +:class:`ctypes.CDLL`, :class:`ctypes.OleDLL`, :class:`ctypes.WinDLL`, and +:class:`ctypes.PyDLL` now accept :term:`path-like objects ` as their ``name`` argument. Patch by Robert Hoelzl. + +.. + +.. date: 2022-06-19-22-04-47 +.. gh-issue: 88324 +.. nonce: GHhSQ1 +.. section: Documentation + +Reword :mod:`subprocess` to emphasize default behavior of *stdin*, *stdout*, +and *stderr* arguments. Remove inaccurate statement about child file handle +inheritance. + +.. + +.. date: 2023-02-04-17-24-33 +.. gh-issue: 101334 +.. nonce: _yOqwg +.. section: Tests + +``test_tarfile`` has been updated to pass when run as a high UID. + +.. + +.. date: 2023-02-04-06-59-07 +.. gh-issue: 101282 +.. nonce: 7sQz5l +.. section: Build + +Update BOLT configration not to use depreacted usage of ``--split +functions``. Patch by Dong-hee Na. + +.. + +.. date: 2023-02-02-23-43-46 +.. gh-issue: 101522 +.. nonce: lnUDta +.. section: Build + +Allow overriding Windows dependencies versions and paths using MSBuild +properties. + +.. + +.. date: 2023-01-26-19-02-11 +.. gh-issue: 77532 +.. nonce: cXD8bg +.. section: Build + +Minor fixes to allow building with ``PlatformToolset=ClangCL`` on Windows. + +.. + +.. date: 2023-01-21-10-31-35 +.. gh-issue: 101152 +.. nonce: xvM8pL +.. section: Build + +In accordance with :PEP:`699`, the ``ma_version_tag`` field in +:c:type:`PyDictObject` is deprecated for extension modules. Accessing this +field will generate a compiler warning at compile time. This field will be +removed in Python 3.14. + +.. + +.. 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-01-15-11-22-15 +.. gh-issue: 101060 +.. nonce: 0mYk9E +.. section: Build + +Conditionally add ``-fno-reorder-blocks-and-partition`` in configure. +Effectively fixes ``--enable-bolt`` when using Clang, as this appears to be +a GCC-only flag. + +.. + +.. date: 2022-10-27-09-57-12 +.. gh-issue: 98705 +.. nonce: H11XmR +.. section: Build + +``__bool__`` is defined in AIX system header files which breaks the build in +AIX, so undefine it. + +.. + +.. date: 2022-10-25-11-53-55 +.. gh-issue: 98636 +.. nonce: e0RPAr +.. section: Build + +Fix a regression in detecting ``gdbm_compat`` library for the ``_gdbm`` +module build. + +.. + +.. date: 2022-08-30-10-16-31 +.. gh-issue: 96305 +.. nonce: 274i8B +.. section: Build + +``_aix_support`` now uses a simple code to get platform details rather than +the now non-existent ``_bootsubprocess`` during bootstrap. + +.. + +.. date: 2023-02-03-17-53-06 +.. gh-issue: 101543 +.. nonce: cORAT4 +.. section: Windows + +Ensure the install path in the registry is only used when the standard +library hasn't been located in any other way. + +.. + +.. date: 2023-01-31-16-50-07 +.. gh-issue: 101467 +.. nonce: ye9t-L +.. section: Windows + +The ``py.exe`` launcher now correctly filters when only a single runtime is +installed. It also correctly handles prefix matches on tags so that ``-3.1`` +does not match ``3.11``, but would still match ``3.1-32``. + +.. + +.. date: 2023-01-25-00-23-31 +.. gh-issue: 99834 +.. nonce: WN41lc +.. section: Windows + +Updates bundled copy of Tcl/Tk to 8.6.13.0 + +.. + +.. date: 2023-01-18-18-25-18 +.. gh-issue: 101135 +.. nonce: HF9VlG +.. section: Windows + +Restore ability to launch older 32-bit versions from the :file:`py.exe` +launcher when both 32-bit and 64-bit installs of the same version are +available. + +.. + +.. date: 2023-01-17-18-17-58 +.. gh-issue: 82052 +.. nonce: mWyysT +.. section: Windows + +Fixed an issue where writing more than 32K of Unicode output to the console +screen in one go can result in mojibake. + +.. + +.. date: 2023-01-11-16-28-09 +.. gh-issue: 100320 +.. nonce: 2DU2it +.. section: Windows + +Ensures the ``PythonPath`` registry key from an install is used when +launching from a different copy of Python that relies on an existing install +to provide a copy of its modules and standard library. + +.. + +.. date: 2023-01-11-14-42-11 +.. gh-issue: 100247 +.. nonce: YfEmSz +.. section: Windows + +Restores support for the :file:`py.exe` launcher finding shebang commands in +its configuration file using the full command name. diff --git a/Misc/NEWS.d/next/Build/2022-08-30-10-16-31.gh-issue-96305.274i8B.rst b/Misc/NEWS.d/next/Build/2022-08-30-10-16-31.gh-issue-96305.274i8B.rst deleted file mode 100644 index 64a48da658f21f..00000000000000 --- a/Misc/NEWS.d/next/Build/2022-08-30-10-16-31.gh-issue-96305.274i8B.rst +++ /dev/null @@ -1,2 +0,0 @@ -``_aix_support`` now uses a simple code to get platform details rather than -the now non-existent ``_bootsubprocess`` during bootstrap. diff --git a/Misc/NEWS.d/next/Build/2022-10-25-11-53-55.gh-issue-98636.e0RPAr.rst b/Misc/NEWS.d/next/Build/2022-10-25-11-53-55.gh-issue-98636.e0RPAr.rst deleted file mode 100644 index 26a7cc8acaf243..00000000000000 --- a/Misc/NEWS.d/next/Build/2022-10-25-11-53-55.gh-issue-98636.e0RPAr.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a regression in detecting ``gdbm_compat`` library for the ``_gdbm`` -module build. diff --git a/Misc/NEWS.d/next/Build/2022-10-27-09-57-12.gh-issue-98705.H11XmR.rst b/Misc/NEWS.d/next/Build/2022-10-27-09-57-12.gh-issue-98705.H11XmR.rst deleted file mode 100644 index 4519853cdbadd1..00000000000000 --- a/Misc/NEWS.d/next/Build/2022-10-27-09-57-12.gh-issue-98705.H11XmR.rst +++ /dev/null @@ -1,2 +0,0 @@ -``__bool__`` is defined in AIX system header files which breaks the build in -AIX, so undefine it. diff --git a/Misc/NEWS.d/next/Build/2023-01-15-11-22-15.gh-issue-101060.0mYk9E.rst b/Misc/NEWS.d/next/Build/2023-01-15-11-22-15.gh-issue-101060.0mYk9E.rst deleted file mode 100644 index bebbf8c898d547..00000000000000 --- a/Misc/NEWS.d/next/Build/2023-01-15-11-22-15.gh-issue-101060.0mYk9E.rst +++ /dev/null @@ -1,3 +0,0 @@ -Conditionally add ``-fno-reorder-blocks-and-partition`` in configure. -Effectively fixes ``--enable-bolt`` when using Clang, as this appears to be -a GCC-only flag. 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-01-21-10-31-35.gh-issue-101152.xvM8pL.rst b/Misc/NEWS.d/next/Build/2023-01-21-10-31-35.gh-issue-101152.xvM8pL.rst deleted file mode 100644 index e35b6178aa4cf9..00000000000000 --- a/Misc/NEWS.d/next/Build/2023-01-21-10-31-35.gh-issue-101152.xvM8pL.rst +++ /dev/null @@ -1,3 +0,0 @@ -In accordance with :PEP:`699`, the ``ma_version_tag`` field in :c:type:`PyDictObject` -is deprecated for extension modules. Accessing this field will generate a compiler -warning at compile time. This field will be removed in Python 3.14. diff --git a/Misc/NEWS.d/next/Build/2023-01-26-19-02-11.gh-issue-77532.cXD8bg.rst b/Misc/NEWS.d/next/Build/2023-01-26-19-02-11.gh-issue-77532.cXD8bg.rst deleted file mode 100644 index 5a746dca2e7d8d..00000000000000 --- a/Misc/NEWS.d/next/Build/2023-01-26-19-02-11.gh-issue-77532.cXD8bg.rst +++ /dev/null @@ -1 +0,0 @@ -Minor fixes to allow building with ``PlatformToolset=ClangCL`` on Windows. diff --git a/Misc/NEWS.d/next/Build/2023-02-02-23-43-46.gh-issue-101522.lnUDta.rst b/Misc/NEWS.d/next/Build/2023-02-02-23-43-46.gh-issue-101522.lnUDta.rst deleted file mode 100644 index 2e7f9029e9ee54..00000000000000 --- a/Misc/NEWS.d/next/Build/2023-02-02-23-43-46.gh-issue-101522.lnUDta.rst +++ /dev/null @@ -1,2 +0,0 @@ -Allow overriding Windows dependencies versions and paths using MSBuild -properties. diff --git a/Misc/NEWS.d/next/Build/2023-02-04-06-59-07.gh-issue-101282.7sQz5l.rst b/Misc/NEWS.d/next/Build/2023-02-04-06-59-07.gh-issue-101282.7sQz5l.rst deleted file mode 100644 index 49d48564667349..00000000000000 --- a/Misc/NEWS.d/next/Build/2023-02-04-06-59-07.gh-issue-101282.7sQz5l.rst +++ /dev/null @@ -1,2 +0,0 @@ -Update BOLT configration not to use depreacted usage of ``--split -functions``. Patch by Dong-hee Na. diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-02-05-21-54-46.bpo-32780.Dtiz8z.rst b/Misc/NEWS.d/next/Core and Builtins/2018-02-05-21-54-46.bpo-32780.Dtiz8z.rst deleted file mode 100644 index 8996d471159a64..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2018-02-05-21-54-46.bpo-32780.Dtiz8z.rst +++ /dev/null @@ -1,3 +0,0 @@ -Inter-field padding is now inserted into the PEP3118 format strings obtained -from :class:`ctypes.Structure` objects, reflecting their true representation in -memory. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-03-14-33-23.gh-issue-100712.po6xyB.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-03-14-33-23.gh-issue-100712.po6xyB.rst deleted file mode 100644 index 3ebee0dd2aa48f..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-03-14-33-23.gh-issue-100712.po6xyB.rst +++ /dev/null @@ -1 +0,0 @@ -Added option to build cpython with specialization disabled, by setting ``ENABLE_SPECIALIZATION=False`` in :mod:`opcode`, followed by ``make regen-all``. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-03-20-59-20.gh-issue-100726.W9huFl.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-03-20-59-20.gh-issue-100726.W9huFl.rst deleted file mode 100644 index 2c93098b347a7f..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-03-20-59-20.gh-issue-100726.W9huFl.rst +++ /dev/null @@ -1 +0,0 @@ -Optimize construction of ``range`` object for medium size integers. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-06-09-22-21.gh-issue-91351.iq2vZ_.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-06-09-22-21.gh-issue-91351.iq2vZ_.rst deleted file mode 100644 index 19de1f8d0fb31e..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-06-09-22-21.gh-issue-91351.iq2vZ_.rst +++ /dev/null @@ -1,5 +0,0 @@ -Fix a case where re-entrant imports could corrupt the import deadlock -detection code and cause a :exc:`KeyError` to be raised out of -:mod:`importlib/_bootstrap`. In addition to the straightforward cases, this -could also happen when garbage collection leads to a warning being emitted -- -as happens when it collects an open socket or file) diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-10-14-11-17.gh-issue-100892.qfBVYI.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-10-14-11-17.gh-issue-100892.qfBVYI.rst deleted file mode 100644 index f2576becc2fcfc..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-10-14-11-17.gh-issue-100892.qfBVYI.rst +++ /dev/null @@ -1 +0,0 @@ -Fix race while iterating over thread states in clearing :class:`threading.local`. Patch by Kumar Aditya. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-10-16-59-33.gh-issue-100923.ypJAX-.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-10-16-59-33.gh-issue-100923.ypJAX-.rst deleted file mode 100644 index b6b3f1d0c58f8e..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-10-16-59-33.gh-issue-100923.ypJAX-.rst +++ /dev/null @@ -1,2 +0,0 @@ -Remove the ``mask`` cache entry for the :opcode:`COMPARE_OP` instruction and -embed the mask into the oparg. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-11-22-52-19.gh-issue-100942.ontOy_.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-11-22-52-19.gh-issue-100942.ontOy_.rst deleted file mode 100644 index daccea255b1626..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-11-22-52-19.gh-issue-100942.ontOy_.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fixed segfault in property.getter/setter/deleter that occurred when a property -subclass overrode the ``__new__`` method to return a non-property instance. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-12-13-46-49.gh-issue-100982.mJ234s.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-12-13-46-49.gh-issue-100982.mJ234s.rst deleted file mode 100644 index 4f43e783cd6a19..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-12-13-46-49.gh-issue-100982.mJ234s.rst +++ /dev/null @@ -1,4 +0,0 @@ -Adds a new :opcode:`COMPARE_AND_BRANCH` instruction. This is a bit more -efficient when performing a comparison immediately followed by a branch, and -restores the design intent of PEP 659 that specializations are local to a -single instruction. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-13-12-56-20.gh-issue-100762.YvHaQJ.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-13-12-56-20.gh-issue-100762.YvHaQJ.rst deleted file mode 100644 index 2f6b121439a985..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-13-12-56-20.gh-issue-100762.YvHaQJ.rst +++ /dev/null @@ -1,3 +0,0 @@ -Record the (virtual) exception block depth in the oparg of -:opcode:`YIELD_VALUE`. Use this to avoid the expensive ``throw()`` when -closing generators (and coroutines) that can be closed trivially. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-14-17-03-08.gh-issue-101037.9ATNuf.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-14-17-03-08.gh-issue-101037.9ATNuf.rst deleted file mode 100644 index a48756657a29d3..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-14-17-03-08.gh-issue-101037.9ATNuf.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix potential memory underallocation issue for instances of :class:`int` -subclasses with value zero. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-15-03-26-04.gh-issue-101046.g2CM4S.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-15-03-26-04.gh-issue-101046.g2CM4S.rst deleted file mode 100644 index f600473620f109..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-15-03-26-04.gh-issue-101046.g2CM4S.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a possible memory leak in the parser when raising :exc:`MemoryError`. -Patch by Pablo Galindo diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-24-17-13-32.gh-issue-101291.Yr6u_c.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-24-17-13-32.gh-issue-101291.Yr6u_c.rst deleted file mode 100644 index b585ff5a817edf..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-24-17-13-32.gh-issue-101291.Yr6u_c.rst +++ /dev/null @@ -1,2 +0,0 @@ -Refactor the ``PyLongObject`` struct into a normal Python object header and -a ``PyLongValue`` struct. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-28-13-11-52.gh-issue-101266.AxV3OF.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-28-13-11-52.gh-issue-101266.AxV3OF.rst deleted file mode 100644 index 51999bacb8de07..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-28-13-11-52.gh-issue-101266.AxV3OF.rst +++ /dev/null @@ -1 +0,0 @@ -Fix :func:`sys.getsizeof` reporting for :class:`int` subclasses. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-28-20-31-42.gh-issue-101372.8BcpCC.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-28-20-31-42.gh-issue-101372.8BcpCC.rst deleted file mode 100644 index 65a207e3f7e436..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-28-20-31-42.gh-issue-101372.8BcpCC.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix :func:`~unicodedata.is_normalized` to properly handle the UCD 3.2.0 -cases. Patch by Dong-hee Na. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-30-08-59-47.gh-issue-101400.Di_ZFm.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-30-08-59-47.gh-issue-101400.Di_ZFm.rst deleted file mode 100644 index f3dd783c01e7c0..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-30-08-59-47.gh-issue-101400.Di_ZFm.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix wrong lineno in exception message on :keyword:`continue` or -:keyword:`break` which are not in a loop. Patch by Dong-hee Na. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-30-11-56-09.gh-issue-59956.7xqnC_.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-30-11-56-09.gh-issue-59956.7xqnC_.rst deleted file mode 100644 index b3c1896b9493e1..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-30-11-56-09.gh-issue-59956.7xqnC_.rst +++ /dev/null @@ -1,3 +0,0 @@ -The GILState API is now partially compatible with subinterpreters. -Previously, ``PyThreadState_GET()`` and ``PyGILState_GetThisThreadState()`` -would get out of sync, causing inconsistent behavior and crashes. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-06-20-13-36.gh-issue-92173.RQE0mk.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-06-20-13-36.gh-issue-92173.RQE0mk.rst deleted file mode 100644 index 6b98aac2a46545..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-02-06-20-13-36.gh-issue-92173.RQE0mk.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix the ``defs`` and ``kwdefs`` arguments to :c:func:`PyEval_EvalCodeEx` -and a reference leak in that function. diff --git a/Misc/NEWS.d/next/Documentation/2022-06-19-22-04-47.gh-issue-88324.GHhSQ1.rst b/Misc/NEWS.d/next/Documentation/2022-06-19-22-04-47.gh-issue-88324.GHhSQ1.rst deleted file mode 100644 index 6c8d192daa7955..00000000000000 --- a/Misc/NEWS.d/next/Documentation/2022-06-19-22-04-47.gh-issue-88324.GHhSQ1.rst +++ /dev/null @@ -1,3 +0,0 @@ -Reword :mod:`subprocess` to emphasize default behavior of *stdin*, *stdout*, -and *stderr* arguments. Remove inaccurate statement about child file handle -inheritance. diff --git a/Misc/NEWS.d/next/Library/2018-05-21-17-18-00.gh-issue-77772.Fhg84L.rst b/Misc/NEWS.d/next/Library/2018-05-21-17-18-00.gh-issue-77772.Fhg84L.rst deleted file mode 100644 index 3a7c6d45297ba4..00000000000000 --- a/Misc/NEWS.d/next/Library/2018-05-21-17-18-00.gh-issue-77772.Fhg84L.rst +++ /dev/null @@ -1,3 +0,0 @@ -:class:`ctypes.CDLL`, :class:`ctypes.OleDLL`, :class:`ctypes.WinDLL`, -and :class:`ctypes.PyDLL` now accept :term:`path-like objects -` as their ``name`` argument. Patch by Robert Hoelzl. diff --git a/Misc/NEWS.d/next/Library/2020-04-18-17-45-03.bpo-29847.Uxtbq0.rst b/Misc/NEWS.d/next/Library/2020-04-18-17-45-03.bpo-29847.Uxtbq0.rst deleted file mode 100644 index 010d775a0d98ee..00000000000000 --- a/Misc/NEWS.d/next/Library/2020-04-18-17-45-03.bpo-29847.Uxtbq0.rst +++ /dev/null @@ -1 +0,0 @@ -Fix a bug where :class:`pathlib.Path` accepted and ignored keyword arguments. Patch provided by Yurii Karabas. diff --git a/Misc/NEWS.d/next/Library/2020-11-20-21-06-08.bpo-40077.M-iZq3.rst b/Misc/NEWS.d/next/Library/2020-11-20-21-06-08.bpo-40077.M-iZq3.rst deleted file mode 100644 index 8a74477a4b359d..00000000000000 --- a/Misc/NEWS.d/next/Library/2020-11-20-21-06-08.bpo-40077.M-iZq3.rst +++ /dev/null @@ -1 +0,0 @@ -Convert :mod:`elementtree` types to heap types. Patch by Erlend E. Aasland. diff --git a/Misc/NEWS.d/next/Library/2022-02-05-12-01-58.bpo-38941.8IhvyG.rst b/Misc/NEWS.d/next/Library/2022-02-05-12-01-58.bpo-38941.8IhvyG.rst deleted file mode 100644 index 5f996042260d09..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-02-05-12-01-58.bpo-38941.8IhvyG.rst +++ /dev/null @@ -1,4 +0,0 @@ -The :mod:`xml.etree.ElementTree` module now emits :exc:`DeprecationWarning` -when testing the truth value of an :class:`xml.etree.ElementTree.Element`. -Before, the Python implementation emitted :exc:`FutureWarning`, and the C -implementation emitted nothing. diff --git a/Misc/NEWS.d/next/Library/2022-07-22-13-38-37.gh-issue-94518._ZP0cz.rst b/Misc/NEWS.d/next/Library/2022-07-22-13-38-37.gh-issue-94518._ZP0cz.rst deleted file mode 100644 index a9d6d69f7effac..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-07-22-13-38-37.gh-issue-94518._ZP0cz.rst +++ /dev/null @@ -1,2 +0,0 @@ -``_posixsubprocess`` now initializes all UID and GID variables using a -reserved ``-1`` value instead of a separate flag. Patch by Oleg Iarygin. diff --git a/Misc/NEWS.d/next/Library/2022-09-26-21-18-47.gh-issue-60580.0hBgde.rst b/Misc/NEWS.d/next/Library/2022-09-26-21-18-47.gh-issue-60580.0hBgde.rst deleted file mode 100644 index 630e56cd2f7b87..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-09-26-21-18-47.gh-issue-60580.0hBgde.rst +++ /dev/null @@ -1,3 +0,0 @@ -:data:`ctypes.wintypes.BYTE` definition changed from -:data:`~ctypes.c_byte` to :data:`~ctypes.c_ubyte` to match Windows -SDK. Patch by Anatoly Techtonik and Oleg Iarygin. diff --git a/Misc/NEWS.d/next/Library/2022-11-14-03-06-03.gh-issue-88597.EYJA-Q.rst b/Misc/NEWS.d/next/Library/2022-11-14-03-06-03.gh-issue-88597.EYJA-Q.rst deleted file mode 100644 index a98e1ab4d15734..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-11-14-03-06-03.gh-issue-88597.EYJA-Q.rst +++ /dev/null @@ -1 +0,0 @@ -:mod:`uuid` now has a command line interface. Try ``python -m uuid -h``. diff --git a/Misc/NEWS.d/next/Library/2022-11-15-23-30-39.gh-issue-86682.gK9i1N.rst b/Misc/NEWS.d/next/Library/2022-11-15-23-30-39.gh-issue-86682.gK9i1N.rst deleted file mode 100644 index 64ef42a9a1c0b2..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-11-15-23-30-39.gh-issue-86682.gK9i1N.rst +++ /dev/null @@ -1,2 +0,0 @@ -Ensure runtime-created collections have the correct module name using -the newly added (internal) :func:`sys._getframemodulename`. diff --git a/Misc/NEWS.d/next/Library/2022-11-24-21-52-31.gh-issue-99266.88GcV9.rst b/Misc/NEWS.d/next/Library/2022-11-24-21-52-31.gh-issue-99266.88GcV9.rst deleted file mode 100644 index 97e9569e40a9bf..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-11-24-21-52-31.gh-issue-99266.88GcV9.rst +++ /dev/null @@ -1 +0,0 @@ -Preserve more detailed error messages in :mod:`ctypes`. diff --git a/Misc/NEWS.d/next/Library/2022-12-10-15-30-17.gh-issue-67790.P9YUZM.rst b/Misc/NEWS.d/next/Library/2022-12-10-15-30-17.gh-issue-67790.P9YUZM.rst deleted file mode 100644 index ba0db774f8b318..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-12-10-15-30-17.gh-issue-67790.P9YUZM.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add float-style formatting support for :class:`fractions.Fraction` -instances. diff --git a/Misc/NEWS.d/next/Library/2022-12-11-14-38-59.gh-issue-99952.IYGLzr.rst b/Misc/NEWS.d/next/Library/2022-12-11-14-38-59.gh-issue-99952.IYGLzr.rst deleted file mode 100644 index 09ec961249534f..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-12-11-14-38-59.gh-issue-99952.IYGLzr.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a reference undercounting issue in :class:`ctypes.Structure` with ``from_param()`` -results larger than a C pointer. diff --git a/Misc/NEWS.d/next/Library/2022-12-19-23-19-26.gh-issue-96290.qFjsi6.rst b/Misc/NEWS.d/next/Library/2022-12-19-23-19-26.gh-issue-96290.qFjsi6.rst deleted file mode 100644 index 33f98602bd1b71..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-12-19-23-19-26.gh-issue-96290.qFjsi6.rst +++ /dev/null @@ -1,5 +0,0 @@ -Fix handling of partial and invalid UNC drives in ``ntpath.splitdrive()``, and in -``ntpath.normpath()`` on non-Windows systems. Paths such as '\\server' and '\\' are now considered -by ``splitdrive()`` to contain only a drive, and consequently are not modified by ``normpath()`` on -non-Windows systems. The behaviour of ``normpath()`` on Windows systems is unaffected, as native -OS APIs are used. Patch by Eryk Sun, with contributions by Barney Gale. diff --git a/Misc/NEWS.d/next/Library/2022-12-21-17-49-50.gh-issue-100160.N0NHRj.rst b/Misc/NEWS.d/next/Library/2022-12-21-17-49-50.gh-issue-100160.N0NHRj.rst deleted file mode 100644 index d5cc785722d7fd..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-12-21-17-49-50.gh-issue-100160.N0NHRj.rst +++ /dev/null @@ -1,3 +0,0 @@ -Emit a deprecation warning in -:meth:`asyncio.DefaultEventLoopPolicy.get_event_loop` if there is no current -event loop set and it decides to create one. diff --git a/Misc/NEWS.d/next/Library/2023-01-04-14-42-59.gh-issue-100750.iFJs5Y.rst b/Misc/NEWS.d/next/Library/2023-01-04-14-42-59.gh-issue-100750.iFJs5Y.rst deleted file mode 100644 index be351532822c4b..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-04-14-42-59.gh-issue-100750.iFJs5Y.rst +++ /dev/null @@ -1 +0,0 @@ -pass encoding kwarg to subprocess in platform diff --git a/Misc/NEWS.d/next/Library/2023-01-08-00-12-44.gh-issue-39615.gn4PhB.rst b/Misc/NEWS.d/next/Library/2023-01-08-00-12-44.gh-issue-39615.gn4PhB.rst deleted file mode 100644 index 1d04cc2cd54b1e..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-08-00-12-44.gh-issue-39615.gn4PhB.rst +++ /dev/null @@ -1,3 +0,0 @@ -:func:`warnings.warn` now has the ability to skip stack frames based on code -filename prefix rather than only a numeric ``stacklevel`` via the new -``skip_file_prefixes`` keyword argument. diff --git a/Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst b/Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst deleted file mode 100644 index 97b95d18d1e426..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst +++ /dev/null @@ -1 +0,0 @@ -Fix a Windows :mod:`asyncio` bug with named pipes where a client doing ``os.stat()`` on the pipe would cause an error in the server that disabled serving future requests. diff --git a/Misc/NEWS.d/next/Library/2023-01-12-21-22-20.gh-issue-101000.wz4Xgc.rst b/Misc/NEWS.d/next/Library/2023-01-12-21-22-20.gh-issue-101000.wz4Xgc.rst deleted file mode 100644 index 2082361c41d697..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-12-21-22-20.gh-issue-101000.wz4Xgc.rst +++ /dev/null @@ -1,3 +0,0 @@ -Add :func:`os.path.splitroot()`, which splits a path into a 3-item tuple -``(drive, root, tail)``. This new function is used by :mod:`pathlib` to -improve the performance of path construction by up to a third. diff --git a/Misc/NEWS.d/next/Library/2023-01-14-12-58-21.gh-issue-101015.stWFid.rst b/Misc/NEWS.d/next/Library/2023-01-14-12-58-21.gh-issue-101015.stWFid.rst deleted file mode 100644 index b9d73ff9855236..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-14-12-58-21.gh-issue-101015.stWFid.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix :func:`typing.get_type_hints` on ``'*tuple[...]'`` and ``*tuple[...]``. -It must not drop the ``Unpack`` part. diff --git a/Misc/NEWS.d/next/Library/2023-01-15-09-11-30.gh-issue-94518.jvxtxm.rst b/Misc/NEWS.d/next/Library/2023-01-15-09-11-30.gh-issue-94518.jvxtxm.rst deleted file mode 100644 index 77563090464dbc..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-15-09-11-30.gh-issue-94518.jvxtxm.rst +++ /dev/null @@ -1,3 +0,0 @@ -Group-related variables of ``_posixsubprocess`` module are renamed to -stress that supplimentary group affinity is added to a fork, not -replace the inherited ones. Patch by Oleg Iarygin. diff --git a/Misc/NEWS.d/next/Library/2023-01-18-17-58-50.gh-issue-101144.FHd8Un.rst b/Misc/NEWS.d/next/Library/2023-01-18-17-58-50.gh-issue-101144.FHd8Un.rst deleted file mode 100644 index 297652259949fc..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-18-17-58-50.gh-issue-101144.FHd8Un.rst +++ /dev/null @@ -1,4 +0,0 @@ -Make :func:`zipfile.Path.open` and :func:`zipfile.Path.read_text` also accept -``encoding`` as a positional argument. This was the behavior in Python 3.9 and -earlier. 3.10 introduced a regression where supplying it as a positional -argument would lead to a :exc:`TypeError`. diff --git a/Misc/NEWS.d/next/Library/2023-01-20-10-46-59.gh-issue-101143.hJo8hu.rst b/Misc/NEWS.d/next/Library/2023-01-20-10-46-59.gh-issue-101143.hJo8hu.rst deleted file mode 100644 index d14b9e25a691fc..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-20-10-46-59.gh-issue-101143.hJo8hu.rst +++ /dev/null @@ -1,2 +0,0 @@ -Remove unused references to :class:`~asyncio.TimerHandle` in -``asyncio.base_events.BaseEventLoop._add_callback``. diff --git a/Misc/NEWS.d/next/Library/2023-01-21-16-50-22.gh-issue-100795.NPMZf7.rst b/Misc/NEWS.d/next/Library/2023-01-21-16-50-22.gh-issue-100795.NPMZf7.rst deleted file mode 100644 index 4cb56ea0f0e58d..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-21-16-50-22.gh-issue-100795.NPMZf7.rst +++ /dev/null @@ -1,3 +0,0 @@ -Avoid potential unexpected ``freeaddrinfo`` call (double free) in :mod:`socket` -when when a libc ``getaddrinfo()`` implementation leaves garbage in an output -pointer when returning an error. Original patch by Sergey G. Brester. diff --git a/Misc/NEWS.d/next/Library/2023-01-24-12-53-59.gh-issue-92123.jf6TO5.rst b/Misc/NEWS.d/next/Library/2023-01-24-12-53-59.gh-issue-92123.jf6TO5.rst deleted file mode 100644 index 4b4443a55fdb1a..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-24-12-53-59.gh-issue-92123.jf6TO5.rst +++ /dev/null @@ -1,2 +0,0 @@ -Adapt the ``_elementtree`` extension module to multi-phase init (:pep:`489`). -Patches by Erlend E. Aasland. diff --git a/Misc/NEWS.d/next/Library/2023-01-25-18-07-20.gh-issue-101326.KL4SFv.rst b/Misc/NEWS.d/next/Library/2023-01-25-18-07-20.gh-issue-101326.KL4SFv.rst deleted file mode 100644 index 54b69b9430910d..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-25-18-07-20.gh-issue-101326.KL4SFv.rst +++ /dev/null @@ -1 +0,0 @@ -Fix regression when passing ``None`` as second or third argument to ``FutureIter.throw``. diff --git a/Misc/NEWS.d/next/Library/2023-01-26-01-25-56.gh-issue-101317.vWaS1x.rst b/Misc/NEWS.d/next/Library/2023-01-26-01-25-56.gh-issue-101317.vWaS1x.rst deleted file mode 100644 index f1ce0e0a527661..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-26-01-25-56.gh-issue-101317.vWaS1x.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add *ssl_shutdown_timeout* parameter for :meth:`asyncio.StreamWriter.start_tls`. - diff --git a/Misc/NEWS.d/next/Library/2023-01-26-06-44-35.gh-issue-101323.h8Hk11.rst b/Misc/NEWS.d/next/Library/2023-01-26-06-44-35.gh-issue-101323.h8Hk11.rst deleted file mode 100644 index f8419e130dbcd7..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-26-06-44-35.gh-issue-101323.h8Hk11.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a bug where errors where not thrown by zlib._ZlibDecompressor if -encountered during decompressing. diff --git a/Misc/NEWS.d/next/Library/2023-02-04-21-01-49.gh-issue-101570.lbtUsD.rst b/Misc/NEWS.d/next/Library/2023-02-04-21-01-49.gh-issue-101570.lbtUsD.rst deleted file mode 100644 index 599edab6c07117..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-02-04-21-01-49.gh-issue-101570.lbtUsD.rst +++ /dev/null @@ -1 +0,0 @@ -Upgrade pip wheel bundled with ensurepip (pip 23.0) diff --git a/Misc/NEWS.d/next/Library/2023-02-05-14-39-49.gh-issue-101541.Mo3ppp.rst b/Misc/NEWS.d/next/Library/2023-02-05-14-39-49.gh-issue-101541.Mo3ppp.rst deleted file mode 100644 index 0f149e80dc54a2..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-02-05-14-39-49.gh-issue-101541.Mo3ppp.rst +++ /dev/null @@ -1 +0,0 @@ -[Enum] - fix psuedo-flag creation diff --git a/Misc/NEWS.d/next/Security/2022-11-08-12-06-52.gh-issue-99108.4Wrsuh.rst b/Misc/NEWS.d/next/Security/2022-11-08-12-06-52.gh-issue-99108.4Wrsuh.rst deleted file mode 100644 index 64acc09c482ecb..00000000000000 --- a/Misc/NEWS.d/next/Security/2022-11-08-12-06-52.gh-issue-99108.4Wrsuh.rst +++ /dev/null @@ -1,4 +0,0 @@ -Replace the builtin :mod:`hashlib` implementations of SHA2-224 and SHA2-256 -originally from LibTomCrypt with formally verified, side-channel resistant -code from the `HACL* `_ project. The -builtins remain a fallback only used when OpenSSL does not provide them. diff --git a/Misc/NEWS.d/next/Tests/2023-02-04-17-24-33.gh-issue-101334._yOqwg.rst b/Misc/NEWS.d/next/Tests/2023-02-04-17-24-33.gh-issue-101334._yOqwg.rst deleted file mode 100644 index 2a95fd9ae53c86..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-02-04-17-24-33.gh-issue-101334._yOqwg.rst +++ /dev/null @@ -1 +0,0 @@ -``test_tarfile`` has been updated to pass when run as a high UID. diff --git a/Misc/NEWS.d/next/Windows/2023-01-11-14-42-11.gh-issue-100247.YfEmSz.rst b/Misc/NEWS.d/next/Windows/2023-01-11-14-42-11.gh-issue-100247.YfEmSz.rst deleted file mode 100644 index 7bfcbd7ddecf5f..00000000000000 --- a/Misc/NEWS.d/next/Windows/2023-01-11-14-42-11.gh-issue-100247.YfEmSz.rst +++ /dev/null @@ -1,2 +0,0 @@ -Restores support for the :file:`py.exe` launcher finding shebang commands in -its configuration file using the full command name. diff --git a/Misc/NEWS.d/next/Windows/2023-01-11-16-28-09.gh-issue-100320.2DU2it.rst b/Misc/NEWS.d/next/Windows/2023-01-11-16-28-09.gh-issue-100320.2DU2it.rst deleted file mode 100644 index c206fc8520a5d9..00000000000000 --- a/Misc/NEWS.d/next/Windows/2023-01-11-16-28-09.gh-issue-100320.2DU2it.rst +++ /dev/null @@ -1,3 +0,0 @@ -Ensures the ``PythonPath`` registry key from an install is used when -launching from a different copy of Python that relies on an existing install -to provide a copy of its modules and standard library. diff --git a/Misc/NEWS.d/next/Windows/2023-01-17-18-17-58.gh-issue-82052.mWyysT.rst b/Misc/NEWS.d/next/Windows/2023-01-17-18-17-58.gh-issue-82052.mWyysT.rst deleted file mode 100644 index 4f7ab200b85cba..00000000000000 --- a/Misc/NEWS.d/next/Windows/2023-01-17-18-17-58.gh-issue-82052.mWyysT.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed an issue where writing more than 32K of Unicode output to the console screen in one go can result in mojibake. diff --git a/Misc/NEWS.d/next/Windows/2023-01-18-18-25-18.gh-issue-101135.HF9VlG.rst b/Misc/NEWS.d/next/Windows/2023-01-18-18-25-18.gh-issue-101135.HF9VlG.rst deleted file mode 100644 index 2e6d6371340d89..00000000000000 --- a/Misc/NEWS.d/next/Windows/2023-01-18-18-25-18.gh-issue-101135.HF9VlG.rst +++ /dev/null @@ -1,3 +0,0 @@ -Restore ability to launch older 32-bit versions from the :file:`py.exe` -launcher when both 32-bit and 64-bit installs of the same version are -available. diff --git a/Misc/NEWS.d/next/Windows/2023-01-25-00-23-31.gh-issue-99834.WN41lc.rst b/Misc/NEWS.d/next/Windows/2023-01-25-00-23-31.gh-issue-99834.WN41lc.rst deleted file mode 100644 index d3894fa4ea3012..00000000000000 --- a/Misc/NEWS.d/next/Windows/2023-01-25-00-23-31.gh-issue-99834.WN41lc.rst +++ /dev/null @@ -1 +0,0 @@ -Updates bundled copy of Tcl/Tk to 8.6.13.0 diff --git a/Misc/NEWS.d/next/Windows/2023-01-31-16-50-07.gh-issue-101467.ye9t-L.rst b/Misc/NEWS.d/next/Windows/2023-01-31-16-50-07.gh-issue-101467.ye9t-L.rst deleted file mode 100644 index 4d4da05afa2d16..00000000000000 --- a/Misc/NEWS.d/next/Windows/2023-01-31-16-50-07.gh-issue-101467.ye9t-L.rst +++ /dev/null @@ -1,3 +0,0 @@ -The ``py.exe`` launcher now correctly filters when only a single runtime is -installed. It also correctly handles prefix matches on tags so that ``-3.1`` -does not match ``3.11``, but would still match ``3.1-32``. diff --git a/Misc/NEWS.d/next/Windows/2023-02-03-17-53-06.gh-issue-101543.cORAT4.rst b/Misc/NEWS.d/next/Windows/2023-02-03-17-53-06.gh-issue-101543.cORAT4.rst deleted file mode 100644 index d4e2c6f23013b6..00000000000000 --- a/Misc/NEWS.d/next/Windows/2023-02-03-17-53-06.gh-issue-101543.cORAT4.rst +++ /dev/null @@ -1,2 +0,0 @@ -Ensure the install path in the registry is only used when the standard -library hasn't been located in any other way. diff --git a/README.rst b/README.rst index 814efef83a357a..b1756e20c141ab 100644 --- a/README.rst +++ b/README.rst @@ -1,4 +1,4 @@ -This is Python version 3.12.0 alpha 4 +This is Python version 3.12.0 alpha 5 ===================================== .. image:: https://github.com/python/cpython/workflows/Tests/badge.svg From a757d721271974c45e2feacef739af4e86ec7350 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 7 Feb 2023 15:59:26 +0200 Subject: [PATCH 029/247] Doctest: Pin sphinxext-opengraph==0.7.5 to prevent importing NumPy (#101642) --- Doc/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/requirements.txt b/Doc/requirements.txt index 134f39d6d7b3d4..71d3cd61e53877 100644 --- a/Doc/requirements.txt +++ b/Doc/requirements.txt @@ -8,7 +8,7 @@ sphinx==4.5.0 blurb sphinx-lint==0.6.7 -sphinxext-opengraph>=0.7.1 +sphinxext-opengraph==0.7.5 # The theme used by the documentation is stored separately, so we need # to install that as well. From a687ae9eb5c0aea06c52de1e426904b79f767f4e Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Tue, 7 Feb 2023 18:54:47 +0400 Subject: [PATCH 030/247] Fix nesting of 'Pending Removal in Python 3.14' (#101637) --- Doc/whatsnew/3.12.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 0c5a70b64574ef..b723b70154f08d 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -479,7 +479,7 @@ APIs: * :class:`webbrowser.MacOSX` (:gh:`86421`) Pending Removal in Python 3.14 -============================== +------------------------------ * Deprecated the following :mod:`importlib.abc` classes, scheduled for removal in Python 3.14: From d54b8d8fbd76c05e9006175ab26d737c4b055dfb Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 7 Feb 2023 08:28:28 -0800 Subject: [PATCH 031/247] gh-98831: Modernize the FOR_ITER family of instructions (#101626) Co-authored-by: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> --- Python/bytecodes.c | 83 +++++++++++++++++++++----------------- Python/generated_cases.c.h | 66 +++++++++++++++++++----------- Python/opcode_metadata.h | 30 +++++++------- 3 files changed, 104 insertions(+), 75 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 0fc0b3b8280f8b..ec0439af98e632 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2066,27 +2066,35 @@ dummy_func( PREDICT(LOAD_CONST); } - // stack effect: ( -- __0) - inst(FOR_ITER) { + // Most members of this family are "secretly" super-instructions. + // When the loop is exhausted, they jump, and the jump target is + // always END_FOR, which pops two values off the stack. + // This is optimized by skipping that instruction and combining + // its effect (popping 'iter' instead of pushing 'next'.) + + family(for_iter, INLINE_CACHE_ENTRIES_FOR_ITER) = { + FOR_ITER, + FOR_ITER_LIST, + FOR_ITER_TUPLE, + FOR_ITER_RANGE, + FOR_ITER_GEN, + }; + + inst(FOR_ITER, (unused/1, iter -- iter, next)) { #if ENABLE_SPECIALIZATION _PyForIterCache *cache = (_PyForIterCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); next_instr--; - _Py_Specialize_ForIter(TOP(), next_instr, oparg); + _Py_Specialize_ForIter(iter, next_instr, oparg); DISPATCH_SAME_OPARG(); } STAT_INC(FOR_ITER, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); #endif /* ENABLE_SPECIALIZATION */ - /* before: [iter]; after: [iter, iter()] *or* [] */ - PyObject *iter = TOP(); - PyObject *next = (*Py_TYPE(iter)->tp_iternext)(iter); - if (next != NULL) { - PUSH(next); - JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER); - } - else { + /* before: [iter]; after: [iter, iter()] *or* [] (and jump over END_FOR.) */ + next = (*Py_TYPE(iter)->tp_iternext)(iter); + if (next == NULL) { if (_PyErr_Occurred(tstate)) { if (!_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) { goto error; @@ -2098,63 +2106,66 @@ dummy_func( } /* iterator ended normally */ assert(_Py_OPCODE(next_instr[INLINE_CACHE_ENTRIES_FOR_ITER + oparg]) == END_FOR); - STACK_SHRINK(1); Py_DECREF(iter); - /* Skip END_FOR */ + STACK_SHRINK(1); + /* Jump forward oparg, then skip following END_FOR instruction */ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1); + DISPATCH(); } + // Common case: no jump, leave it to the code generator } - // stack effect: ( -- __0) - inst(FOR_ITER_LIST) { + inst(FOR_ITER_LIST, (unused/1, iter -- iter, next)) { assert(cframe.use_tracing == 0); - _PyListIterObject *it = (_PyListIterObject *)TOP(); - DEOPT_IF(Py_TYPE(it) != &PyListIter_Type, FOR_ITER); + DEOPT_IF(Py_TYPE(iter) != &PyListIter_Type, FOR_ITER); + _PyListIterObject *it = (_PyListIterObject *)iter; STAT_INC(FOR_ITER, hit); PyListObject *seq = it->it_seq; if (seq) { if (it->it_index < PyList_GET_SIZE(seq)) { - PyObject *next = PyList_GET_ITEM(seq, it->it_index++); - PUSH(Py_NewRef(next)); - JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER); + next = Py_NewRef(PyList_GET_ITEM(seq, it->it_index++)); goto end_for_iter_list; // End of this instruction } it->it_seq = NULL; Py_DECREF(seq); } + Py_DECREF(iter); STACK_SHRINK(1); - Py_DECREF(it); + /* Jump forward oparg, then skip following END_FOR instruction */ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1); + DISPATCH(); end_for_iter_list: + // Common case: no jump, leave it to the code generator } - // stack effect: ( -- __0) - inst(FOR_ITER_TUPLE) { + inst(FOR_ITER_TUPLE, (unused/1, iter -- iter, next)) { assert(cframe.use_tracing == 0); - _PyTupleIterObject *it = (_PyTupleIterObject *)TOP(); + _PyTupleIterObject *it = (_PyTupleIterObject *)iter; DEOPT_IF(Py_TYPE(it) != &PyTupleIter_Type, FOR_ITER); STAT_INC(FOR_ITER, hit); PyTupleObject *seq = it->it_seq; if (seq) { if (it->it_index < PyTuple_GET_SIZE(seq)) { - PyObject *next = PyTuple_GET_ITEM(seq, it->it_index++); - PUSH(Py_NewRef(next)); - JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER); + next = Py_NewRef(PyTuple_GET_ITEM(seq, it->it_index++)); goto end_for_iter_tuple; // End of this instruction } it->it_seq = NULL; Py_DECREF(seq); } + Py_DECREF(iter); STACK_SHRINK(1); - Py_DECREF(it); + /* Jump forward oparg, then skip following END_FOR instruction */ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1); + DISPATCH(); end_for_iter_tuple: + // Common case: no jump, leave it to the code generator } - // stack effect: ( -- __0) - inst(FOR_ITER_RANGE) { + // This is slightly different, when the loop isn't terminated we + // jump over the immediately following STORE_FAST instruction. + inst(FOR_ITER_RANGE, (unused/1, iter -- iter, unused)) { assert(cframe.use_tracing == 0); - _PyRangeIterObject *r = (_PyRangeIterObject *)TOP(); + _PyRangeIterObject *r = (_PyRangeIterObject *)iter; DEOPT_IF(Py_TYPE(r) != &PyRangeIter_Type, FOR_ITER); STAT_INC(FOR_ITER, hit); _Py_CODEUNIT next = next_instr[INLINE_CACHE_ENTRIES_FOR_ITER]; @@ -2162,6 +2173,7 @@ dummy_func( if (r->len <= 0) { STACK_SHRINK(1); Py_DECREF(r); + // Jump over END_FOR instruction. JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1); } else { @@ -2174,11 +2186,13 @@ dummy_func( // The STORE_FAST is already done. JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + 1); } + DISPATCH(); } - inst(FOR_ITER_GEN) { + // This is *not* a super-instruction, unique in the family. + inst(FOR_ITER_GEN, (unused/1, iter -- iter, unused)) { assert(cframe.use_tracing == 0); - PyGenObject *gen = (PyGenObject *)TOP(); + PyGenObject *gen = (PyGenObject *)iter; DEOPT_IF(Py_TYPE(gen) != &PyGen_Type, FOR_ITER); DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, FOR_ITER); STAT_INC(FOR_ITER, hit); @@ -3168,9 +3182,6 @@ family(call, INLINE_CACHE_ENTRIES_CALL) = { CALL_NO_KW_LIST_APPEND, CALL_NO_KW_METHOD_DESCRIPTOR_FAST, CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS, CALL_NO_KW_METHOD_DESCRIPTOR_O, CALL_NO_KW_STR_1, CALL_NO_KW_TUPLE_1, CALL_NO_KW_TYPE_1 }; -family(for_iter, INLINE_CACHE_ENTRIES_FOR_ITER) = { - FOR_ITER, FOR_ITER_LIST, - FOR_ITER_RANGE }; family(store_fast) = { STORE_FAST, STORE_FAST__LOAD_FAST, STORE_FAST__STORE_FAST }; family(unpack_sequence, INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE) = { UNPACK_SEQUENCE, UNPACK_SEQUENCE_LIST, diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index f0f314a143c2c0..4e511f43d95028 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2619,25 +2619,23 @@ TARGET(FOR_ITER) { PREDICTED(FOR_ITER); + static_assert(INLINE_CACHE_ENTRIES_FOR_ITER == 1, "incorrect cache size"); + PyObject *iter = PEEK(1); + PyObject *next; #if ENABLE_SPECIALIZATION _PyForIterCache *cache = (_PyForIterCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); next_instr--; - _Py_Specialize_ForIter(TOP(), next_instr, oparg); + _Py_Specialize_ForIter(iter, next_instr, oparg); DISPATCH_SAME_OPARG(); } STAT_INC(FOR_ITER, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); #endif /* ENABLE_SPECIALIZATION */ - /* before: [iter]; after: [iter, iter()] *or* [] */ - PyObject *iter = TOP(); - PyObject *next = (*Py_TYPE(iter)->tp_iternext)(iter); - if (next != NULL) { - PUSH(next); - JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER); - } - else { + /* before: [iter]; after: [iter, iter()] *or* [] (and jump over END_FOR.) */ + next = (*Py_TYPE(iter)->tp_iternext)(iter); + if (next == NULL) { if (_PyErr_Occurred(tstate)) { if (!_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) { goto error; @@ -2649,63 +2647,81 @@ } /* iterator ended normally */ assert(_Py_OPCODE(next_instr[INLINE_CACHE_ENTRIES_FOR_ITER + oparg]) == END_FOR); - STACK_SHRINK(1); Py_DECREF(iter); - /* Skip END_FOR */ + STACK_SHRINK(1); + /* Jump forward oparg, then skip following END_FOR instruction */ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1); + DISPATCH(); } + // Common case: no jump, leave it to the code generator + STACK_GROW(1); + POKE(1, next); + JUMPBY(1); DISPATCH(); } TARGET(FOR_ITER_LIST) { + PyObject *iter = PEEK(1); + PyObject *next; assert(cframe.use_tracing == 0); - _PyListIterObject *it = (_PyListIterObject *)TOP(); - DEOPT_IF(Py_TYPE(it) != &PyListIter_Type, FOR_ITER); + DEOPT_IF(Py_TYPE(iter) != &PyListIter_Type, FOR_ITER); + _PyListIterObject *it = (_PyListIterObject *)iter; STAT_INC(FOR_ITER, hit); PyListObject *seq = it->it_seq; if (seq) { if (it->it_index < PyList_GET_SIZE(seq)) { - PyObject *next = PyList_GET_ITEM(seq, it->it_index++); - PUSH(Py_NewRef(next)); - JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER); + next = Py_NewRef(PyList_GET_ITEM(seq, it->it_index++)); goto end_for_iter_list; // End of this instruction } it->it_seq = NULL; Py_DECREF(seq); } + Py_DECREF(iter); STACK_SHRINK(1); - Py_DECREF(it); + /* Jump forward oparg, then skip following END_FOR instruction */ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1); + DISPATCH(); end_for_iter_list: + // Common case: no jump, leave it to the code generator + STACK_GROW(1); + POKE(1, next); + JUMPBY(1); DISPATCH(); } TARGET(FOR_ITER_TUPLE) { + PyObject *iter = PEEK(1); + PyObject *next; assert(cframe.use_tracing == 0); - _PyTupleIterObject *it = (_PyTupleIterObject *)TOP(); + _PyTupleIterObject *it = (_PyTupleIterObject *)iter; DEOPT_IF(Py_TYPE(it) != &PyTupleIter_Type, FOR_ITER); STAT_INC(FOR_ITER, hit); PyTupleObject *seq = it->it_seq; if (seq) { if (it->it_index < PyTuple_GET_SIZE(seq)) { - PyObject *next = PyTuple_GET_ITEM(seq, it->it_index++); - PUSH(Py_NewRef(next)); - JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER); + next = Py_NewRef(PyTuple_GET_ITEM(seq, it->it_index++)); goto end_for_iter_tuple; // End of this instruction } it->it_seq = NULL; Py_DECREF(seq); } + Py_DECREF(iter); STACK_SHRINK(1); - Py_DECREF(it); + /* Jump forward oparg, then skip following END_FOR instruction */ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1); + DISPATCH(); end_for_iter_tuple: + // Common case: no jump, leave it to the code generator + STACK_GROW(1); + POKE(1, next); + JUMPBY(1); DISPATCH(); } TARGET(FOR_ITER_RANGE) { + PyObject *iter = PEEK(1); assert(cframe.use_tracing == 0); - _PyRangeIterObject *r = (_PyRangeIterObject *)TOP(); + _PyRangeIterObject *r = (_PyRangeIterObject *)iter; DEOPT_IF(Py_TYPE(r) != &PyRangeIter_Type, FOR_ITER); STAT_INC(FOR_ITER, hit); _Py_CODEUNIT next = next_instr[INLINE_CACHE_ENTRIES_FOR_ITER]; @@ -2713,6 +2729,7 @@ if (r->len <= 0) { STACK_SHRINK(1); Py_DECREF(r); + // Jump over END_FOR instruction. JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1); } else { @@ -2729,8 +2746,9 @@ } TARGET(FOR_ITER_GEN) { + PyObject *iter = PEEK(1); assert(cframe.use_tracing == 0); - PyGenObject *gen = (PyGenObject *)TOP(); + PyGenObject *gen = (PyGenObject *)iter; DEOPT_IF(Py_TYPE(gen) != &PyGen_Type, FOR_ITER); DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, FOR_ITER); STAT_INC(FOR_ITER, hit); diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index 948d17519e2709..ed26ff00c7b182 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -261,15 +261,15 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { case GET_YIELD_FROM_ITER: return 1; case FOR_ITER: - return -1; + return 1; case FOR_ITER_LIST: - return -1; + return 1; case FOR_ITER_TUPLE: - return -1; + return 1; case FOR_ITER_RANGE: - return -1; + return 1; case FOR_ITER_GEN: - return -1; + return 1; case BEFORE_ASYNC_WITH: return 1; case BEFORE_WITH: @@ -607,15 +607,15 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { case GET_YIELD_FROM_ITER: return 1; case FOR_ITER: - return -1; + return 2; case FOR_ITER_LIST: - return -1; + return 2; case FOR_ITER_TUPLE: - return -1; + return 2; case FOR_ITER_RANGE: - return -1; + return 2; case FOR_ITER_GEN: - return -1; + return 2; case BEFORE_ASYNC_WITH: return 2; case BEFORE_WITH: @@ -829,11 +829,11 @@ struct opcode_metadata { [MATCH_KEYS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [GET_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [GET_YIELD_FROM_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [FOR_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [FOR_ITER_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [FOR_ITER_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [FOR_ITER_RANGE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [FOR_ITER_GEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [FOR_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, + [FOR_ITER_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, + [FOR_ITER_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, + [FOR_ITER_RANGE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, + [FOR_ITER_GEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, [BEFORE_ASYNC_WITH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [BEFORE_WITH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [WITH_EXCEPT_START] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, From 6fd5eb640af19b535f4f2ba27b1b61b8d17f02e9 Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Tue, 7 Feb 2023 11:22:58 -0600 Subject: [PATCH 032/247] Make use of TESTFN_ASCII in test_fileio (GH-101645) testBytesOpen requires an ASCII filename, but TESTFN usually isn't ASCII. Automerge-Triggered-By: GH:zware --- Lib/test/test_fileio.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_fileio.py b/Lib/test/test_fileio.py index 2263604ed1f97d..ebfcffd1829174 100644 --- a/Lib/test/test_fileio.py +++ b/Lib/test/test_fileio.py @@ -12,7 +12,9 @@ from test.support import ( cpython_only, swap_attr, gc_collect, is_emscripten, is_wasi ) -from test.support.os_helper import (TESTFN, TESTFN_UNICODE, make_bad_fd) +from test.support.os_helper import ( + TESTFN, TESTFN_ASCII, TESTFN_UNICODE, make_bad_fd, + ) from test.support.warnings_helper import check_warnings from collections import UserList @@ -431,18 +433,15 @@ def testUnicodeOpen(self): def testBytesOpen(self): # Opening a bytes filename - try: - fn = TESTFN.encode("ascii") - except UnicodeEncodeError: - self.skipTest('could not encode %r to ascii' % TESTFN) + fn = TESTFN_ASCII.encode("ascii") f = self.FileIO(fn, "w") try: f.write(b"abc") f.close() - with open(TESTFN, "rb") as f: + with open(TESTFN_ASCII, "rb") as f: self.assertEqual(f.read(), b"abc") finally: - os.unlink(TESTFN) + os.unlink(TESTFN_ASCII) @unittest.skipIf(sys.getfilesystemencoding() != 'utf-8', "test only works for utf-8 filesystems") From f87f6e23964d7a4c38b655089cda65538a24ec36 Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Tue, 7 Feb 2023 22:04:31 +0400 Subject: [PATCH 033/247] gh-97725: Fix documentation for the default file of `asyncio.Task.print_stack` (#101652) --- Doc/library/asyncio-task.rst | 2 +- .../Documentation/2023-02-07-21-43-24.gh-issue-97725.cuY7Cd.rst | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Documentation/2023-02-07-21-43-24.gh-issue-97725.cuY7Cd.rst diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst index 39112580285cc0..9b984243282268 100644 --- a/Doc/library/asyncio-task.rst +++ b/Doc/library/asyncio-task.rst @@ -1097,7 +1097,7 @@ Task Object The *limit* argument is passed to :meth:`get_stack` directly. The *file* argument is an I/O stream to which the output - is written; by default output is written to :data:`sys.stderr`. + is written; by default output is written to :data:`sys.stdout`. .. method:: get_coro() diff --git a/Misc/NEWS.d/next/Documentation/2023-02-07-21-43-24.gh-issue-97725.cuY7Cd.rst b/Misc/NEWS.d/next/Documentation/2023-02-07-21-43-24.gh-issue-97725.cuY7Cd.rst new file mode 100644 index 00000000000000..fd9ea049c23968 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2023-02-07-21-43-24.gh-issue-97725.cuY7Cd.rst @@ -0,0 +1,2 @@ +Fix :meth:`asyncio.Task.print_stack` description for ``file=None``. +Patch by Oleg Iarygin. From dec1ab03879e959f7efb910a723caf4a9ce453cf Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Tue, 7 Feb 2023 20:37:43 +0000 Subject: [PATCH 034/247] gh-98831: rewrite UNPACK_EX, UNPACK_SEQUENCE, UNPACK_SEQUENCE_TWO_TUPLE in the instruction definition DSL (#101641) --- Python/bytecodes.c | 37 ++++++++++------------------------ Python/generated_cases.c.h | 41 +++++++++++++++++++------------------- Python/opcode_metadata.h | 16 +++++++-------- 3 files changed, 40 insertions(+), 54 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index ec0439af98e632..b43625fd283cc3 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -859,13 +859,11 @@ dummy_func( } } - // stack effect: (__0 -- __array[oparg]) - inst(UNPACK_SEQUENCE) { + inst(UNPACK_SEQUENCE, (unused/1, seq -- unused[oparg])) { #if ENABLE_SPECIALIZATION _PyUnpackSequenceCache *cache = (_PyUnpackSequenceCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); - PyObject *seq = TOP(); next_instr--; _Py_Specialize_UnpackSequence(seq, next_instr, oparg); DISPATCH_SAME_OPARG(); @@ -873,27 +871,19 @@ dummy_func( STAT_INC(UNPACK_SEQUENCE, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); #endif /* ENABLE_SPECIALIZATION */ - PyObject *seq = POP(); - PyObject **top = stack_pointer + oparg; - if (!unpack_iterable(tstate, seq, oparg, -1, top)) { - Py_DECREF(seq); - goto error; - } - STACK_GROW(oparg); + PyObject **top = stack_pointer + oparg - 1; + int res = unpack_iterable(tstate, seq, oparg, -1, top); Py_DECREF(seq); - JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE); + ERROR_IF(res == 0, error); } - // stack effect: (__0 -- __array[oparg]) - inst(UNPACK_SEQUENCE_TWO_TUPLE) { - PyObject *seq = TOP(); + inst(UNPACK_SEQUENCE_TWO_TUPLE, (unused/1, seq -- v1, v0)) { DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE); DEOPT_IF(PyTuple_GET_SIZE(seq) != 2, UNPACK_SEQUENCE); STAT_INC(UNPACK_SEQUENCE, hit); - SET_TOP(Py_NewRef(PyTuple_GET_ITEM(seq, 1))); - PUSH(Py_NewRef(PyTuple_GET_ITEM(seq, 0))); + v1 = Py_NewRef(PyTuple_GET_ITEM(seq, 1)); + v0 = Py_NewRef(PyTuple_GET_ITEM(seq, 0)); Py_DECREF(seq); - JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE); } // stack effect: (__0 -- __array[oparg]) @@ -926,17 +916,12 @@ dummy_func( JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE); } - // error: UNPACK_EX has irregular stack effect - inst(UNPACK_EX) { + inst(UNPACK_EX, (seq -- unused[oparg & 0xFF], unused, unused[oparg >> 8])) { int totalargs = 1 + (oparg & 0xFF) + (oparg >> 8); - PyObject *seq = POP(); - PyObject **top = stack_pointer + totalargs; - if (!unpack_iterable(tstate, seq, oparg & 0xFF, oparg >> 8, top)) { - Py_DECREF(seq); - goto error; - } - STACK_GROW(totalargs); + PyObject **top = stack_pointer + totalargs - 1; + int res = unpack_iterable(tstate, seq, oparg & 0xFF, oparg >> 8, top); Py_DECREF(seq); + ERROR_IF(res == 0, error); } family(store_attr, INLINE_CACHE_ENTRIES_STORE_ATTR) = { diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 4e511f43d95028..ab19f431710644 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -1108,11 +1108,11 @@ TARGET(UNPACK_SEQUENCE) { PREDICTED(UNPACK_SEQUENCE); + PyObject *seq = PEEK(1); #if ENABLE_SPECIALIZATION _PyUnpackSequenceCache *cache = (_PyUnpackSequenceCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); - PyObject *seq = TOP(); next_instr--; _Py_Specialize_UnpackSequence(seq, next_instr, oparg); DISPATCH_SAME_OPARG(); @@ -1120,27 +1120,30 @@ STAT_INC(UNPACK_SEQUENCE, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); #endif /* ENABLE_SPECIALIZATION */ - PyObject *seq = POP(); - PyObject **top = stack_pointer + oparg; - if (!unpack_iterable(tstate, seq, oparg, -1, top)) { - Py_DECREF(seq); - goto error; - } - STACK_GROW(oparg); + PyObject **top = stack_pointer + oparg - 1; + int res = unpack_iterable(tstate, seq, oparg, -1, top); Py_DECREF(seq); - JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE); + if (res == 0) goto pop_1_error; + STACK_SHRINK(1); + STACK_GROW(oparg); + JUMPBY(1); DISPATCH(); } TARGET(UNPACK_SEQUENCE_TWO_TUPLE) { - PyObject *seq = TOP(); + PyObject *seq = PEEK(1); + PyObject *v1; + PyObject *v0; DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE); DEOPT_IF(PyTuple_GET_SIZE(seq) != 2, UNPACK_SEQUENCE); STAT_INC(UNPACK_SEQUENCE, hit); - SET_TOP(Py_NewRef(PyTuple_GET_ITEM(seq, 1))); - PUSH(Py_NewRef(PyTuple_GET_ITEM(seq, 0))); + v1 = Py_NewRef(PyTuple_GET_ITEM(seq, 1)); + v0 = Py_NewRef(PyTuple_GET_ITEM(seq, 0)); Py_DECREF(seq); - JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE); + STACK_GROW(1); + POKE(1, v0); + POKE(2, v1); + JUMPBY(1); DISPATCH(); } @@ -1175,15 +1178,13 @@ } TARGET(UNPACK_EX) { + PyObject *seq = PEEK(1); int totalargs = 1 + (oparg & 0xFF) + (oparg >> 8); - PyObject *seq = POP(); - PyObject **top = stack_pointer + totalargs; - if (!unpack_iterable(tstate, seq, oparg & 0xFF, oparg >> 8, top)) { - Py_DECREF(seq); - goto error; - } - STACK_GROW(totalargs); + PyObject **top = stack_pointer + totalargs - 1; + int res = unpack_iterable(tstate, seq, oparg & 0xFF, oparg >> 8, top); Py_DECREF(seq); + if (res == 0) goto pop_1_error; + STACK_GROW((oparg & 0xFF) + (oparg >> 8)); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index ed26ff00c7b182..d2585351f69fd2 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -121,15 +121,15 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { case DELETE_NAME: return 0; case UNPACK_SEQUENCE: - return -1; + return 1; case UNPACK_SEQUENCE_TWO_TUPLE: - return -1; + return 1; case UNPACK_SEQUENCE_TUPLE: return -1; case UNPACK_SEQUENCE_LIST: return -1; case UNPACK_EX: - return -1; + return 1; case STORE_ATTR: return 2; case DELETE_ATTR: @@ -467,15 +467,15 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { case DELETE_NAME: return 0; case UNPACK_SEQUENCE: - return -1; + return oparg; case UNPACK_SEQUENCE_TWO_TUPLE: - return -1; + return 2; case UNPACK_SEQUENCE_TUPLE: return -1; case UNPACK_SEQUENCE_LIST: return -1; case UNPACK_EX: - return -1; + return (oparg & 0xFF) + (oparg >> 8) + 1; case STORE_ATTR: return 0; case DELETE_ATTR: @@ -759,8 +759,8 @@ struct opcode_metadata { [LOAD_BUILD_CLASS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [STORE_NAME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [DELETE_NAME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [UNPACK_SEQUENCE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [UNPACK_SEQUENCE_TWO_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [UNPACK_SEQUENCE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, + [UNPACK_SEQUENCE_TWO_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, [UNPACK_SEQUENCE_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [UNPACK_SEQUENCE_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [UNPACK_EX] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, From acc2f3b19d28d4bf3f8fb32357f581cba5ba24c7 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Wed, 8 Feb 2023 00:43:33 +0300 Subject: [PATCH 035/247] gh-101656: Fix "conversion from Py_ssize_t to int" warning in `_testcapimodule` (#101657) --- Modules/_testcapimodule.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 5a6097ef0ac5a6..8b2ce1a2cfd4bd 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3185,11 +3185,11 @@ eval_eval_code_ex(PyObject *mod, PyObject *pos_args) globals, locals, c_args, - c_args_len, + (int)c_args_len, c_kwargs, - c_kwargs_len, + (int)c_kwargs_len, c_defaults, - c_defaults_len, + (int)c_defaults_len, kw_defaults, closure ); From 46f461be56ab90891d2d43240d80a0e19d100ba9 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Tue, 7 Feb 2023 23:13:10 +0100 Subject: [PATCH 036/247] Post 3.12.0a5 --- Include/patchlevel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/patchlevel.h b/Include/patchlevel.h index df6098be8bbcc9..7957220ed7cf9f 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -23,7 +23,7 @@ #define PY_RELEASE_SERIAL 5 /* Version as a string */ -#define PY_VERSION "3.12.0a5" +#define PY_VERSION "3.12.0a5+" /*--end constants--*/ /* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2. From 753fc8a5d64369cd228c3e43fef1811ac3cfde83 Mon Sep 17 00:00:00 2001 From: penguin_wwy <940375606@qq.com> Date: Wed, 8 Feb 2023 06:32:21 +0800 Subject: [PATCH 037/247] gh-101632: Add the new RETURN_CONST opcode (#101633) --- Doc/library/dis.rst | 7 + Include/internal/pycore_opcode.h | 10 +- Include/opcode.h | 16 +- Lib/dis.py | 3 +- Lib/importlib/_bootstrap_external.py | 3 +- Lib/opcode.py | 2 + Lib/test/test_ast.py | 2 +- Lib/test/test_code.py | 1 + Lib/test/test_compile.py | 18 +- Lib/test/test_dis.py | 155 ++++++++---------- Lib/test/test_peepholer.py | 6 +- ...-02-07-14-56-43.gh-issue-101632.Fd1yxk.rst | 1 + Objects/frameobject.c | 2 + Programs/test_frozenmain.h | 50 +++--- Python/bytecodes.c | 17 ++ Python/compile.c | 17 +- Python/generated_cases.c.h | 17 ++ Python/opcode_metadata.h | 5 + Python/opcode_targets.h | 8 +- 19 files changed, 186 insertions(+), 154 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-07-14-56-43.gh-issue-101632.Fd1yxk.rst diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index 1fe2d5d6227d61..b1e61d7e77b2f5 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -694,6 +694,13 @@ iterations of the loop. Returns with ``STACK[-1]`` to the caller of the function. +.. opcode:: RETURN_CONST (consti) + + Returns with ``co_consts[consti]`` to the caller of the function. + + .. versionadded:: 3.12 + + .. opcode:: YIELD_VALUE Yields ``STACK.pop()`` from a :term:`generator`. diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h index 05c0485b0641d8..47c84721335196 100644 --- a/Include/internal/pycore_opcode.h +++ b/Include/internal/pycore_opcode.h @@ -192,6 +192,7 @@ const uint8_t _PyOpcode_Deopt[256] = { [RAISE_VARARGS] = RAISE_VARARGS, [RERAISE] = RERAISE, [RESUME] = RESUME, + [RETURN_CONST] = RETURN_CONST, [RETURN_GENERATOR] = RETURN_GENERATOR, [RETURN_VALUE] = RETURN_VALUE, [SEND] = SEND, @@ -349,7 +350,7 @@ static const char *const _PyOpcode_OpName[263] = { [CONTAINS_OP] = "CONTAINS_OP", [RERAISE] = "RERAISE", [COPY] = "COPY", - [STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST", + [RETURN_CONST] = "RETURN_CONST", [BINARY_OP] = "BINARY_OP", [SEND] = "SEND", [LOAD_FAST] = "LOAD_FAST", @@ -371,7 +372,7 @@ static const char *const _PyOpcode_OpName[263] = { [JUMP_BACKWARD] = "JUMP_BACKWARD", [COMPARE_AND_BRANCH] = "COMPARE_AND_BRANCH", [CALL_FUNCTION_EX] = "CALL_FUNCTION_EX", - [STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST", + [STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST", [EXTENDED_ARG] = "EXTENDED_ARG", [LIST_APPEND] = "LIST_APPEND", [SET_ADD] = "SET_ADD", @@ -381,15 +382,15 @@ static const char *const _PyOpcode_OpName[263] = { [YIELD_VALUE] = "YIELD_VALUE", [RESUME] = "RESUME", [MATCH_CLASS] = "MATCH_CLASS", + [STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST", [STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT", - [STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT", [FORMAT_VALUE] = "FORMAT_VALUE", [BUILD_CONST_KEY_MAP] = "BUILD_CONST_KEY_MAP", [BUILD_STRING] = "BUILD_STRING", + [STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT", [UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST", [UNPACK_SEQUENCE_TUPLE] = "UNPACK_SEQUENCE_TUPLE", [UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE", - [161] = "<161>", [LIST_EXTEND] = "LIST_EXTEND", [SET_UPDATE] = "SET_UPDATE", [DICT_MERGE] = "DICT_MERGE", @@ -495,7 +496,6 @@ static const char *const _PyOpcode_OpName[263] = { #endif #define EXTRA_CASES \ - case 161: \ case 166: \ case 167: \ case 168: \ diff --git a/Include/opcode.h b/Include/opcode.h index 827f9931beb3e6..77ad7c22440d72 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -76,6 +76,7 @@ extern "C" { #define CONTAINS_OP 118 #define RERAISE 119 #define COPY 120 +#define RETURN_CONST 121 #define BINARY_OP 122 #define SEND 123 #define LOAD_FAST 124 @@ -179,13 +180,13 @@ extern "C" { #define STORE_ATTR_INSTANCE_VALUE 86 #define STORE_ATTR_SLOT 87 #define STORE_ATTR_WITH_HINT 113 -#define STORE_FAST__LOAD_FAST 121 -#define STORE_FAST__STORE_FAST 143 -#define STORE_SUBSCR_DICT 153 -#define STORE_SUBSCR_LIST_INT 154 -#define UNPACK_SEQUENCE_LIST 158 -#define UNPACK_SEQUENCE_TUPLE 159 -#define UNPACK_SEQUENCE_TWO_TUPLE 160 +#define STORE_FAST__LOAD_FAST 143 +#define STORE_FAST__STORE_FAST 153 +#define STORE_SUBSCR_DICT 154 +#define STORE_SUBSCR_LIST_INT 158 +#define UNPACK_SEQUENCE_LIST 159 +#define UNPACK_SEQUENCE_TUPLE 160 +#define UNPACK_SEQUENCE_TWO_TUPLE 161 #define DO_TRACING 255 #define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\ @@ -196,6 +197,7 @@ extern "C" { #define HAS_CONST(op) (false\ || ((op) == LOAD_CONST) \ + || ((op) == RETURN_CONST) \ || ((op) == KW_NAMES) \ ) diff --git a/Lib/dis.py b/Lib/dis.py index 72ab9536a2bf6a..a6921008d9d0e5 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -34,6 +34,7 @@ MAKE_FUNCTION_FLAGS = ('defaults', 'kwdefaults', 'annotations', 'closure') LOAD_CONST = opmap['LOAD_CONST'] +RETURN_CONST = opmap['RETURN_CONST'] LOAD_GLOBAL = opmap['LOAD_GLOBAL'] BINARY_OP = opmap['BINARY_OP'] JUMP_BACKWARD = opmap['JUMP_BACKWARD'] @@ -363,7 +364,7 @@ def _get_const_value(op, arg, co_consts): assert op in hasconst argval = UNKNOWN - if op == LOAD_CONST: + if op == LOAD_CONST or op == RETURN_CONST: if co_consts is not None: argval = co_consts[arg] return argval diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index e760fbb15759d4..933c8c7d7e0590 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -431,6 +431,7 @@ def _write_atomic(path, data, mode=0o666): # Python 3.12a5 3515 (Embed jump mask in COMPARE_OP oparg) # Python 3.12a5 3516 (Add COMPARE_AND_BRANCH instruction) # Python 3.12a5 3517 (Change YIELD_VALUE oparg to exception block depth) +# Python 3.12a5 3518 (Add RETURN_CONST instruction) # Python 3.13 will start with 3550 @@ -443,7 +444,7 @@ def _write_atomic(path, data, mode=0o666): # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # in PC/launcher.c must also be updated. -MAGIC_NUMBER = (3517).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3518).to_bytes(2, 'little') + b'\r\n' _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c diff --git a/Lib/opcode.py b/Lib/opcode.py index c317e23beae62b..5f163d2ccb80df 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -164,6 +164,8 @@ def pseudo_op(name, op, real_ops): def_op('CONTAINS_OP', 118) def_op('RERAISE', 119) def_op('COPY', 120) +def_op('RETURN_CONST', 121) +hasconst.append(121) def_op('BINARY_OP', 122) jrel_op('SEND', 123) # Number of bytes to skip def_op('LOAD_FAST', 124) # Local variable number, no null check diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index 98d9b603bbc1cb..7c9a57c685df75 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -1900,7 +1900,7 @@ def get_load_const(self, tree): co = compile(tree, '', 'exec') consts = [] for instr in dis.get_instructions(co): - if instr.opname == 'LOAD_CONST': + if instr.opname == 'LOAD_CONST' or instr.opname == 'RETURN_CONST': consts.append(instr.argval) return consts diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py index 67ed1694205cd6..9c2ac83e1b69e3 100644 --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -723,6 +723,7 @@ def f(): pass PY_CODE_LOCATION_INFO_NO_COLUMNS = 13 f.__code__ = f.__code__.replace( + co_stacksize=1, co_firstlineno=42, co_code=bytes( [ diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 05a5ed1fa9a637..90b067bcf30912 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -741,7 +741,7 @@ def unused_code_at_end(): # RETURN_VALUE opcode. This does not always crash an interpreter. # When you build with the clang memory sanitizer it reliably aborts. self.assertEqual( - 'RETURN_VALUE', + 'RETURN_CONST', list(dis.get_instructions(unused_code_at_end))[-1].opname) def test_dont_merge_constants(self): @@ -822,10 +822,9 @@ def unused_block_while_else(): for func in funcs: opcodes = list(dis.get_instructions(func)) - self.assertLessEqual(len(opcodes), 4) - self.assertEqual('LOAD_CONST', opcodes[-2].opname) - self.assertEqual(None, opcodes[-2].argval) - self.assertEqual('RETURN_VALUE', opcodes[-1].opname) + self.assertLessEqual(len(opcodes), 3) + self.assertEqual('RETURN_CONST', opcodes[-1].opname) + self.assertEqual(None, opcodes[-1].argval) def test_false_while_loop(self): def break_in_while(): @@ -841,10 +840,9 @@ def continue_in_while(): # Check that we did not raise but we also don't generate bytecode for func in funcs: opcodes = list(dis.get_instructions(func)) - self.assertEqual(3, len(opcodes)) - self.assertEqual('LOAD_CONST', opcodes[1].opname) + self.assertEqual(2, len(opcodes)) + self.assertEqual('RETURN_CONST', opcodes[1].opname) self.assertEqual(None, opcodes[1].argval) - self.assertEqual('RETURN_VALUE', opcodes[2].opname) def test_consts_in_conditionals(self): def and_true(x): @@ -1311,7 +1309,7 @@ def test_multiline_generator_expression(self): line=1, end_line=2, column=1, end_column=8, occurrence=1) self.assertOpcodeSourcePositionIs(compiled_code, 'JUMP_BACKWARD', line=1, end_line=2, column=1, end_column=8, occurrence=1) - self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_VALUE', + self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_CONST', line=1, end_line=6, column=0, end_column=32, occurrence=1) def test_multiline_async_generator_expression(self): @@ -1328,7 +1326,7 @@ def test_multiline_async_generator_expression(self): self.assertIsInstance(compiled_code, types.CodeType) self.assertOpcodeSourcePositionIs(compiled_code, 'YIELD_VALUE', line=1, end_line=2, column=1, end_column=8, occurrence=2) - self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_VALUE', + self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_CONST', line=1, end_line=6, column=0, end_column=32, occurrence=1) def test_multiline_list_comprehension(self): diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index bdf48c15309296..1050b15e16eaaa 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -49,8 +49,7 @@ def cm(cls, x): COMPARE_OP 32 (==) LOAD_FAST 0 (self) STORE_ATTR 0 (x) - LOAD_CONST 0 (None) - RETURN_VALUE + RETURN_CONST 0 (None) """ % (_C.__init__.__code__.co_firstlineno, _C.__init__.__code__.co_firstlineno + 1,) dis_c_instance_method_bytes = """\ @@ -60,8 +59,7 @@ def cm(cls, x): COMPARE_OP 32 (==) LOAD_FAST 0 STORE_ATTR 0 - LOAD_CONST 0 - RETURN_VALUE + RETURN_CONST 0 """ dis_c_class_method = """\ @@ -72,8 +70,7 @@ def cm(cls, x): COMPARE_OP 32 (==) LOAD_FAST 0 (cls) STORE_ATTR 0 (x) - LOAD_CONST 0 (None) - RETURN_VALUE + RETURN_CONST 0 (None) """ % (_C.cm.__code__.co_firstlineno, _C.cm.__code__.co_firstlineno + 2,) dis_c_static_method = """\ @@ -83,8 +80,7 @@ def cm(cls, x): LOAD_CONST 1 (1) COMPARE_OP 32 (==) STORE_FAST 0 (x) - LOAD_CONST 0 (None) - RETURN_VALUE + RETURN_CONST 0 (None) """ % (_C.sm.__code__.co_firstlineno, _C.sm.__code__.co_firstlineno + 2,) # Class disassembling info has an extra newline at end. @@ -111,8 +107,7 @@ def _f(a): CALL 1 POP_TOP -%3d LOAD_CONST 1 (1) - RETURN_VALUE +%3d RETURN_CONST 1 (1) """ % (_f.__code__.co_firstlineno, _f.__code__.co_firstlineno + 1, _f.__code__.co_firstlineno + 2) @@ -124,8 +119,7 @@ def _f(a): LOAD_FAST 0 CALL 1 POP_TOP - LOAD_CONST 1 - RETURN_VALUE + RETURN_CONST 1 """ @@ -150,8 +144,7 @@ def bug708901(): %3d JUMP_BACKWARD 4 (to 30) %3d >> END_FOR - LOAD_CONST 0 (None) - RETURN_VALUE + RETURN_CONST 0 (None) """ % (bug708901.__code__.co_firstlineno, bug708901.__code__.co_firstlineno + 1, bug708901.__code__.co_firstlineno + 2, @@ -198,8 +191,7 @@ def bug42562(): dis_bug42562 = """\ RESUME 0 - LOAD_CONST 0 (None) - RETURN_VALUE + RETURN_CONST 0 (None) """ # Extended arg followed by NOP @@ -240,8 +232,7 @@ def bug42562(): %3d LOAD_GLOBAL 0 (spam) POP_TOP - LOAD_CONST 0 (None) - RETURN_VALUE + RETURN_CONST 0 (None) """ _BIG_LINENO_FORMAT2 = """\ @@ -249,20 +240,17 @@ def bug42562(): %4d LOAD_GLOBAL 0 (spam) POP_TOP - LOAD_CONST 0 (None) - RETURN_VALUE + RETURN_CONST 0 (None) """ dis_module_expected_results = """\ Disassembly of f: 4 RESUME 0 - LOAD_CONST 0 (None) - RETURN_VALUE + RETURN_CONST 0 (None) Disassembly of g: 5 RESUME 0 - LOAD_CONST 0 (None) - RETURN_VALUE + RETURN_CONST 0 (None) """ @@ -286,8 +274,7 @@ def bug42562(): LOAD_CONST 0 (1) BINARY_OP 0 (+) STORE_NAME 0 (x) - LOAD_CONST 1 (None) - RETURN_VALUE + RETURN_CONST 1 (None) """ annot_stmt_str = """\ @@ -326,8 +313,7 @@ def bug42562(): STORE_SUBSCR LOAD_NAME 1 (int) POP_TOP - LOAD_CONST 4 (None) - RETURN_VALUE + RETURN_CONST 4 (None) """ compound_stmt_str = """\ @@ -447,12 +433,11 @@ def _with(c): %3d LOAD_CONST 2 (2) STORE_FAST 2 (y) - LOAD_CONST 0 (None) - RETURN_VALUE + RETURN_CONST 0 (None) %3d >> PUSH_EXC_INFO WITH_EXCEPT_START - POP_JUMP_IF_TRUE 1 (to 46) + POP_JUMP_IF_TRUE 1 (to 44) RERAISE 2 >> POP_TOP POP_EXCEPT @@ -461,8 +446,7 @@ def _with(c): %3d LOAD_CONST 2 (2) STORE_FAST 2 (y) - LOAD_CONST 0 (None) - RETURN_VALUE + RETURN_CONST 0 (None) >> COPY 3 POP_EXCEPT RERAISE 1 @@ -514,23 +498,22 @@ async def _asyncwith(c): %3d LOAD_CONST 2 (2) STORE_FAST 2 (y) - LOAD_CONST 0 (None) - RETURN_VALUE + RETURN_CONST 0 (None) %3d >> CLEANUP_THROW - JUMP_BACKWARD 24 (to 22) + JUMP_BACKWARD 23 (to 22) >> CLEANUP_THROW - JUMP_BACKWARD 9 (to 56) + JUMP_BACKWARD 8 (to 56) >> PUSH_EXC_INFO WITH_EXCEPT_START GET_AWAITABLE 2 LOAD_CONST 0 (None) - >> SEND 4 (to 92) + >> SEND 4 (to 90) YIELD_VALUE 3 RESUME 3 - JUMP_BACKWARD_NO_INTERRUPT 4 (to 82) + JUMP_BACKWARD_NO_INTERRUPT 4 (to 80) >> CLEANUP_THROW - >> POP_JUMP_IF_TRUE 1 (to 96) + >> POP_JUMP_IF_TRUE 1 (to 94) RERAISE 2 >> POP_TOP POP_EXCEPT @@ -539,8 +522,7 @@ async def _asyncwith(c): %3d LOAD_CONST 2 (2) STORE_FAST 2 (y) - LOAD_CONST 0 (None) - RETURN_VALUE + RETURN_CONST 0 (None) >> COPY 3 POP_EXCEPT RERAISE 1 @@ -610,8 +592,7 @@ def _tryfinallyconst(b): LOAD_FAST 0 (b) CALL 0 POP_TOP - LOAD_CONST 1 (1) - RETURN_VALUE + RETURN_CONST 1 (1) PUSH_EXC_INFO PUSH_NULL LOAD_FAST 0 (b) @@ -754,8 +735,7 @@ def loop_test(): JUMP_BACKWARD 17 (to 16) %3d >> END_FOR - LOAD_CONST 0 (None) - RETURN_VALUE + RETURN_CONST 0 (None) """ % (loop_test.__code__.co_firstlineno, loop_test.__code__.co_firstlineno + 1, loop_test.__code__.co_firstlineno + 2, @@ -772,8 +752,7 @@ def extended_arg_quick(): 6 UNPACK_EX 256 8 STORE_FAST 0 (_) 10 STORE_FAST 0 (_) - 12 LOAD_CONST 0 (None) - 14 RETURN_VALUE + 12 RETURN_CONST 0 (None) """% (extended_arg_quick.__code__.co_firstlineno, extended_arg_quick.__code__.co_firstlineno + 1,) @@ -1549,8 +1528,7 @@ def _prepare_test_cases(): Instruction(opname='LOAD_FAST', opcode=124, arg=1, argval='f', argrepr='f', offset=26, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='CALL', opcode=171, arg=6, argval=6, argrepr='', offset=28, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=38, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=40, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=42, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='RETURN_CONST', opcode=121, arg=0, argval=None, argrepr='None', offset=40, starts_line=None, is_jump_target=False, positions=None) ] expected_opinfo_jumpy = [ @@ -1630,52 +1608,50 @@ def _prepare_test_cases(): Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=286, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=288, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=298, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=300, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=302, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='PUSH_EXC_INFO', opcode=35, arg=None, argval=None, argrepr='', offset=304, starts_line=25, is_jump_target=False, positions=None), - Instruction(opname='WITH_EXCEPT_START', opcode=49, arg=None, argval=None, argrepr='', offset=306, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_TRUE', opcode=115, arg=1, argval=312, argrepr='to 312', offset=308, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=119, arg=2, argval=2, argrepr='', offset=310, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=312, starts_line=None, is_jump_target=True, positions=None), - Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=314, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='RETURN_CONST', opcode=121, arg=0, argval=None, argrepr='None', offset=300, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='PUSH_EXC_INFO', opcode=35, arg=None, argval=None, argrepr='', offset=302, starts_line=25, is_jump_target=False, positions=None), + Instruction(opname='WITH_EXCEPT_START', opcode=49, arg=None, argval=None, argrepr='', offset=304, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_JUMP_IF_TRUE', opcode=115, arg=1, argval=310, argrepr='to 310', offset=306, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='RERAISE', opcode=119, arg=2, argval=2, argrepr='', offset=308, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=310, starts_line=None, is_jump_target=True, positions=None), + Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=312, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=314, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=316, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=318, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='JUMP_BACKWARD', opcode=140, arg=24, argval=274, argrepr='to 274', offset=320, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='COPY', opcode=120, arg=3, argval=3, argrepr='', offset=322, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=324, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=119, arg=1, argval=1, argrepr='', offset=326, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='PUSH_EXC_INFO', opcode=35, arg=None, argval=None, argrepr='', offset=328, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=116, arg=4, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=330, starts_line=22, is_jump_target=False, positions=None), - Instruction(opname='CHECK_EXC_MATCH', opcode=36, arg=None, argval=None, argrepr='', offset=342, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=16, argval=378, argrepr='to 378', offset=344, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=346, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=348, starts_line=23, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=100, arg=9, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=360, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=362, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=372, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=374, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='JUMP_BACKWARD', opcode=140, arg=52, argval=274, argrepr='to 274', offset=376, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=119, arg=0, argval=0, argrepr='', offset=378, starts_line=22, is_jump_target=True, positions=None), - Instruction(opname='COPY', opcode=120, arg=3, argval=3, argrepr='', offset=380, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=382, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=119, arg=1, argval=1, argrepr='', offset=384, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='PUSH_EXC_INFO', opcode=35, arg=None, argval=None, argrepr='', offset=386, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=388, starts_line=28, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=400, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=402, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=412, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=119, arg=0, argval=0, argrepr='', offset=414, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='COPY', opcode=120, arg=3, argval=3, argrepr='', offset=416, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=418, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=119, arg=1, argval=1, argrepr='', offset=420, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='JUMP_BACKWARD', opcode=140, arg=23, argval=274, argrepr='to 274', offset=318, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='COPY', opcode=120, arg=3, argval=3, argrepr='', offset=320, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=322, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='RERAISE', opcode=119, arg=1, argval=1, argrepr='', offset=324, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='PUSH_EXC_INFO', opcode=35, arg=None, argval=None, argrepr='', offset=326, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=4, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=328, starts_line=22, is_jump_target=False, positions=None), + Instruction(opname='CHECK_EXC_MATCH', opcode=36, arg=None, argval=None, argrepr='', offset=340, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=16, argval=376, argrepr='to 376', offset=342, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=344, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=346, starts_line=23, is_jump_target=False, positions=None), + Instruction(opname='LOAD_CONST', opcode=100, arg=9, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=358, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=360, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=370, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=372, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='JUMP_BACKWARD', opcode=140, arg=51, argval=274, argrepr='to 274', offset=374, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='RERAISE', opcode=119, arg=0, argval=0, argrepr='', offset=376, starts_line=22, is_jump_target=True, positions=None), + Instruction(opname='COPY', opcode=120, arg=3, argval=3, argrepr='', offset=378, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=380, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='RERAISE', opcode=119, arg=1, argval=1, argrepr='', offset=382, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='PUSH_EXC_INFO', opcode=35, arg=None, argval=None, argrepr='', offset=384, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=386, starts_line=28, is_jump_target=False, positions=None), + Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=398, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=400, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=410, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='RERAISE', opcode=119, arg=0, argval=0, argrepr='', offset=412, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='COPY', opcode=120, arg=3, argval=3, argrepr='', offset=414, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=416, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='RERAISE', opcode=119, arg=1, argval=1, argrepr='', offset=418, starts_line=None, is_jump_target=False, positions=None) ] # One last piece of inspect fodder to check the default line number handling def simple(): pass expected_opinfo_simple = [ Instruction(opname='RESUME', opcode=151, arg=0, argval=0, argrepr='', offset=0, starts_line=simple.__code__.co_firstlineno, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=2, starts_line=None, is_jump_target=False), - Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=4, starts_line=None, is_jump_target=False) + Instruction(opname='RETURN_CONST', opcode=121, arg=0, argval=None, argrepr='None', offset=2, starts_line=None, is_jump_target=False), ] @@ -1736,7 +1712,6 @@ def test_co_positions(self): (2, 2, 8, 9), (1, 3, 0, 1), (1, 3, 0, 1), - (1, 3, 0, 1), (1, 3, 0, 1) ] self.assertEqual(positions, expected) diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 239c9d03fd9d1f..707ff821b31a8a 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -117,7 +117,7 @@ def f(): return None self.assertNotInBytecode(f, 'LOAD_GLOBAL') - self.assertInBytecode(f, 'LOAD_CONST', None) + self.assertInBytecode(f, 'RETURN_CONST', None) self.check_lnotab(f) def test_while_one(self): @@ -134,7 +134,7 @@ def f(): def test_pack_unpack(self): for line, elem in ( - ('a, = a,', 'LOAD_CONST',), + ('a, = a,', 'RETURN_CONST',), ('a, b = a, b', 'SWAP',), ('a, b, c = a, b, c', 'SWAP',), ): @@ -165,7 +165,7 @@ def test_folding_of_tuples_of_constants(self): # One LOAD_CONST for the tuple, one for the None return value load_consts = [instr for instr in dis.get_instructions(code) if instr.opname == 'LOAD_CONST'] - self.assertEqual(len(load_consts), 2) + self.assertEqual(len(load_consts), 1) self.check_lnotab(code) # Bug 1053819: Tuple of constants misidentified when presented with: diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-07-14-56-43.gh-issue-101632.Fd1yxk.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-07-14-56-43.gh-issue-101632.Fd1yxk.rst new file mode 100644 index 00000000000000..136909ca699903 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-07-14-56-43.gh-issue-101632.Fd1yxk.rst @@ -0,0 +1 @@ +Adds a new :opcode:`RETURN_CONST` instruction. diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 6bc04bc8e848fc..0e52a3e2399c06 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -397,6 +397,8 @@ mark_stacks(PyCodeObject *code_obj, int len) assert(pop_value(next_stack) == EMPTY_STACK); assert(top_of_stack(next_stack) == Object); break; + case RETURN_CONST: + break; case RAISE_VARARGS: break; case RERAISE: diff --git a/Programs/test_frozenmain.h b/Programs/test_frozenmain.h index 95f78b19e65eb6..8e5055bd7bceb1 100644 --- a/Programs/test_frozenmain.h +++ b/Programs/test_frozenmain.h @@ -1,7 +1,7 @@ // Auto-generated by Programs/freeze_test_frozenmain.py unsigned char M_test_frozenmain[] = { 227,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0, - 0,0,0,0,0,243,184,0,0,0,151,0,100,0,100,1, + 0,0,0,0,0,243,182,0,0,0,151,0,100,0,100,1, 108,0,90,0,100,0,100,1,108,1,90,1,2,0,101,2, 100,2,171,1,0,0,0,0,0,0,0,0,1,0,2,0, 101,2,100,3,101,0,106,6,0,0,0,0,0,0,0,0, @@ -12,28 +12,28 @@ unsigned char M_test_frozenmain[] = { 0,0,0,0,90,5,100,5,68,0,93,23,0,0,90,6, 2,0,101,2,100,6,101,6,155,0,100,7,101,5,101,6, 25,0,0,0,0,0,0,0,0,0,155,0,157,4,171,1, - 0,0,0,0,0,0,0,0,1,0,140,25,4,0,100,1, - 83,0,41,8,233,0,0,0,0,78,122,18,70,114,111,122, - 101,110,32,72,101,108,108,111,32,87,111,114,108,100,122,8, - 115,121,115,46,97,114,103,118,218,6,99,111,110,102,105,103, - 41,5,218,12,112,114,111,103,114,97,109,95,110,97,109,101, - 218,10,101,120,101,99,117,116,97,98,108,101,218,15,117,115, - 101,95,101,110,118,105,114,111,110,109,101,110,116,218,17,99, - 111,110,102,105,103,117,114,101,95,99,95,115,116,100,105,111, - 218,14,98,117,102,102,101,114,101,100,95,115,116,100,105,111, - 122,7,99,111,110,102,105,103,32,122,2,58,32,41,7,218, - 3,115,121,115,218,17,95,116,101,115,116,105,110,116,101,114, - 110,97,108,99,97,112,105,218,5,112,114,105,110,116,218,4, - 97,114,103,118,218,11,103,101,116,95,99,111,110,102,105,103, - 115,114,3,0,0,0,218,3,107,101,121,169,0,243,0,0, - 0,0,250,18,116,101,115,116,95,102,114,111,122,101,110,109, - 97,105,110,46,112,121,250,8,60,109,111,100,117,108,101,62, - 114,18,0,0,0,1,0,0,0,115,100,0,0,0,240,3, - 1,1,1,243,8,0,1,11,219,0,24,225,0,5,208,6, - 26,213,0,27,217,0,5,128,106,144,35,151,40,145,40,213, - 0,27,216,9,38,208,9,26,215,9,38,209,9,38,212,9, - 40,168,24,212,9,50,128,6,240,2,6,12,2,242,0,7, - 1,42,128,67,241,14,0,5,10,208,10,40,144,67,209,10, - 40,152,54,160,35,156,59,209,10,40,214,4,41,242,15,7, - 1,42,114,16,0,0,0, + 0,0,0,0,0,0,0,0,1,0,140,25,4,0,121,1, + 41,8,233,0,0,0,0,78,122,18,70,114,111,122,101,110, + 32,72,101,108,108,111,32,87,111,114,108,100,122,8,115,121, + 115,46,97,114,103,118,218,6,99,111,110,102,105,103,41,5, + 218,12,112,114,111,103,114,97,109,95,110,97,109,101,218,10, + 101,120,101,99,117,116,97,98,108,101,218,15,117,115,101,95, + 101,110,118,105,114,111,110,109,101,110,116,218,17,99,111,110, + 102,105,103,117,114,101,95,99,95,115,116,100,105,111,218,14, + 98,117,102,102,101,114,101,100,95,115,116,100,105,111,122,7, + 99,111,110,102,105,103,32,122,2,58,32,41,7,218,3,115, + 121,115,218,17,95,116,101,115,116,105,110,116,101,114,110,97, + 108,99,97,112,105,218,5,112,114,105,110,116,218,4,97,114, + 103,118,218,11,103,101,116,95,99,111,110,102,105,103,115,114, + 3,0,0,0,218,3,107,101,121,169,0,243,0,0,0,0, + 250,18,116,101,115,116,95,102,114,111,122,101,110,109,97,105, + 110,46,112,121,250,8,60,109,111,100,117,108,101,62,114,18, + 0,0,0,1,0,0,0,115,100,0,0,0,240,3,1,1, + 1,243,8,0,1,11,219,0,24,225,0,5,208,6,26,213, + 0,27,217,0,5,128,106,144,35,151,40,145,40,213,0,27, + 216,9,38,208,9,26,215,9,38,209,9,38,212,9,40,168, + 24,212,9,50,128,6,240,2,6,12,2,242,0,7,1,42, + 128,67,241,14,0,5,10,208,10,40,144,67,209,10,40,152, + 54,160,35,156,59,209,10,40,214,4,41,241,15,7,1,42, + 114,16,0,0,0, }; diff --git a/Python/bytecodes.c b/Python/bytecodes.c index b43625fd283cc3..0d7d922816ce91 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -555,6 +555,23 @@ dummy_func( goto resume_frame; } + inst(RETURN_CONST, (--)) { + PyObject *retval = GETITEM(consts, oparg); + Py_INCREF(retval); + assert(EMPTY()); + _PyFrame_SetStackPointer(frame, stack_pointer); + TRACE_FUNCTION_EXIT(); + DTRACE_FUNCTION_EXIT(); + _Py_LeaveRecursiveCallPy(tstate); + assert(frame != &entry_frame); + // GH-99729: We need to unlink the frame *before* clearing it: + _PyInterpreterFrame *dying = frame; + frame = cframe.current_frame = dying->previous; + _PyEvalFrameClearAndPop(tstate, dying); + _PyFrame_StackPush(frame, retval); + goto resume_frame; + } + inst(GET_AITER, (obj -- iter)) { unaryfunc getter = NULL; PyTypeObject *type = Py_TYPE(obj); diff --git a/Python/compile.c b/Python/compile.c index d9ec68958972b5..df2dffb95bbd7e 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -124,6 +124,7 @@ #define IS_SCOPE_EXIT_OPCODE(opcode) \ ((opcode) == RETURN_VALUE || \ + (opcode) == RETURN_CONST || \ (opcode) == RAISE_VARARGS || \ (opcode) == RERAISE) @@ -354,7 +355,7 @@ basicblock_last_instr(const basicblock *b) { static inline int basicblock_returns(const basicblock *b) { struct instr *last = basicblock_last_instr(b); - return last && last->i_opcode == RETURN_VALUE; + return last && (last->i_opcode == RETURN_VALUE || last->i_opcode == RETURN_CONST); } static inline int @@ -1119,6 +1120,8 @@ stack_effect(int opcode, int oparg, int jump) case RETURN_VALUE: return -1; + case RETURN_CONST: + return 0; case SETUP_ANNOTATIONS: return 0; case YIELD_VALUE: @@ -9261,6 +9264,10 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts) } Py_DECREF(cnt); break; + case RETURN_VALUE: + INSTR_SET_OP1(inst, RETURN_CONST, oparg); + INSTR_SET_OP0(&bb->b_instr[i + 1], NOP); + break; } break; } @@ -9723,9 +9730,7 @@ remove_unused_consts(basicblock *entryblock, PyObject *consts) /* mark used consts */ for (basicblock *b = entryblock; b != NULL; b = b->b_next) { for (int i = 0; i < b->b_iused; i++) { - if (b->b_instr[i].i_opcode == LOAD_CONST || - b->b_instr[i].i_opcode == KW_NAMES) { - + if (HAS_CONST(b->b_instr[i].i_opcode)) { int index = b->b_instr[i].i_oparg; index_map[index] = index; } @@ -9780,9 +9785,7 @@ remove_unused_consts(basicblock *entryblock, PyObject *consts) for (basicblock *b = entryblock; b != NULL; b = b->b_next) { for (int i = 0; i < b->b_iused; i++) { - if (b->b_instr[i].i_opcode == LOAD_CONST || - b->b_instr[i].i_opcode == KW_NAMES) { - + if (HAS_CONST(b->b_instr[i].i_opcode)) { int index = b->b_instr[i].i_oparg; assert(reverse_index_map[index] >= 0); assert(reverse_index_map[index] < n_used_consts); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index ab19f431710644..de98b1a4f2ed72 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -742,6 +742,23 @@ goto resume_frame; } + TARGET(RETURN_CONST) { + PyObject *retval = GETITEM(consts, oparg); + Py_INCREF(retval); + assert(EMPTY()); + _PyFrame_SetStackPointer(frame, stack_pointer); + TRACE_FUNCTION_EXIT(); + DTRACE_FUNCTION_EXIT(); + _Py_LeaveRecursiveCallPy(tstate); + assert(frame != &entry_frame); + // GH-99729: We need to unlink the frame *before* clearing it: + _PyInterpreterFrame *dying = frame; + frame = cframe.current_frame = dying->previous; + _PyEvalFrameClearAndPop(tstate, dying); + _PyFrame_StackPush(frame, retval); + goto resume_frame; + } + TARGET(GET_AITER) { PyObject *obj = PEEK(1); PyObject *iter; diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index d2585351f69fd2..bae5492c0496e6 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -92,6 +92,8 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { return 1; case RETURN_VALUE: return 1; + case RETURN_CONST: + return 0; case GET_AITER: return 1; case GET_ANEXT: @@ -438,6 +440,8 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { return 0; case RETURN_VALUE: return 0; + case RETURN_CONST: + return 0; case GET_AITER: return 1; case GET_ANEXT: @@ -745,6 +749,7 @@ struct opcode_metadata { [RAISE_VARARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [INTERPRETER_EXIT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [RETURN_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [RETURN_CONST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [GET_AITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [GET_ANEXT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [GET_AWAITABLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index f1c3f3e0c4ee17..eceb246fac4909 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -120,7 +120,7 @@ static void *opcode_targets[256] = { &&TARGET_CONTAINS_OP, &&TARGET_RERAISE, &&TARGET_COPY, - &&TARGET_STORE_FAST__LOAD_FAST, + &&TARGET_RETURN_CONST, &&TARGET_BINARY_OP, &&TARGET_SEND, &&TARGET_LOAD_FAST, @@ -142,7 +142,7 @@ static void *opcode_targets[256] = { &&TARGET_JUMP_BACKWARD, &&TARGET_COMPARE_AND_BRANCH, &&TARGET_CALL_FUNCTION_EX, - &&TARGET_STORE_FAST__STORE_FAST, + &&TARGET_STORE_FAST__LOAD_FAST, &&TARGET_EXTENDED_ARG, &&TARGET_LIST_APPEND, &&TARGET_SET_ADD, @@ -152,15 +152,15 @@ static void *opcode_targets[256] = { &&TARGET_YIELD_VALUE, &&TARGET_RESUME, &&TARGET_MATCH_CLASS, + &&TARGET_STORE_FAST__STORE_FAST, &&TARGET_STORE_SUBSCR_DICT, - &&TARGET_STORE_SUBSCR_LIST_INT, &&TARGET_FORMAT_VALUE, &&TARGET_BUILD_CONST_KEY_MAP, &&TARGET_BUILD_STRING, + &&TARGET_STORE_SUBSCR_LIST_INT, &&TARGET_UNPACK_SEQUENCE_LIST, &&TARGET_UNPACK_SEQUENCE_TUPLE, &&TARGET_UNPACK_SEQUENCE_TWO_TUPLE, - &&_unknown_opcode, &&TARGET_LIST_EXTEND, &&TARGET_SET_UPDATE, &&TARGET_DICT_MERGE, From aacbdb0c650492756738b044e6ddf8b72f89a1a2 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 7 Feb 2023 15:44:37 -0800 Subject: [PATCH 038/247] gh-98831: Finish the UNPACK_SEQUENCE family (#101666) New generator feature: Generate useful glue for output arrays, so you can just write values to the output array (no bounds checking). Rewrote UNPACK_SEQUENCE_TWO_TUPLE to use this, and also UNPACK_SEQUENCE_{TUPLE,LIST}. --- Python/bytecodes.c | 37 +++++++++++------------- Python/generated_cases.c.h | 38 ++++++++++++++----------- Python/opcode_metadata.h | 16 +++++------ Tools/cases_generator/generate_cases.py | 25 +++++++++++----- Tools/cases_generator/test_generator.py | 19 ++++++------- 5 files changed, 72 insertions(+), 63 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 0d7d922816ce91..c6c00a7ab9b0cf 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -876,6 +876,13 @@ dummy_func( } } + family(unpack_sequence, INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE) = { + UNPACK_SEQUENCE, + UNPACK_SEQUENCE_TWO_TUPLE, + UNPACK_SEQUENCE_TUPLE, + UNPACK_SEQUENCE_LIST, + }; + inst(UNPACK_SEQUENCE, (unused/1, seq -- unused[oparg])) { #if ENABLE_SPECIALIZATION _PyUnpackSequenceCache *cache = (_PyUnpackSequenceCache *)next_instr; @@ -894,43 +901,36 @@ dummy_func( ERROR_IF(res == 0, error); } - inst(UNPACK_SEQUENCE_TWO_TUPLE, (unused/1, seq -- v1, v0)) { + inst(UNPACK_SEQUENCE_TWO_TUPLE, (unused/1, seq -- values[oparg])) { DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE); DEOPT_IF(PyTuple_GET_SIZE(seq) != 2, UNPACK_SEQUENCE); + assert(oparg == 2); STAT_INC(UNPACK_SEQUENCE, hit); - v1 = Py_NewRef(PyTuple_GET_ITEM(seq, 1)); - v0 = Py_NewRef(PyTuple_GET_ITEM(seq, 0)); + values[0] = Py_NewRef(PyTuple_GET_ITEM(seq, 1)); + values[1] = Py_NewRef(PyTuple_GET_ITEM(seq, 0)); Py_DECREF(seq); } - // stack effect: (__0 -- __array[oparg]) - inst(UNPACK_SEQUENCE_TUPLE) { - PyObject *seq = TOP(); + inst(UNPACK_SEQUENCE_TUPLE, (unused/1, seq -- values[oparg])) { DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE); DEOPT_IF(PyTuple_GET_SIZE(seq) != oparg, UNPACK_SEQUENCE); STAT_INC(UNPACK_SEQUENCE, hit); - STACK_SHRINK(1); PyObject **items = _PyTuple_ITEMS(seq); - while (oparg--) { - PUSH(Py_NewRef(items[oparg])); + for (int i = oparg; --i >= 0; ) { + *values++ = Py_NewRef(items[i]); } Py_DECREF(seq); - JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE); } - // stack effect: (__0 -- __array[oparg]) - inst(UNPACK_SEQUENCE_LIST) { - PyObject *seq = TOP(); + inst(UNPACK_SEQUENCE_LIST, (unused/1, seq -- values[oparg])) { DEOPT_IF(!PyList_CheckExact(seq), UNPACK_SEQUENCE); DEOPT_IF(PyList_GET_SIZE(seq) != oparg, UNPACK_SEQUENCE); STAT_INC(UNPACK_SEQUENCE, hit); - STACK_SHRINK(1); PyObject **items = _PyList_ITEMS(seq); - while (oparg--) { - PUSH(Py_NewRef(items[oparg])); + for (int i = oparg; --i >= 0; ) { + *values++ = Py_NewRef(items[i]); } Py_DECREF(seq); - JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE); } inst(UNPACK_EX, (seq -- unused[oparg & 0xFF], unused, unused[oparg >> 8])) { @@ -3185,6 +3185,3 @@ family(call, INLINE_CACHE_ENTRIES_CALL) = { CALL_NO_KW_METHOD_DESCRIPTOR_O, CALL_NO_KW_STR_1, CALL_NO_KW_TUPLE_1, CALL_NO_KW_TYPE_1 }; family(store_fast) = { STORE_FAST, STORE_FAST__LOAD_FAST, STORE_FAST__STORE_FAST }; -family(unpack_sequence, INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE) = { - UNPACK_SEQUENCE, UNPACK_SEQUENCE_LIST, - UNPACK_SEQUENCE_TUPLE, UNPACK_SEQUENCE_TWO_TUPLE }; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index de98b1a4f2ed72..ded68d011c6ba1 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -1125,6 +1125,7 @@ TARGET(UNPACK_SEQUENCE) { PREDICTED(UNPACK_SEQUENCE); + static_assert(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE == 1, "incorrect cache size"); PyObject *seq = PEEK(1); #if ENABLE_SPECIALIZATION _PyUnpackSequenceCache *cache = (_PyUnpackSequenceCache *)next_instr; @@ -1149,48 +1150,51 @@ TARGET(UNPACK_SEQUENCE_TWO_TUPLE) { PyObject *seq = PEEK(1); - PyObject *v1; - PyObject *v0; + PyObject **values = stack_pointer - (1); DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE); DEOPT_IF(PyTuple_GET_SIZE(seq) != 2, UNPACK_SEQUENCE); + assert(oparg == 2); STAT_INC(UNPACK_SEQUENCE, hit); - v1 = Py_NewRef(PyTuple_GET_ITEM(seq, 1)); - v0 = Py_NewRef(PyTuple_GET_ITEM(seq, 0)); + values[0] = Py_NewRef(PyTuple_GET_ITEM(seq, 1)); + values[1] = Py_NewRef(PyTuple_GET_ITEM(seq, 0)); Py_DECREF(seq); - STACK_GROW(1); - POKE(1, v0); - POKE(2, v1); + STACK_SHRINK(1); + STACK_GROW(oparg); JUMPBY(1); DISPATCH(); } TARGET(UNPACK_SEQUENCE_TUPLE) { - PyObject *seq = TOP(); + PyObject *seq = PEEK(1); + PyObject **values = stack_pointer - (1); DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE); DEOPT_IF(PyTuple_GET_SIZE(seq) != oparg, UNPACK_SEQUENCE); STAT_INC(UNPACK_SEQUENCE, hit); - STACK_SHRINK(1); PyObject **items = _PyTuple_ITEMS(seq); - while (oparg--) { - PUSH(Py_NewRef(items[oparg])); + for (int i = oparg; --i >= 0; ) { + *values++ = Py_NewRef(items[i]); } Py_DECREF(seq); - JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE); + STACK_SHRINK(1); + STACK_GROW(oparg); + JUMPBY(1); DISPATCH(); } TARGET(UNPACK_SEQUENCE_LIST) { - PyObject *seq = TOP(); + PyObject *seq = PEEK(1); + PyObject **values = stack_pointer - (1); DEOPT_IF(!PyList_CheckExact(seq), UNPACK_SEQUENCE); DEOPT_IF(PyList_GET_SIZE(seq) != oparg, UNPACK_SEQUENCE); STAT_INC(UNPACK_SEQUENCE, hit); - STACK_SHRINK(1); PyObject **items = _PyList_ITEMS(seq); - while (oparg--) { - PUSH(Py_NewRef(items[oparg])); + for (int i = oparg; --i >= 0; ) { + *values++ = Py_NewRef(items[i]); } Py_DECREF(seq); - JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE); + STACK_SHRINK(1); + STACK_GROW(oparg); + JUMPBY(1); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index bae5492c0496e6..c1e12a4bbede89 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -127,9 +127,9 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { case UNPACK_SEQUENCE_TWO_TUPLE: return 1; case UNPACK_SEQUENCE_TUPLE: - return -1; + return 1; case UNPACK_SEQUENCE_LIST: - return -1; + return 1; case UNPACK_EX: return 1; case STORE_ATTR: @@ -473,11 +473,11 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { case UNPACK_SEQUENCE: return oparg; case UNPACK_SEQUENCE_TWO_TUPLE: - return 2; + return oparg; case UNPACK_SEQUENCE_TUPLE: - return -1; + return oparg; case UNPACK_SEQUENCE_LIST: - return -1; + return oparg; case UNPACK_EX: return (oparg & 0xFF) + (oparg >> 8) + 1; case STORE_ATTR: @@ -765,9 +765,9 @@ struct opcode_metadata { [STORE_NAME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [DELETE_NAME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [UNPACK_SEQUENCE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, - [UNPACK_SEQUENCE_TWO_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, - [UNPACK_SEQUENCE_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [UNPACK_SEQUENCE_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [UNPACK_SEQUENCE_TWO_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, + [UNPACK_SEQUENCE_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, + [UNPACK_SEQUENCE_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, [UNPACK_EX] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [STORE_ATTR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, [DELETE_ATTR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 3925583b40e728..4f94b48d114de8 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -180,11 +180,8 @@ def assign(self, dst: StackEffect, src: StackEffect): stmt = f"if ({src.cond}) {{ {stmt} }}" self.emit(stmt) elif m := re.match(r"^&PEEK\(.*\)$", dst.name): - # NOTE: MOVE_ITEMS() does not actually exist. - # The only supported output array forms are: - # - unused[...] - # - X[...] where X[...] matches an input array exactly - self.emit(f"MOVE_ITEMS({dst.name}, {src.name}, {src.size});") + # The user code is responsible for writing to the output array. + pass elif m := re.match(r"^REG\(oparg(\d+)\)$", dst.name): self.emit(f"Py_XSETREF({dst.name}, {cast}{src.name});") else: @@ -309,10 +306,24 @@ def write(self, out: Formatter) -> None: out.declare(ieffect, src) # Write output stack effect variable declarations + isize = string_effect_size(list_effect_size(self.input_effects)) input_names = {ieffect.name for ieffect in self.input_effects} - for oeffect in self.output_effects: + for i, oeffect in enumerate(self.output_effects): if oeffect.name not in input_names: - out.declare(oeffect, None) + if oeffect.size: + osize = string_effect_size( + list_effect_size([oeff for oeff in self.output_effects[:i]]) + ) + offset = "stack_pointer" + if isize != osize: + if isize != "0": + offset += f" - ({isize})" + if osize != "0": + offset += f" + {osize}" + src = StackEffect(offset, "PyObject **") + out.declare(oeffect, src) + else: + out.declare(oeffect, None) # out.emit(f"JUMPBY(OPSIZE({self.inst.name}) - 1);") diff --git a/Tools/cases_generator/test_generator.py b/Tools/cases_generator/test_generator.py index 9df97d24ab6f43..0c3d04b45dd959 100644 --- a/Tools/cases_generator/test_generator.py +++ b/Tools/cases_generator/test_generator.py @@ -424,20 +424,18 @@ def test_array_input(): def test_array_output(): input = """ - inst(OP, (-- below, values[oparg*3], above)) { - spam(); + inst(OP, (unused, unused -- below, values[oparg*3], above)) { + spam(values, oparg); } """ output = """ TARGET(OP) { PyObject *below; - PyObject **values; + PyObject **values = stack_pointer - (2) + 1; PyObject *above; - spam(); - STACK_GROW(2); + spam(values, oparg); STACK_GROW(oparg*3); POKE(1, above); - MOVE_ITEMS(&PEEK(1 + oparg*3), values, oparg*3); POKE(2 + oparg*3, below); DISPATCH(); } @@ -446,18 +444,17 @@ def test_array_output(): def test_array_input_output(): input = """ - inst(OP, (below, values[oparg] -- values[oparg], above)) { - spam(); + inst(OP, (values[oparg] -- values[oparg], above)) { + spam(values, oparg); } """ output = """ TARGET(OP) { PyObject **values = &PEEK(oparg); - PyObject *below = PEEK(1 + oparg); PyObject *above; - spam(); + spam(values, oparg); + STACK_GROW(1); POKE(1, above); - MOVE_ITEMS(&PEEK(1 + oparg), values, oparg); DISPATCH(); } """ From b2b85b5db9cfdb24f966b61757536a898abc3830 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 7 Feb 2023 17:35:55 -0800 Subject: [PATCH 039/247] gh-98831: Modernize FORMAT_VALUE (#101628) Generator update: support balanced parentheses and brackets in conditions and size expressions. --- Python/bytecodes.c | 18 +++--------------- Python/generated_cases.c.h | 20 +++++++------------- Python/opcode_metadata.h | 4 ++-- Tools/cases_generator/parser.py | 9 ++++++++- Tools/cases_generator/test_generator.py | 8 ++++---- 5 files changed, 24 insertions(+), 35 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index c6c00a7ab9b0cf..d0f0513a36f8d5 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -3054,18 +3054,10 @@ dummy_func( ERROR_IF(slice == NULL, error); } - // error: FORMAT_VALUE has irregular stack effect - inst(FORMAT_VALUE) { + inst(FORMAT_VALUE, (value, fmt_spec if ((oparg & FVS_MASK) == FVS_HAVE_SPEC) -- result)) { /* Handles f-string value formatting. */ - PyObject *result; - PyObject *fmt_spec; - PyObject *value; PyObject *(*conv_fn)(PyObject *); int which_conversion = oparg & FVC_MASK; - int have_fmt_spec = (oparg & FVS_MASK) == FVS_HAVE_SPEC; - - fmt_spec = have_fmt_spec ? POP() : NULL; - value = POP(); /* See if any conversion is specified. */ switch (which_conversion) { @@ -3088,7 +3080,7 @@ dummy_func( Py_DECREF(value); if (result == NULL) { Py_XDECREF(fmt_spec); - goto error; + ERROR_IF(true, error); } value = result; } @@ -3106,12 +3098,8 @@ dummy_func( result = PyObject_Format(value, fmt_spec); Py_DECREF(value); Py_XDECREF(fmt_spec); - if (result == NULL) { - goto error; - } + ERROR_IF(result == NULL, error); } - - PUSH(result); } inst(COPY, (bottom, unused[oparg-1] -- bottom, unused[oparg-1], top)) { diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index ded68d011c6ba1..3ef808691e0171 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -3703,16 +3703,12 @@ } TARGET(FORMAT_VALUE) { - /* Handles f-string value formatting. */ + PyObject *fmt_spec = ((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? PEEK((((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? 1 : 0)) : NULL; + PyObject *value = PEEK(1 + (((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? 1 : 0)); PyObject *result; - PyObject *fmt_spec; - PyObject *value; + /* Handles f-string value formatting. */ PyObject *(*conv_fn)(PyObject *); int which_conversion = oparg & FVC_MASK; - int have_fmt_spec = (oparg & FVS_MASK) == FVS_HAVE_SPEC; - - fmt_spec = have_fmt_spec ? POP() : NULL; - value = POP(); /* See if any conversion is specified. */ switch (which_conversion) { @@ -3735,7 +3731,7 @@ Py_DECREF(value); if (result == NULL) { Py_XDECREF(fmt_spec); - goto error; + if (true) { STACK_SHRINK((((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? 1 : 0)); goto pop_1_error; } } value = result; } @@ -3753,12 +3749,10 @@ result = PyObject_Format(value, fmt_spec); Py_DECREF(value); Py_XDECREF(fmt_spec); - if (result == NULL) { - goto error; - } + if (result == NULL) { STACK_SHRINK((((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? 1 : 0)); goto pop_1_error; } } - - PUSH(result); + STACK_SHRINK((((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? 1 : 0)); + POKE(1, result); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index c1e12a4bbede89..52bab1c680e3e4 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -333,7 +333,7 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { case BUILD_SLICE: return ((oparg == 3) ? 1 : 0) + 2; case FORMAT_VALUE: - return -1; + return (((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? 1 : 0) + 1; case COPY: return (oparg-1) + 1; case BINARY_OP: @@ -681,7 +681,7 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { case BUILD_SLICE: return 1; case FORMAT_VALUE: - return -1; + return 1; case COPY: return (oparg-1) + 2; case BINARY_OP: diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py index ced66faee4931f..c7c8d8af6b7318 100644 --- a/Tools/cases_generator/parser.py +++ b/Tools/cases_generator/parser.py @@ -263,7 +263,14 @@ def stack_effect(self) -> StackEffect | None: @contextual def expression(self) -> Expression | None: tokens: list[lx.Token] = [] - while (tkn := self.peek()) and tkn.kind not in (lx.RBRACKET, lx.RPAREN): + level = 1 + while tkn := self.peek(): + if tkn.kind in (lx.LBRACKET, lx.LPAREN): + level += 1 + elif tkn.kind in (lx.RBRACKET, lx.RPAREN): + level -= 1 + if level == 0: + break tokens.append(tkn) self.next() if not tokens: diff --git a/Tools/cases_generator/test_generator.py b/Tools/cases_generator/test_generator.py index 0c3d04b45dd959..33bba7ee340a49 100644 --- a/Tools/cases_generator/test_generator.py +++ b/Tools/cases_generator/test_generator.py @@ -500,20 +500,20 @@ def test_register(): def test_cond_effect(): input = """ - inst(OP, (aa, input if (oparg & 1), cc -- xx, output if (oparg & 2), zz)) { + inst(OP, (aa, input if ((oparg & 1) == 1), cc -- xx, output if (oparg & 2), zz)) { output = spam(oparg, input); } """ output = """ TARGET(OP) { PyObject *cc = PEEK(1); - PyObject *input = (oparg & 1) ? PEEK(1 + ((oparg & 1) ? 1 : 0)) : NULL; - PyObject *aa = PEEK(2 + ((oparg & 1) ? 1 : 0)); + PyObject *input = ((oparg & 1) == 1) ? PEEK(1 + (((oparg & 1) == 1) ? 1 : 0)) : NULL; + PyObject *aa = PEEK(2 + (((oparg & 1) == 1) ? 1 : 0)); PyObject *xx; PyObject *output = NULL; PyObject *zz; output = spam(oparg, input); - STACK_SHRINK(((oparg & 1) ? 1 : 0)); + STACK_SHRINK((((oparg & 1) == 1) ? 1 : 0)); STACK_GROW(((oparg & 2) ? 1 : 0)); POKE(1, zz); if (oparg & 2) { POKE(1 + ((oparg & 2) ? 1 : 0), output); } From 790ff6bc6a56b4bd6e403aa43a984b99f7171dd7 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Wed, 8 Feb 2023 05:01:10 +0300 Subject: [PATCH 040/247] gh-101446: Change `repr` of `collections.OrderedDict` (#101661) --- Lib/collections/__init__.py | 2 +- Lib/test/test_ordered_dict.py | 4 +- ...-02-07-22-21-46.gh-issue-101446.-c0FdK.rst | 2 + Objects/odictobject.c | 53 +++---------------- 4 files changed, 11 insertions(+), 50 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-02-07-22-21-46.gh-issue-101446.-c0FdK.rst diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py index b5e4d16e9dbcad..a5393aad4249c0 100644 --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -267,7 +267,7 @@ def __repr__(self): 'od.__repr__() <==> repr(od)' if not self: return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, list(self.items())) + return '%s(%r)' % (self.__class__.__name__, dict(self.items())) def __reduce__(self): 'Return state information for pickling' diff --git a/Lib/test/test_ordered_dict.py b/Lib/test/test_ordered_dict.py index 37447fd249b8c0..decbcc2419c9fc 100644 --- a/Lib/test/test_ordered_dict.py +++ b/Lib/test/test_ordered_dict.py @@ -362,7 +362,7 @@ def test_repr(self): OrderedDict = self.OrderedDict od = OrderedDict([('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]) self.assertEqual(repr(od), - "OrderedDict([('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)])") + "OrderedDict({'c': 1, 'b': 2, 'a': 3, 'd': 4, 'e': 5, 'f': 6})") self.assertEqual(eval(repr(od)), od) self.assertEqual(repr(OrderedDict()), "OrderedDict()") @@ -372,7 +372,7 @@ def test_repr_recursive(self): od = OrderedDict.fromkeys('abc') od['x'] = od self.assertEqual(repr(od), - "OrderedDict([('a', None), ('b', None), ('c', None), ('x', ...)])") + "OrderedDict({'a': None, 'b': None, 'c': None, 'x': ...})") def test_repr_recursive_values(self): OrderedDict = self.OrderedDict diff --git a/Misc/NEWS.d/next/Library/2023-02-07-22-21-46.gh-issue-101446.-c0FdK.rst b/Misc/NEWS.d/next/Library/2023-02-07-22-21-46.gh-issue-101446.-c0FdK.rst new file mode 100644 index 00000000000000..ddf897b71bb1d1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-07-22-21-46.gh-issue-101446.-c0FdK.rst @@ -0,0 +1,2 @@ +Change repr of :class:`collections.OrderedDict` to use regular dictionary +formating instead of pairs of keys and values. diff --git a/Objects/odictobject.c b/Objects/odictobject.c index 4976b70b5dff5a..ab2bbed35873de 100644 --- a/Objects/odictobject.c +++ b/Objects/odictobject.c @@ -1367,7 +1367,7 @@ static PyObject * odict_repr(PyODictObject *self) { int i; - PyObject *pieces = NULL, *result = NULL; + PyObject *result = NULL, *dcopy = NULL; if (PyODict_SIZE(self) == 0) return PyUnicode_FromFormat("%s()", _PyType_Name(Py_TYPE(self))); @@ -1377,57 +1377,16 @@ odict_repr(PyODictObject *self) return i > 0 ? PyUnicode_FromString("...") : NULL; } - if (PyODict_CheckExact(self)) { - Py_ssize_t count = 0; - _ODictNode *node; - pieces = PyList_New(PyODict_SIZE(self)); - if (pieces == NULL) - goto Done; - - _odict_FOREACH(self, node) { - PyObject *pair; - PyObject *key = _odictnode_KEY(node); - PyObject *value = _odictnode_VALUE(node, self); - if (value == NULL) { - if (!PyErr_Occurred()) - PyErr_SetObject(PyExc_KeyError, key); - goto Done; - } - pair = PyTuple_Pack(2, key, value); - if (pair == NULL) - goto Done; - - if (count < PyList_GET_SIZE(pieces)) - PyList_SET_ITEM(pieces, count, pair); /* steals reference */ - else { - if (PyList_Append(pieces, pair) < 0) { - Py_DECREF(pair); - goto Done; - } - Py_DECREF(pair); - } - count++; - } - if (count < PyList_GET_SIZE(pieces)) { - Py_SET_SIZE(pieces, count); - } - } - else { - PyObject *items = PyObject_CallMethodNoArgs( - (PyObject *)self, &_Py_ID(items)); - if (items == NULL) - goto Done; - pieces = PySequence_List(items); - Py_DECREF(items); - if (pieces == NULL) - goto Done; + dcopy = PyDict_Copy((PyObject *)self); + if (dcopy == NULL) { + goto Done; } result = PyUnicode_FromFormat("%s(%R)", - _PyType_Name(Py_TYPE(self)), pieces); + _PyType_Name(Py_TYPE(self)), + dcopy); Done: - Py_XDECREF(pieces); Py_ReprLeave((PyObject *)self); return result; } From a9f01448a99c6a2ae34d448806176f2df3a5b323 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 7 Feb 2023 20:03:22 -0800 Subject: [PATCH 041/247] gh-98831: Modernize CALL_FUNCTION_EX (#101627) New generator feature: Move CHECK_EVAL_BREAKER() call to just before DISPATCH(). --- Python/bytecodes.c | 23 +++++++-------------- Python/generated_cases.c.h | 27 ++++++++++++------------- Python/opcode_metadata.h | 4 ++-- Tools/cases_generator/generate_cases.py | 15 +++++++++++--- Tools/cases_generator/test_generator.py | 9 +++++++-- 5 files changed, 41 insertions(+), 37 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index d0f0513a36f8d5..9633f34212a68d 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2951,26 +2951,21 @@ dummy_func( CHECK_EVAL_BREAKER(); } - // error: CALL_FUNCTION_EX has irregular stack effect - inst(CALL_FUNCTION_EX) { - PyObject *func, *callargs, *kwargs = NULL, *result; - if (oparg & 0x01) { - kwargs = POP(); + inst(CALL_FUNCTION_EX, (unused, func, callargs, kwargs if (oparg & 1) -- result)) { + if (oparg & 1) { // DICT_MERGE is called before this opcode if there are kwargs. // It converts all dict subtypes in kwargs into regular dicts. assert(PyDict_CheckExact(kwargs)); } - callargs = POP(); - func = TOP(); if (!PyTuple_CheckExact(callargs)) { if (check_args_iterable(tstate, func, callargs) < 0) { - Py_DECREF(callargs); goto error; } - Py_SETREF(callargs, PySequence_Tuple(callargs)); - if (callargs == NULL) { + PyObject *tuple = PySequence_Tuple(callargs); + if (tuple == NULL) { goto error; } + Py_SETREF(callargs, tuple); } assert(PyTuple_CheckExact(callargs)); @@ -2979,12 +2974,8 @@ dummy_func( Py_DECREF(callargs); Py_XDECREF(kwargs); - STACK_SHRINK(1); - assert(TOP() == NULL); - SET_TOP(result); - if (result == NULL) { - goto error; - } + assert(PEEK(3 + (oparg & 1)) == NULL); + ERROR_IF(result == NULL, error); CHECK_EVAL_BREAKER(); } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 3ef808691e0171..f38286441be4b3 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -3587,24 +3587,24 @@ TARGET(CALL_FUNCTION_EX) { PREDICTED(CALL_FUNCTION_EX); - PyObject *func, *callargs, *kwargs = NULL, *result; - if (oparg & 0x01) { - kwargs = POP(); + PyObject *kwargs = (oparg & 1) ? PEEK(((oparg & 1) ? 1 : 0)) : NULL; + PyObject *callargs = PEEK(1 + ((oparg & 1) ? 1 : 0)); + PyObject *func = PEEK(2 + ((oparg & 1) ? 1 : 0)); + PyObject *result; + if (oparg & 1) { // DICT_MERGE is called before this opcode if there are kwargs. // It converts all dict subtypes in kwargs into regular dicts. assert(PyDict_CheckExact(kwargs)); } - callargs = POP(); - func = TOP(); if (!PyTuple_CheckExact(callargs)) { if (check_args_iterable(tstate, func, callargs) < 0) { - Py_DECREF(callargs); goto error; } - Py_SETREF(callargs, PySequence_Tuple(callargs)); - if (callargs == NULL) { + PyObject *tuple = PySequence_Tuple(callargs); + if (tuple == NULL) { goto error; } + Py_SETREF(callargs, tuple); } assert(PyTuple_CheckExact(callargs)); @@ -3613,12 +3613,11 @@ Py_DECREF(callargs); Py_XDECREF(kwargs); - STACK_SHRINK(1); - assert(TOP() == NULL); - SET_TOP(result); - if (result == NULL) { - goto error; - } + assert(PEEK(3 + (oparg & 1)) == NULL); + if (result == NULL) { STACK_SHRINK(((oparg & 1) ? 1 : 0)); goto pop_3_error; } + STACK_SHRINK(((oparg & 1) ? 1 : 0)); + STACK_SHRINK(2); + POKE(1, result); CHECK_EVAL_BREAKER(); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index 52bab1c680e3e4..054ef6c2998234 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -325,7 +325,7 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { case CALL_NO_KW_METHOD_DESCRIPTOR_FAST: return -1; case CALL_FUNCTION_EX: - return -1; + return ((oparg & 1) ? 1 : 0) + 3; case MAKE_FUNCTION: return ((oparg & 0x01) ? 1 : 0) + ((oparg & 0x02) ? 1 : 0) + ((oparg & 0x04) ? 1 : 0) + ((oparg & 0x08) ? 1 : 0) + 1; case RETURN_GENERATOR: @@ -673,7 +673,7 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { case CALL_NO_KW_METHOD_DESCRIPTOR_FAST: return -1; case CALL_FUNCTION_EX: - return -1; + return 1; case MAKE_FUNCTION: return 1; case RETURN_GENERATOR: diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 4f94b48d114de8..9b5aa914cdee86 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -227,7 +227,8 @@ def __init__(self, inst: parser.InstDef): self.kind = inst.kind self.name = inst.name self.block = inst.block - self.block_text, self.predictions = extract_block_text(self.block) + self.block_text, self.check_eval_breaker, self.predictions = \ + extract_block_text(self.block) self.always_exits = always_exits(self.block_text) self.cache_effects = [ effect for effect in inst.inputs if isinstance(effect, parser.CacheEffect) @@ -1027,6 +1028,8 @@ def write_instr(self, instr: Instruction) -> None: if not instr.always_exits: for prediction in instr.predictions: self.out.emit(f"PREDICT({prediction});") + if instr.check_eval_breaker: + self.out.emit("CHECK_EVAL_BREAKER();") self.out.emit(f"DISPATCH();") def write_super(self, sup: SuperInstruction) -> None: @@ -1102,7 +1105,7 @@ def wrap_super_or_macro(self, up: SuperOrMacroInstruction): self.out.emit(f"DISPATCH();") -def extract_block_text(block: parser.Block) -> tuple[list[str], list[str]]: +def extract_block_text(block: parser.Block) -> tuple[list[str], bool, list[str]]: # Get lines of text with proper dedent blocklines = block.text.splitlines(True) @@ -1122,6 +1125,12 @@ def extract_block_text(block: parser.Block) -> tuple[list[str], list[str]]: while blocklines and not blocklines[-1].strip(): blocklines.pop() + # Separate CHECK_EVAL_BREAKER() macro from end + check_eval_breaker = \ + blocklines != [] and blocklines[-1].strip() == "CHECK_EVAL_BREAKER();" + if check_eval_breaker: + del blocklines[-1] + # Separate PREDICT(...) macros from end predictions: list[str] = [] while blocklines and ( @@ -1130,7 +1139,7 @@ def extract_block_text(block: parser.Block) -> tuple[list[str], list[str]]: predictions.insert(0, m.group(1)) blocklines.pop() - return blocklines, predictions + return blocklines, check_eval_breaker, predictions def always_exits(lines: list[str]) -> bool: diff --git a/Tools/cases_generator/test_generator.py b/Tools/cases_generator/test_generator.py index 33bba7ee340a49..036094ac8ef487 100644 --- a/Tools/cases_generator/test_generator.py +++ b/Tools/cases_generator/test_generator.py @@ -177,15 +177,16 @@ def test_overlap(): """ run_cases_test(input, output) -def test_predictions(): +def test_predictions_and_eval_breaker(): input = """ inst(OP1, (--)) { } inst(OP2, (--)) { } - inst(OP3, (--)) { + inst(OP3, (arg -- res)) { DEOPT_IF(xxx, OP1); PREDICT(OP2); + CHECK_EVAL_BREAKER(); } """ output = """ @@ -200,8 +201,12 @@ def test_predictions(): } TARGET(OP3) { + PyObject *arg = PEEK(1); + PyObject *res; DEOPT_IF(xxx, OP1); + POKE(1, res); PREDICT(OP2); + CHECK_EVAL_BREAKER(); DISPATCH(); } """ From 027adf42cd85db41fee05b0a40d89ef822876c97 Mon Sep 17 00:00:00 2001 From: Stanley <46876382+slateny@users.noreply.github.com> Date: Wed, 8 Feb 2023 00:12:46 -0800 Subject: [PATCH 042/247] gh-47937: Note that Popen attributes are read-only (#93070) * Note that Popen attributes aren't meant to be set by users by rewording the text about the attributes. * Also update some universal_newlines references to mention the modern text parameter name while in the area. Co-authored-by: Gregory P. Smith --- Doc/library/subprocess.rst | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst index 785251afdf262e..a87369a2461a54 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -457,7 +457,7 @@ functions. - :const:`0` means unbuffered (read and write are one system call and can return short) - :const:`1` means line buffered - (only usable if ``universal_newlines=True`` i.e., in a text mode) + (only usable if ``text=True`` or ``universal_newlines=True``) - any other positive value means use a buffer of approximately that size - negative bufsize (the default) means the system default of @@ -847,7 +847,8 @@ Instances of the :class:`Popen` class have the following methods: On Windows :meth:`kill` is an alias for :meth:`terminate`. -The following attributes are also available: +The following attributes are also set by the class for you to access. +Reassigning them to new values is unsupported: .. attribute:: Popen.args @@ -860,9 +861,9 @@ The following attributes are also available: If the *stdin* argument was :data:`PIPE`, this attribute is a writeable stream object as returned by :func:`open`. If the *encoding* or *errors* - arguments were specified or the *universal_newlines* argument was ``True``, - the stream is a text stream, otherwise it is a byte stream. If the *stdin* - argument was not :data:`PIPE`, this attribute is ``None``. + arguments were specified or the *text* or *universal_newlines* argument + was ``True``, the stream is a text stream, otherwise it is a byte stream. + If the *stdin* argument was not :data:`PIPE`, this attribute is ``None``. .. attribute:: Popen.stdout @@ -870,9 +871,9 @@ The following attributes are also available: If the *stdout* argument was :data:`PIPE`, this attribute is a readable stream object as returned by :func:`open`. Reading from the stream provides output from the child process. If the *encoding* or *errors* arguments were - specified or the *universal_newlines* argument was ``True``, the stream is a - text stream, otherwise it is a byte stream. If the *stdout* argument was not - :data:`PIPE`, this attribute is ``None``. + specified or the *text* or *universal_newlines* argument was ``True``, the + stream is a text stream, otherwise it is a byte stream. If the *stdout* + argument was not :data:`PIPE`, this attribute is ``None``. .. attribute:: Popen.stderr @@ -880,9 +881,9 @@ The following attributes are also available: If the *stderr* argument was :data:`PIPE`, this attribute is a readable stream object as returned by :func:`open`. Reading from the stream provides error output from the child process. If the *encoding* or *errors* arguments - were specified or the *universal_newlines* argument was ``True``, the stream - is a text stream, otherwise it is a byte stream. If the *stderr* argument was - not :data:`PIPE`, this attribute is ``None``. + were specified or the *text* or *universal_newlines* argument was ``True``, the + stream is a text stream, otherwise it is a byte stream. If the *stderr* argument + was not :data:`PIPE`, this attribute is ``None``. .. warning:: From feec49c40736fc05626a183a8d14c4ebbea5ae28 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 8 Feb 2023 09:31:12 +0000 Subject: [PATCH 043/247] GH-101578: Normalize the current exception (GH-101607) * Make sure that the current exception is always normalized. * Remove redundant type and traceback fields for the current exception. * Add new API functions: PyErr_GetRaisedException, PyErr_SetRaisedException * Add new API functions: PyException_GetArgs, PyException_SetArgs --- Doc/c-api/exceptions.rst | 79 +++++- Doc/data/stable_abi.dat | 4 + Include/cpython/pyerrors.h | 1 + Include/cpython/pystate.h | 4 +- Include/internal/pycore_pyerrors.h | 11 +- Include/pyerrors.h | 6 + Lib/test/test_capi/test_misc.py | 39 +++ Lib/test/test_exceptions.py | 8 +- Lib/test/test_stable_abi_ctypes.py | 4 + ...-02-06-16-14-30.gh-issue-101578.PW5fA9.rst | 13 + Misc/stable_abi.toml | 9 + Modules/_testcapi/heaptype.c | 24 +- Modules/_testcapimodule.c | 37 +++ Modules/gcmodule.c | 5 +- Objects/dictobject.c | 6 +- Objects/exceptions.c | 75 +++++- Objects/object.c | 12 +- PC/python3dll.c | 4 + Parser/pegen.c | 15 +- Python/bytecodes.c | 4 +- Python/ceval.c | 12 +- Python/errors.c | 225 +++++++++++------- Python/generated_cases.c.h | 4 +- Python/import.c | 7 + Python/initconfig.c | 5 +- Python/pystate.c | 4 +- Python/pythonrun.c | 11 +- Python/sysmodule.c | 10 +- Python/traceback.c | 9 +- 29 files changed, 476 insertions(+), 171 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2023-02-06-16-14-30.gh-issue-101578.PW5fA9.rst diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst index 087e0a61d12d59..de9b15edd6859a 100644 --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -400,8 +400,61 @@ Querying the error indicator recursively in subtuples) are searched for a match. +.. c:function:: PyObject *PyErr_GetRaisedException(void) + + Returns the exception currently being raised, clearing the exception at + the same time. Do not confuse this with the exception currently being + handled which can be accessed with :c:func:`PyErr_GetHandledException`. + + .. note:: + + This function is normally only used by code that needs to catch exceptions or + by code that needs to save and restore the error indicator temporarily, e.g.:: + + { + PyObject *exc = PyErr_GetRaisedException(); + + /* ... code that might produce other errors ... */ + + PyErr_SetRaisedException(exc); + } + + .. versionadded:: 3.12 + + +.. c:function:: void PyErr_SetRaisedException(PyObject *exc) + + Sets the exception currently being raised ``exc``. + If the exception is already set, it is cleared first. + + ``exc`` must be a valid exception. + (Violating this rules will cause subtle problems later.) + This call consumes a reference to the ``exc`` object: you must own a + reference to that object before the call and after the call you no longer own + that reference. + (If you don't understand this, don't use this function. I warned you.) + + .. note:: + + This function is normally only used by code that needs to save and restore the + error indicator temporarily. Use :c:func:`PyErr_GetRaisedException` to save + the current exception, e.g.:: + + { + PyObject *exc = PyErr_GetRaisedException(); + + /* ... code that might produce other errors ... */ + + PyErr_SetRaisedException(exc); + } + + .. versionadded:: 3.12 + + .. c:function:: void PyErr_Fetch(PyObject **ptype, PyObject **pvalue, PyObject **ptraceback) + As of 3.12, this function is deprecated. Use :c:func:`PyErr_GetRaisedException` instead. + Retrieve the error indicator into three variables whose addresses are passed. If the error indicator is not set, set all three variables to ``NULL``. If it is set, it will be cleared and you own a reference to each object retrieved. The @@ -421,10 +474,14 @@ Querying the error indicator PyErr_Restore(type, value, traceback); } + .. deprecated:: 3.12 + .. c:function:: void PyErr_Restore(PyObject *type, PyObject *value, PyObject *traceback) - Set the error indicator from the three objects. If the error indicator is + As of 3.12, this function is deprecated. Use :c:func:`PyErr_SetRaisedException` instead. + + Set the error indicator from the three objects. If the error indicator is already set, it is cleared first. If the objects are ``NULL``, the error indicator is cleared. Do not pass a ``NULL`` type and non-``NULL`` value or traceback. The exception type should be a class. Do not pass an invalid @@ -440,9 +497,15 @@ Querying the error indicator error indicator temporarily. Use :c:func:`PyErr_Fetch` to save the current error indicator. + .. deprecated:: 3.12 + .. c:function:: void PyErr_NormalizeException(PyObject **exc, PyObject **val, PyObject **tb) + As of 3.12, this function is deprecated. + Use :c:func:`PyErr_GetRaisedException` instead of :c:func:`PyErr_Fetch` to avoid + any possible de-normalization. + Under certain circumstances, the values returned by :c:func:`PyErr_Fetch` below can be "unnormalized", meaning that ``*exc`` is a class object but ``*val`` is not an instance of the same class. This function can be used to instantiate @@ -459,6 +522,8 @@ Querying the error indicator PyException_SetTraceback(val, tb); } + .. deprecated:: 3.12 + .. c:function:: PyObject* PyErr_GetHandledException(void) @@ -704,6 +769,18 @@ Exception Objects :attr:`__suppress_context__` is implicitly set to ``True`` by this function. +.. c:function:: PyObject* PyException_GetArgs(PyObject *ex) + + Return args of the given exception as a new reference, + as accessible from Python through :attr:`args`. + + +.. c:function:: void PyException_SetArgs(PyObject *ex, PyObject *args) + + Set the args of the given exception, + as accessible from Python through :attr:`args`. + + .. _unicodeexceptions: Unicode Exception Objects diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat index 53895bbced8408..68ab0b5061f434 100644 --- a/Doc/data/stable_abi.dat +++ b/Doc/data/stable_abi.dat @@ -139,6 +139,7 @@ function,PyErr_Format,3.2,, function,PyErr_FormatV,3.5,, function,PyErr_GetExcInfo,3.7,, function,PyErr_GetHandledException,3.11,, +function,PyErr_GetRaisedException,3.12,, function,PyErr_GivenExceptionMatches,3.2,, function,PyErr_NewException,3.2,, function,PyErr_NewExceptionWithDoc,3.2,, @@ -168,6 +169,7 @@ function,PyErr_SetInterrupt,3.2,, function,PyErr_SetInterruptEx,3.10,, function,PyErr_SetNone,3.2,, function,PyErr_SetObject,3.2,, +function,PyErr_SetRaisedException,3.12,, function,PyErr_SetString,3.2,, function,PyErr_SyntaxLocation,3.2,, function,PyErr_SyntaxLocationEx,3.7,, @@ -266,9 +268,11 @@ var,PyExc_Warning,3.2,, var,PyExc_WindowsError,3.7,on Windows, var,PyExc_ZeroDivisionError,3.2,, function,PyExceptionClass_Name,3.8,, +function,PyException_GetArgs,3.12,, function,PyException_GetCause,3.2,, function,PyException_GetContext,3.2,, function,PyException_GetTraceback,3.2,, +function,PyException_SetArgs,3.12,, function,PyException_SetCause,3.2,, function,PyException_SetContext,3.2,, function,PyException_SetTraceback,3.2,, diff --git a/Include/cpython/pyerrors.h b/Include/cpython/pyerrors.h index 141341667795e8..0d9cc9922f7368 100644 --- a/Include/cpython/pyerrors.h +++ b/Include/cpython/pyerrors.h @@ -99,6 +99,7 @@ PyAPI_FUNC(void) _PyErr_GetExcInfo(PyThreadState *, PyObject **, PyObject **, Py /* Context manipulation (PEP 3134) */ PyAPI_FUNC(void) _PyErr_ChainExceptions(PyObject *, PyObject *, PyObject *); +PyAPI_FUNC(void) _PyErr_ChainExceptions1(PyObject *); /* Like PyErr_Format(), but saves current exception as __context__ and __cause__. diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index f5db52f76e8f4f..be1fcb61fa2244 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -166,9 +166,7 @@ struct _ts { PyObject *c_traceobj; /* The exception currently being raised */ - PyObject *curexc_type; - PyObject *curexc_value; - PyObject *curexc_traceback; + PyObject *current_exception; /* Pointer to the top of the exception stack for the exceptions * we may be currently handling. (See _PyErr_StackItem above.) diff --git a/Include/internal/pycore_pyerrors.h b/Include/internal/pycore_pyerrors.h index 66f37942ef916a..1bb4a9aa103898 100644 --- a/Include/internal/pycore_pyerrors.h +++ b/Include/internal/pycore_pyerrors.h @@ -20,7 +20,10 @@ extern void _PyErr_FiniTypes(PyInterpreterState *); static inline PyObject* _PyErr_Occurred(PyThreadState *tstate) { assert(tstate != NULL); - return tstate->curexc_type; + if (tstate->current_exception == NULL) { + return NULL; + } + return (PyObject *)Py_TYPE(tstate->current_exception); } static inline void _PyErr_ClearExcState(_PyErr_StackItem *exc_state) @@ -37,10 +40,16 @@ PyAPI_FUNC(void) _PyErr_Fetch( PyObject **value, PyObject **traceback); +extern PyObject * +_PyErr_GetRaisedException(PyThreadState *tstate); + PyAPI_FUNC(int) _PyErr_ExceptionMatches( PyThreadState *tstate, PyObject *exc); +void +_PyErr_SetRaisedException(PyThreadState *tstate, PyObject *exc); + PyAPI_FUNC(void) _PyErr_Restore( PyThreadState *tstate, PyObject *type, diff --git a/Include/pyerrors.h b/Include/pyerrors.h index d5ac6af5b32c6c..d089fa71779330 100644 --- a/Include/pyerrors.h +++ b/Include/pyerrors.h @@ -18,6 +18,8 @@ PyAPI_FUNC(PyObject *) PyErr_Occurred(void); PyAPI_FUNC(void) PyErr_Clear(void); PyAPI_FUNC(void) PyErr_Fetch(PyObject **, PyObject **, PyObject **); PyAPI_FUNC(void) PyErr_Restore(PyObject *, PyObject *, PyObject *); +PyAPI_FUNC(PyObject *) PyErr_GetRaisedException(void); +PyAPI_FUNC(void) PyErr_SetRaisedException(PyObject *); #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030b0000 PyAPI_FUNC(PyObject*) PyErr_GetHandledException(void); PyAPI_FUNC(void) PyErr_SetHandledException(PyObject *); @@ -51,6 +53,10 @@ PyAPI_FUNC(void) PyException_SetCause(PyObject *, PyObject *); PyAPI_FUNC(PyObject *) PyException_GetContext(PyObject *); PyAPI_FUNC(void) PyException_SetContext(PyObject *, PyObject *); + +PyAPI_FUNC(PyObject *) PyException_GetArgs(PyObject *); +PyAPI_FUNC(void) PyException_SetArgs(PyObject *, PyObject *); + /* */ #define PyExceptionClass_Check(x) \ diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 03e22d7a2d382d..7612cddb1f6576 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -1553,5 +1553,44 @@ def func2(x=None): self.do_test(func2) +class Test_ErrSetAndRestore(unittest.TestCase): + + def test_err_set_raised(self): + with self.assertRaises(ValueError): + _testcapi.err_set_raised(ValueError()) + v = ValueError() + try: + _testcapi.err_set_raised(v) + except ValueError as ex: + self.assertIs(v, ex) + + def test_err_restore(self): + with self.assertRaises(ValueError): + _testcapi.err_restore(ValueError) + with self.assertRaises(ValueError): + _testcapi.err_restore(ValueError, 1) + with self.assertRaises(ValueError): + _testcapi.err_restore(ValueError, 1, None) + with self.assertRaises(ValueError): + _testcapi.err_restore(ValueError, ValueError()) + try: + _testcapi.err_restore(KeyError, "hi") + except KeyError as k: + self.assertEqual("hi", k.args[0]) + try: + 1/0 + except Exception as e: + tb = e.__traceback__ + with self.assertRaises(ValueError): + _testcapi.err_restore(ValueError, 1, tb) + with self.assertRaises(TypeError): + _testcapi.err_restore(ValueError, 1, 0) + try: + _testcapi.err_restore(ValueError, 1, tb) + except ValueError as v: + self.assertEqual(1, v.args[0]) + self.assertIs(tb, v.__traceback__.tb_next) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index f629321458d8ae..4ae71e431c56dc 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -347,6 +347,7 @@ def test_capi2(): _testcapi.raise_exception(BadException, 0) except RuntimeError as err: exc, err, tb = sys.exc_info() + tb = tb.tb_next co = tb.tb_frame.f_code self.assertEqual(co.co_name, "__init__") self.assertTrue(co.co_filename.endswith('test_exceptions.py')) @@ -1415,8 +1416,8 @@ def gen(): @cpython_only def test_recursion_normalizing_infinite_exception(self): # Issue #30697. Test that a RecursionError is raised when - # PyErr_NormalizeException() maximum recursion depth has been - # exceeded. + # maximum recursion depth has been exceeded when creating + # an exception code = """if 1: import _testcapi try: @@ -1426,8 +1427,7 @@ def test_recursion_normalizing_infinite_exception(self): """ rc, out, err = script_helper.assert_python_failure("-c", code) self.assertEqual(rc, 1) - self.assertIn(b'RecursionError: maximum recursion depth exceeded ' - b'while normalizing an exception', err) + self.assertIn(b'RecursionError: maximum recursion depth exceeded', err) self.assertIn(b'Done.', out) diff --git a/Lib/test/test_stable_abi_ctypes.py b/Lib/test/test_stable_abi_ctypes.py index 67c653428a6dee..e77c1c8409880d 100644 --- a/Lib/test/test_stable_abi_ctypes.py +++ b/Lib/test/test_stable_abi_ctypes.py @@ -172,6 +172,7 @@ def test_windows_feature_macros(self): "PyErr_FormatV", "PyErr_GetExcInfo", "PyErr_GetHandledException", + "PyErr_GetRaisedException", "PyErr_GivenExceptionMatches", "PyErr_NewException", "PyErr_NewExceptionWithDoc", @@ -195,6 +196,7 @@ def test_windows_feature_macros(self): "PyErr_SetInterruptEx", "PyErr_SetNone", "PyErr_SetObject", + "PyErr_SetRaisedException", "PyErr_SetString", "PyErr_SyntaxLocation", "PyErr_SyntaxLocationEx", @@ -292,9 +294,11 @@ def test_windows_feature_macros(self): "PyExc_Warning", "PyExc_ZeroDivisionError", "PyExceptionClass_Name", + "PyException_GetArgs", "PyException_GetCause", "PyException_GetContext", "PyException_GetTraceback", + "PyException_SetArgs", "PyException_SetCause", "PyException_SetContext", "PyException_SetTraceback", diff --git a/Misc/NEWS.d/next/C API/2023-02-06-16-14-30.gh-issue-101578.PW5fA9.rst b/Misc/NEWS.d/next/C API/2023-02-06-16-14-30.gh-issue-101578.PW5fA9.rst new file mode 100644 index 00000000000000..fc694f6e051b53 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2023-02-06-16-14-30.gh-issue-101578.PW5fA9.rst @@ -0,0 +1,13 @@ +Add new C-API functions for saving and restoring the current exception: +``PyErr_GetRaisedException`` and ``PyErr_SetRaisedException``. +These functions take and return a single exception rather than +the triple of ``PyErr_Fetch`` and ``PyErr_Restore``. +This is less error prone and a bit more efficient. + +The three arguments forms of saving and restoring the +current exception: ``PyErr_Fetch`` and ``PyErr_Restore`` +are deprecated. + +Also add ``PyException_GetArgs`` and ``PyException_SetArgs`` +as convenience functions to help dealing with +exceptions in the C API. diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml index c716f403d638ac..21ff9616133445 100644 --- a/Misc/stable_abi.toml +++ b/Misc/stable_abi.toml @@ -2333,6 +2333,15 @@ added = '3.12' [function.PyVectorcall_Call] added = '3.12' +[function.PyErr_GetRaisedException] + added = '3.12' +[function.PyErr_SetRaisedException] + added = '3.12' +[function.PyException_GetArgs] + added = '3.12' +[function.PyException_SetArgs] + added = '3.12' + [typedef.vectorcallfunc] added = '3.12' [function.PyObject_Vectorcall] diff --git a/Modules/_testcapi/heaptype.c b/Modules/_testcapi/heaptype.c index bf80fd64d80b35..39639f7ed048f2 100644 --- a/Modules/_testcapi/heaptype.c +++ b/Modules/_testcapi/heaptype.c @@ -116,10 +116,10 @@ test_from_spec_invalid_metatype_inheritance(PyObject *self, PyObject *Py_UNUSED( PyObject *bases = NULL; PyObject *new = NULL; PyObject *meta_error_string = NULL; - PyObject *exc_type = NULL; - PyObject *exc_value = NULL; - PyObject *exc_traceback = NULL; + PyObject *exc = NULL; PyObject *result = NULL; + PyObject *message = NULL; + PyObject *args = NULL; metaclass_a = PyType_FromSpecWithBases(&MinimalMetaclass_spec, (PyObject*)&PyType_Type); if (metaclass_a == NULL) { @@ -156,13 +156,19 @@ test_from_spec_invalid_metatype_inheritance(PyObject *self, PyObject *Py_UNUSED( // Assert that the correct exception was raised if (PyErr_ExceptionMatches(PyExc_TypeError)) { - PyErr_Fetch(&exc_type, &exc_value, &exc_traceback); - + exc = PyErr_GetRaisedException(); + args = PyException_GetArgs(exc); + if (!PyTuple_Check(args) || PyTuple_Size(args) != 1) { + PyErr_SetString(PyExc_AssertionError, + "TypeError args are not a one-tuple"); + goto finally; + } + message = Py_NewRef(PyTuple_GET_ITEM(args, 0)); meta_error_string = PyUnicode_FromString("metaclass conflict:"); if (meta_error_string == NULL) { goto finally; } - int res = PyUnicode_Contains(exc_value, meta_error_string); + int res = PyUnicode_Contains(message, meta_error_string); if (res < 0) { goto finally; } @@ -179,11 +185,11 @@ test_from_spec_invalid_metatype_inheritance(PyObject *self, PyObject *Py_UNUSED( Py_XDECREF(bases); Py_XDECREF(new); Py_XDECREF(meta_error_string); - Py_XDECREF(exc_type); - Py_XDECREF(exc_value); - Py_XDECREF(exc_traceback); + Py_XDECREF(exc); + Py_XDECREF(message); Py_XDECREF(class_a); Py_XDECREF(class_b); + Py_XDECREF(args); return result; } diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 8b2ce1a2cfd4bd..3c411fa0d76358 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3470,6 +3470,41 @@ function_set_kw_defaults(PyObject *self, PyObject *args) Py_RETURN_NONE; } +static PyObject * +err_set_raised(PyObject *self, PyObject *exc) +{ + Py_INCREF(exc); + PyErr_SetRaisedException(exc); + assert(PyErr_Occurred()); + return NULL; +} + +static PyObject * +err_restore(PyObject *self, PyObject *args) { + PyObject *type = NULL, *value = NULL, *traceback = NULL; + switch(PyTuple_Size(args)) { + case 3: + traceback = PyTuple_GetItem(args, 2); + Py_INCREF(traceback); + /* fall through */ + case 2: + value = PyTuple_GetItem(args, 1); + Py_INCREF(value); + /* fall through */ + case 1: + type = PyTuple_GetItem(args, 0); + Py_INCREF(type); + break; + default: + PyErr_SetString(PyExc_TypeError, + "wrong number of arguments"); + return NULL; + } + PyErr_Restore(type, value, traceback); + assert(PyErr_Occurred()); + return NULL; +} + static PyObject *test_buildvalue_issue38913(PyObject *, PyObject *); static PyMethodDef TestMethods[] = { @@ -3622,6 +3657,8 @@ static PyMethodDef TestMethods[] = { {"function_set_defaults", function_set_defaults, METH_VARARGS, NULL}, {"function_get_kw_defaults", function_get_kw_defaults, METH_O, NULL}, {"function_set_kw_defaults", function_set_kw_defaults, METH_VARARGS, NULL}, + {"err_set_raised", err_set_raised, METH_O, NULL}, + {"err_restore", err_restore, METH_VARARGS, NULL}, {NULL, NULL} /* sentinel */ }; diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 6630faa6f4471d..5879c5e11fe14a 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -2082,11 +2082,10 @@ PyGC_Collect(void) n = 0; } else { - PyObject *exc, *value, *tb; gcstate->collecting = 1; - _PyErr_Fetch(tstate, &exc, &value, &tb); + PyObject *exc = _PyErr_GetRaisedException(tstate); n = gc_collect_with_callback(tstate, NUM_GENERATIONS - 1); - _PyErr_Restore(tstate, exc, value, tb); + _PyErr_SetRaisedException(tstate, exc); gcstate->collecting = 0; } diff --git a/Objects/dictobject.c b/Objects/dictobject.c index b9067213820b52..fc658ca2f4b7f8 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -1663,15 +1663,15 @@ PyDict_GetItem(PyObject *op, PyObject *key) #endif /* Preserve the existing exception */ - PyObject *exc_type, *exc_value, *exc_tb; PyObject *value; Py_ssize_t ix; (void)ix; - _PyErr_Fetch(tstate, &exc_type, &exc_value, &exc_tb); + + PyObject *exc = _PyErr_GetRaisedException(tstate); ix = _Py_dict_lookup(mp, key, hash, &value); /* Ignore any exception raised by the lookup */ - _PyErr_Restore(tstate, exc_type, exc_value, exc_tb); + _PyErr_SetRaisedException(tstate, exc); assert(ix >= 0 || value == NULL); diff --git a/Objects/exceptions.c b/Objects/exceptions.c index db6f7d52804d6a..976f84dbf63c93 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -8,6 +8,7 @@ #include #include #include "pycore_ceval.h" // _Py_EnterRecursiveCall +#include "pycore_pyerrors.h" // struct _PyErr_SetRaisedException #include "pycore_exceptions.h" // struct _Py_exc_state #include "pycore_initconfig.h" #include "pycore_object.h" @@ -288,13 +289,17 @@ BaseException_set_tb(PyBaseExceptionObject *self, PyObject *tb, void *Py_UNUSED( PyErr_SetString(PyExc_TypeError, "__traceback__ may not be deleted"); return -1; } - else if (!(tb == Py_None || PyTraceBack_Check(tb))) { + if (PyTraceBack_Check(tb)) { + Py_XSETREF(self->traceback, Py_NewRef(tb)); + } + else if (tb == Py_None) { + Py_CLEAR(self->traceback); + } + else { PyErr_SetString(PyExc_TypeError, "__traceback__ must be a traceback or None"); return -1; } - - Py_XSETREF(self->traceback, Py_NewRef(tb)); return 0; } @@ -413,6 +418,20 @@ PyException_SetContext(PyObject *self, PyObject *context) Py_XSETREF(_PyBaseExceptionObject_cast(self)->context, context); } +PyObject * +PyException_GetArgs(PyObject *self) +{ + PyObject *args = _PyBaseExceptionObject_cast(self)->args; + return Py_NewRef(args); +} + +void +PyException_SetArgs(PyObject *self, PyObject *args) +{ + Py_INCREF(args); + Py_XSETREF(_PyBaseExceptionObject_cast(self)->args, args); +} + const char * PyExceptionClass_Name(PyObject *ob) { @@ -3188,20 +3207,19 @@ SimpleExtendsException(PyExc_Exception, ReferenceError, #define MEMERRORS_SAVE 16 +static PyBaseExceptionObject last_resort_memory_error; + static PyObject * -MemoryError_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +get_memory_error(int allow_allocation, PyObject *args, PyObject *kwds) { PyBaseExceptionObject *self; - - /* If this is a subclass of MemoryError, don't use the freelist - * and just return a fresh object */ - if (type != (PyTypeObject *) PyExc_MemoryError) { - return BaseException_new(type, args, kwds); - } - struct _Py_exc_state *state = get_exc_state(); if (state->memerrors_freelist == NULL) { - return BaseException_new(type, args, kwds); + if (!allow_allocation) { + return Py_NewRef(&last_resort_memory_error); + } + PyObject *result = BaseException_new((PyTypeObject *)PyExc_MemoryError, args, kwds); + return result; } /* Fetch object from freelist and revive it */ @@ -3221,6 +3239,35 @@ MemoryError_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return (PyObject *)self; } +static PyBaseExceptionObject last_resort_memory_error; + +static PyObject * +MemoryError_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + /* If this is a subclass of MemoryError, don't use the freelist + * and just return a fresh object */ + if (type != (PyTypeObject *) PyExc_MemoryError) { + return BaseException_new(type, args, kwds); + } + return get_memory_error(1, args, kwds); +} + +PyObject * +_PyErr_NoMemory(PyThreadState *tstate) +{ + if (Py_IS_TYPE(PyExc_MemoryError, NULL)) { + /* PyErr_NoMemory() has been called before PyExc_MemoryError has been + initialized by _PyExc_Init() */ + Py_FatalError("Out of memory and PyExc_MemoryError is not " + "initialized yet"); + } + PyObject *err = get_memory_error(0, NULL, NULL); + if (err != NULL) { + _PyErr_SetRaisedException(tstate, err); + } + return NULL; +} + static void MemoryError_dealloc(PyBaseExceptionObject *self) { @@ -3252,6 +3299,7 @@ preallocate_memerrors(void) /* We create enough MemoryErrors and then decref them, which will fill up the freelist. */ int i; + PyObject *errors[MEMERRORS_SAVE]; for (i = 0; i < MEMERRORS_SAVE; i++) { errors[i] = MemoryError_new((PyTypeObject *) PyExc_MemoryError, @@ -3291,6 +3339,9 @@ static PyTypeObject _PyExc_MemoryError = { }; PyObject *PyExc_MemoryError = (PyObject *) &_PyExc_MemoryError; +static PyBaseExceptionObject last_resort_memory_error = { + _PyObject_IMMORTAL_INIT(&_PyExc_MemoryError) +}; /* * BufferError extends Exception diff --git a/Objects/object.c b/Objects/object.c index e940222c657e3c..7817c04ef5f5be 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -2416,10 +2416,10 @@ _Py_Dealloc(PyObject *op) destructor dealloc = type->tp_dealloc; #ifdef Py_DEBUG PyThreadState *tstate = _PyThreadState_GET(); - PyObject *old_exc_type = tstate != NULL ? tstate->curexc_type : NULL; + PyObject *old_exc = tstate != NULL ? tstate->current_exception : NULL; // Keep the old exception type alive to prevent undefined behavior // on (tstate->curexc_type != old_exc_type) below - Py_XINCREF(old_exc_type); + Py_XINCREF(old_exc); // Make sure that type->tp_name remains valid Py_INCREF(type); #endif @@ -2432,12 +2432,12 @@ _Py_Dealloc(PyObject *op) #ifdef Py_DEBUG // gh-89373: The tp_dealloc function must leave the current exception // unchanged. - if (tstate != NULL && tstate->curexc_type != old_exc_type) { + if (tstate != NULL && tstate->current_exception != old_exc) { const char *err; - if (old_exc_type == NULL) { + if (old_exc == NULL) { err = "Deallocator of type '%s' raised an exception"; } - else if (tstate->curexc_type == NULL) { + else if (tstate->current_exception == NULL) { err = "Deallocator of type '%s' cleared the current exception"; } else { @@ -2448,7 +2448,7 @@ _Py_Dealloc(PyObject *op) } _Py_FatalErrorFormat(__func__, err, type->tp_name); } - Py_XDECREF(old_exc_type); + Py_XDECREF(old_exc); Py_DECREF(type); #endif } diff --git a/PC/python3dll.c b/PC/python3dll.c index 931f316bb99843..e300819365756e 100755 --- a/PC/python3dll.c +++ b/PC/python3dll.c @@ -198,6 +198,7 @@ EXPORT_FUNC(PyErr_Format) EXPORT_FUNC(PyErr_FormatV) EXPORT_FUNC(PyErr_GetExcInfo) EXPORT_FUNC(PyErr_GetHandledException) +EXPORT_FUNC(PyErr_GetRaisedException) EXPORT_FUNC(PyErr_GivenExceptionMatches) EXPORT_FUNC(PyErr_NewException) EXPORT_FUNC(PyErr_NewExceptionWithDoc) @@ -227,6 +228,7 @@ EXPORT_FUNC(PyErr_SetInterrupt) EXPORT_FUNC(PyErr_SetInterruptEx) EXPORT_FUNC(PyErr_SetNone) EXPORT_FUNC(PyErr_SetObject) +EXPORT_FUNC(PyErr_SetRaisedException) EXPORT_FUNC(PyErr_SetString) EXPORT_FUNC(PyErr_SyntaxLocation) EXPORT_FUNC(PyErr_SyntaxLocationEx) @@ -255,9 +257,11 @@ EXPORT_FUNC(PyEval_ReleaseThread) EXPORT_FUNC(PyEval_RestoreThread) EXPORT_FUNC(PyEval_SaveThread) EXPORT_FUNC(PyEval_ThreadsInitialized) +EXPORT_FUNC(PyException_GetArgs) EXPORT_FUNC(PyException_GetCause) EXPORT_FUNC(PyException_GetContext) EXPORT_FUNC(PyException_GetTraceback) +EXPORT_FUNC(PyException_SetArgs) EXPORT_FUNC(PyException_SetCause) EXPORT_FUNC(PyException_SetContext) EXPORT_FUNC(PyException_SetTraceback) diff --git a/Parser/pegen.c b/Parser/pegen.c index d84e06861edefc..94dd9de8a431c1 100644 --- a/Parser/pegen.c +++ b/Parser/pegen.c @@ -643,13 +643,10 @@ _PyPegen_number_token(Parser *p) PyThreadState *tstate = _PyThreadState_GET(); // The only way a ValueError should happen in _this_ code is via // PyLong_FromString hitting a length limit. - if (tstate->curexc_type == PyExc_ValueError && - tstate->curexc_value != NULL) { - PyObject *type, *value, *tb; - // This acts as PyErr_Clear() as we're replacing curexc. - PyErr_Fetch(&type, &value, &tb); - Py_XDECREF(tb); - Py_DECREF(type); + if (tstate->current_exception != NULL && + Py_TYPE(tstate->current_exception) == (PyTypeObject *)PyExc_ValueError + ) { + PyObject *exc = PyErr_GetRaisedException(); /* Intentionally omitting columns to avoid a wall of 1000s of '^'s * on the error message. Nobody is going to overlook their huge * numeric literal once given the line. */ @@ -659,8 +656,8 @@ _PyPegen_number_token(Parser *p) t->end_lineno, -1 /* end_col_offset */, "%S - Consider hexadecimal for huge integer literals " "to avoid decimal conversion limits.", - value); - Py_DECREF(value); + exc); + Py_DECREF(exc); } return NULL; } diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 9633f34212a68d..1169d8d172dd57 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -804,9 +804,7 @@ dummy_func( DECREF_INPUTS(); } else { - PyObject *exc_type = Py_NewRef(Py_TYPE(exc_value)); - PyObject *exc_traceback = PyException_GetTraceback(exc_value); - _PyErr_Restore(tstate, exc_type, Py_NewRef(exc_value), exc_traceback); + _PyErr_SetRaisedException(tstate, Py_NewRef(exc_value)); goto exception_unwind; } } diff --git a/Python/ceval.c b/Python/ceval.c index ecb5bf9655553e..a91f5baca8853e 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2902,13 +2902,13 @@ format_kwargs_error(PyThreadState *tstate, PyObject *func, PyObject *kwargs) } } else if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { - PyObject *exc, *val, *tb; - _PyErr_Fetch(tstate, &exc, &val, &tb); - if (val && PyTuple_Check(val) && PyTuple_GET_SIZE(val) == 1) { + PyObject *exc = _PyErr_GetRaisedException(tstate); + PyObject *args = ((PyBaseExceptionObject *)exc)->args; + if (exc && PyTuple_Check(args) && PyTuple_GET_SIZE(args) == 1) { _PyErr_Clear(tstate); PyObject *funcstr = _PyObject_FunctionStr(func); if (funcstr != NULL) { - PyObject *key = PyTuple_GET_ITEM(val, 0); + PyObject *key = PyTuple_GET_ITEM(args, 0); _PyErr_Format( tstate, PyExc_TypeError, "%U got multiple values for keyword argument '%S'", @@ -2916,11 +2916,9 @@ format_kwargs_error(PyThreadState *tstate, PyObject *func, PyObject *kwargs) Py_DECREF(funcstr); } Py_XDECREF(exc); - Py_XDECREF(val); - Py_XDECREF(tb); } else { - _PyErr_Restore(tstate, exc, val, tb); + _PyErr_SetRaisedException(tstate, exc); } } } diff --git a/Python/errors.c b/Python/errors.c index 05ef62246ec0a4..f573bed3d63ef0 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -27,32 +27,84 @@ static PyObject * _PyErr_FormatV(PyThreadState *tstate, PyObject *exception, const char *format, va_list vargs); - void -_PyErr_Restore(PyThreadState *tstate, PyObject *type, PyObject *value, - PyObject *traceback) +_PyErr_SetRaisedException(PyThreadState *tstate, PyObject *exc) { - PyObject *oldtype, *oldvalue, *oldtraceback; + PyObject *old_exc = tstate->current_exception; + tstate->current_exception = exc; + Py_XDECREF(old_exc); +} - if (traceback != NULL && !PyTraceBack_Check(traceback)) { - /* XXX Should never happen -- fatal error instead? */ - /* Well, it could be None. */ - Py_SETREF(traceback, NULL); +static PyObject* +_PyErr_CreateException(PyObject *exception_type, PyObject *value) +{ + PyObject *exc; + + if (value == NULL || value == Py_None) { + exc = _PyObject_CallNoArgs(exception_type); + } + else if (PyTuple_Check(value)) { + exc = PyObject_Call(exception_type, value, NULL); + } + else { + exc = PyObject_CallOneArg(exception_type, value); } - /* Save these in locals to safeguard against recursive - invocation through Py_XDECREF */ - oldtype = tstate->curexc_type; - oldvalue = tstate->curexc_value; - oldtraceback = tstate->curexc_traceback; + if (exc != NULL && !PyExceptionInstance_Check(exc)) { + PyErr_Format(PyExc_TypeError, + "calling %R should have returned an instance of " + "BaseException, not %s", + exception_type, Py_TYPE(exc)->tp_name); + Py_CLEAR(exc); + } - tstate->curexc_type = type; - tstate->curexc_value = value; - tstate->curexc_traceback = traceback; + return exc; +} - Py_XDECREF(oldtype); - Py_XDECREF(oldvalue); - Py_XDECREF(oldtraceback); +void +_PyErr_Restore(PyThreadState *tstate, PyObject *type, PyObject *value, + PyObject *traceback) +{ + if (type == NULL) { + assert(value == NULL); + assert(traceback == NULL); + _PyErr_SetRaisedException(tstate, NULL); + return; + } + assert(PyExceptionClass_Check(type)); + if (value != NULL && type == (PyObject *)Py_TYPE(value)) { + /* Already normalized */ + assert(((PyBaseExceptionObject *)value)->traceback != Py_None); + } + else { + PyObject *exc = _PyErr_CreateException(type, value); + Py_XDECREF(value); + if (exc == NULL) { + Py_DECREF(type); + Py_XDECREF(traceback); + return; + } + value = exc; + } + assert(PyExceptionInstance_Check(value)); + if (traceback != NULL && !PyTraceBack_Check(traceback)) { + if (traceback == Py_None) { + Py_DECREF(Py_None); + traceback = NULL; + } + else { + PyErr_SetString(PyExc_TypeError, "traceback must be a Traceback or None"); + Py_XDECREF(value); + Py_DECREF(type); + Py_XDECREF(traceback); + return; + } + } + PyObject *old_traceback = ((PyBaseExceptionObject *)value)->traceback; + ((PyBaseExceptionObject *)value)->traceback = traceback; + Py_XDECREF(old_traceback); + _PyErr_SetRaisedException(tstate, value); + Py_DECREF(type); } void @@ -62,6 +114,12 @@ PyErr_Restore(PyObject *type, PyObject *value, PyObject *traceback) _PyErr_Restore(tstate, type, value, traceback); } +void +PyErr_SetRaisedException(PyObject *exc) +{ + PyThreadState *tstate = _PyThreadState_GET(); + _PyErr_SetRaisedException(tstate, exc); +} _PyErr_StackItem * _PyErr_GetTopmostException(PyThreadState *tstate) @@ -77,32 +135,6 @@ _PyErr_GetTopmostException(PyThreadState *tstate) return exc_info; } -static PyObject* -_PyErr_CreateException(PyObject *exception_type, PyObject *value) -{ - PyObject *exc; - - if (value == NULL || value == Py_None) { - exc = _PyObject_CallNoArgs(exception_type); - } - else if (PyTuple_Check(value)) { - exc = PyObject_Call(exception_type, value, NULL); - } - else { - exc = PyObject_CallOneArg(exception_type, value); - } - - if (exc != NULL && !PyExceptionInstance_Check(exc)) { - PyErr_Format(PyExc_TypeError, - "calling %R should have returned an instance of " - "BaseException, not %s", - exception_type, Py_TYPE(exc)->tp_name); - Py_CLEAR(exc); - } - - return exc; -} - void _PyErr_SetObject(PyThreadState *tstate, PyObject *exception, PyObject *value) { @@ -117,30 +149,29 @@ _PyErr_SetObject(PyThreadState *tstate, PyObject *exception, PyObject *value) exception); return; } - Py_XINCREF(value); - exc_value = _PyErr_GetTopmostException(tstate)->exc_value; - if (exc_value != NULL && exc_value != Py_None) { - /* Implicit exception chaining */ - Py_INCREF(exc_value); - if (value == NULL || !PyExceptionInstance_Check(value)) { - /* We must normalize the value right now */ - PyObject *fixed_value; - - /* Issue #23571: functions must not be called with an - exception set */ - _PyErr_Clear(tstate); + /* Normalize the exception */ + if (value == NULL || (PyObject *)Py_TYPE(value) != exception) { + /* We must normalize the value right now */ + PyObject *fixed_value; - fixed_value = _PyErr_CreateException(exception, value); - Py_XDECREF(value); - if (fixed_value == NULL) { - Py_DECREF(exc_value); - return; - } + /* Issue #23571: functions must not be called with an + exception set */ + _PyErr_Clear(tstate); - value = fixed_value; + fixed_value = _PyErr_CreateException(exception, value); + Py_XDECREF(value); + if (fixed_value == NULL) { + return; } + value = fixed_value; + } + + exc_value = _PyErr_GetTopmostException(tstate)->exc_value; + if (exc_value != NULL && exc_value != Py_None) { + /* Implicit exception chaining */ + Py_INCREF(exc_value); /* Avoid creating new reference cycles through the context chain, while taking care not to hang on pre-existing ones. @@ -414,17 +445,34 @@ PyErr_NormalizeException(PyObject **exc, PyObject **val, PyObject **tb) } +PyObject * +_PyErr_GetRaisedException(PyThreadState *tstate) { + PyObject *exc = tstate->current_exception; + tstate->current_exception = NULL; + return exc; +} + +PyObject * +PyErr_GetRaisedException(void) +{ + PyThreadState *tstate = _PyThreadState_GET(); + return _PyErr_GetRaisedException(tstate); +} + void _PyErr_Fetch(PyThreadState *tstate, PyObject **p_type, PyObject **p_value, PyObject **p_traceback) { - *p_type = tstate->curexc_type; - *p_value = tstate->curexc_value; - *p_traceback = tstate->curexc_traceback; - - tstate->curexc_type = NULL; - tstate->curexc_value = NULL; - tstate->curexc_traceback = NULL; + PyObject *exc = _PyErr_GetRaisedException(tstate); + *p_value = exc; + if (exc == NULL) { + *p_type = NULL; + *p_traceback = NULL; + } + else { + *p_type = Py_NewRef(Py_TYPE(exc)); + *p_traceback = Py_XNewRef(((PyBaseExceptionObject *)exc)->traceback); + } } @@ -597,6 +645,28 @@ _PyErr_ChainExceptions(PyObject *typ, PyObject *val, PyObject *tb) } } +/* Like PyErr_SetRaisedException(), but if an exception is already set, + set the context associated with it. + + The caller is responsible for ensuring that this call won't create + any cycles in the exception context chain. */ +void +_PyErr_ChainExceptions1(PyObject *exc) +{ + if (exc == NULL) { + return; + } + PyThreadState *tstate = _PyThreadState_GET(); + if (_PyErr_Occurred(tstate)) { + PyObject *exc2 = _PyErr_GetRaisedException(tstate); + PyException_SetContext(exc2, exc); + _PyErr_SetRaisedException(tstate, exc2); + } + else { + _PyErr_SetRaisedException(tstate, exc); + } +} + /* Set the currently set exception's context to the given exception. If the provided exc_info is NULL, then the current Python thread state's @@ -706,19 +776,6 @@ PyErr_BadArgument(void) return 0; } -PyObject * -_PyErr_NoMemory(PyThreadState *tstate) -{ - if (Py_IS_TYPE(PyExc_MemoryError, NULL)) { - /* PyErr_NoMemory() has been called before PyExc_MemoryError has been - initialized by _PyExc_Init() */ - Py_FatalError("Out of memory and PyExc_MemoryError is not " - "initialized yet"); - } - _PyErr_SetNone(tstate, PyExc_MemoryError); - return NULL; -} - PyObject * PyErr_NoMemory(void) { diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index f38286441be4b3..09eb6893ebf6b4 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -1036,9 +1036,7 @@ Py_DECREF(exc_value); } else { - PyObject *exc_type = Py_NewRef(Py_TYPE(exc_value)); - PyObject *exc_traceback = PyException_GetTraceback(exc_value); - _PyErr_Restore(tstate, exc_type, Py_NewRef(exc_value), exc_traceback); + _PyErr_SetRaisedException(tstate, Py_NewRef(exc_value)); goto exception_unwind; } STACK_SHRINK(2); diff --git a/Python/import.c b/Python/import.c index da6c15c5fd4144..1318c09d9b3212 100644 --- a/Python/import.c +++ b/Python/import.c @@ -1592,6 +1592,13 @@ remove_importlib_frames(PyThreadState *tstate) Py_DECREF(code); tb = next; } + assert(PyExceptionInstance_Check(value)); + assert((PyObject *)Py_TYPE(value) == exception); + if (base_tb == NULL) { + base_tb = Py_None; + Py_INCREF(Py_None); + } + PyException_SetTraceback(value, base_tb); done: _PyErr_Restore(tstate, exception, value, base_tb); } diff --git a/Python/initconfig.c b/Python/initconfig.c index d7b2dc4a297425..deec805a6b1ca4 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -3143,8 +3143,7 @@ init_dump_ascii_wstr(const wchar_t *str) void _Py_DumpPathConfig(PyThreadState *tstate) { - PyObject *exc_type, *exc_value, *exc_tb; - _PyErr_Fetch(tstate, &exc_type, &exc_value, &exc_tb); + PyObject *exc = _PyErr_GetRaisedException(tstate); PySys_WriteStderr("Python path configuration:\n"); @@ -3202,5 +3201,5 @@ _Py_DumpPathConfig(PyThreadState *tstate) PySys_WriteStderr(" ]\n"); } - _PyErr_Restore(tstate, exc_type, exc_value, exc_tb); + _PyErr_SetRaisedException(tstate, exc); } diff --git a/Python/pystate.c b/Python/pystate.c index ed8c2e212a5539..1261092d1435fa 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1375,9 +1375,7 @@ PyThreadState_Clear(PyThreadState *tstate) Py_CLEAR(tstate->dict); Py_CLEAR(tstate->async_exc); - Py_CLEAR(tstate->curexc_type); - Py_CLEAR(tstate->curexc_value); - Py_CLEAR(tstate->curexc_traceback); + Py_CLEAR(tstate->current_exception); Py_CLEAR(tstate->exc_state.exc_value); diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 35292b6478a833..6a4d593768690a 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -748,13 +748,10 @@ _Py_HandleSystemExit(int *exitcode_p) } done: - /* Restore and clear the exception info, in order to properly decref - * the exception, value, and traceback. If we just exit instead, - * these leak, which confuses PYTHONDUMPREFS output, and may prevent - * some finalizers from running. - */ - PyErr_Restore(exception, value, tb); - PyErr_Clear(); + /* Cleanup the exception */ + Py_CLEAR(exception); + Py_CLEAR(value); + Py_CLEAR(tb); *exitcode_p = exitcode; return 1; } diff --git a/Python/sysmodule.c b/Python/sysmodule.c index f9f766a94d1464..6e81ef92b67f70 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -66,12 +66,11 @@ _PySys_GetAttr(PyThreadState *tstate, PyObject *name) if (sd == NULL) { return NULL; } - PyObject *exc_type, *exc_value, *exc_tb; - _PyErr_Fetch(tstate, &exc_type, &exc_value, &exc_tb); + PyObject *exc = _PyErr_GetRaisedException(tstate); /* XXX Suppress a new exception if it was raised and restore * the old one. */ PyObject *value = _PyDict_GetItemWithError(sd, name); - _PyErr_Restore(tstate, exc_type, exc_value, exc_tb); + _PyErr_SetRaisedException(tstate, exc); return value; } @@ -3704,11 +3703,10 @@ static void sys_format(PyObject *key, FILE *fp, const char *format, va_list va) { PyObject *file, *message; - PyObject *error_type, *error_value, *error_traceback; const char *utf8; PyThreadState *tstate = _PyThreadState_GET(); - _PyErr_Fetch(tstate, &error_type, &error_value, &error_traceback); + PyObject *error = _PyErr_GetRaisedException(tstate); file = _PySys_GetAttr(tstate, key); message = PyUnicode_FromFormatV(format, va); if (message != NULL) { @@ -3720,7 +3718,7 @@ sys_format(PyObject *key, FILE *fp, const char *format, va_list va) } Py_DECREF(message); } - _PyErr_Restore(tstate, error_type, error_value, error_traceback); + _PyErr_SetRaisedException(tstate, error); } void diff --git a/Python/traceback.c b/Python/traceback.c index da26c9b260a3bd..31b85e77575efa 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -249,6 +249,8 @@ PyTraceBack_Here(PyFrameObject *frame) _PyErr_ChainExceptions(exc, val, tb); return -1; } + assert(PyExceptionInstance_Check(val)); + PyException_SetTraceback(val, newtb); PyErr_Restore(exc, val, newtb); Py_XDECREF(tb); return 0; @@ -260,13 +262,12 @@ void _PyTraceback_Add(const char *funcname, const char *filename, int lineno) PyObject *globals; PyCodeObject *code; PyFrameObject *frame; - PyObject *exc, *val, *tb; PyThreadState *tstate = _PyThreadState_GET(); /* Save and clear the current exception. Python functions must not be called with an exception set. Calling Python functions happens when the codec of the filesystem encoding is implemented in pure Python. */ - _PyErr_Fetch(tstate, &exc, &val, &tb); + PyObject *exc = _PyErr_GetRaisedException(tstate); globals = PyDict_New(); if (!globals) @@ -283,13 +284,13 @@ void _PyTraceback_Add(const char *funcname, const char *filename, int lineno) goto error; frame->f_lineno = lineno; - _PyErr_Restore(tstate, exc, val, tb); + _PyErr_SetRaisedException(tstate, exc); PyTraceBack_Here(frame); Py_DECREF(frame); return; error: - _PyErr_ChainExceptions(exc, val, tb); + _PyErr_ChainExceptions1(exc); } static PyObject * From eb49d32b9af0b3b01a5588626179187f11d145c9 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Wed, 8 Feb 2023 13:13:43 +0300 Subject: [PATCH 044/247] gh-100933: Improve `check_element` helper in `test_xml_etree` (#100934) Items checked by this test are always `str` and `dict` instances. --- Lib/test/test_xml_etree.py | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py index ca5bb562996b52..11efee00582e01 100644 --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -203,25 +203,6 @@ def serialize_check(self, elem, expected): def test_interface(self): # Test element tree interface. - def check_string(string): - len(string) - for char in string: - self.assertEqual(len(char), 1, - msg="expected one-character string, got %r" % char) - new_string = string + "" - new_string = string + " " - string[:0] - - def check_mapping(mapping): - len(mapping) - keys = mapping.keys() - items = mapping.items() - for key in keys: - item = mapping[key] - mapping["key"] = "value" - self.assertEqual(mapping["key"], "value", - msg="expected value string, got %r" % mapping["key"]) - def check_element(element): self.assertTrue(ET.iselement(element), msg="not an element") direlem = dir(element) @@ -231,12 +212,12 @@ def check_element(element): self.assertIn(attr, direlem, msg='no %s visible by dir' % attr) - check_string(element.tag) - check_mapping(element.attrib) + self.assertIsInstance(element.tag, str) + self.assertIsInstance(element.attrib, dict) if element.text is not None: - check_string(element.text) + self.assertIsInstance(element.text, str) if element.tail is not None: - check_string(element.tail) + self.assertIsInstance(element.tail, str) for elem in element: check_element(elem) From 3a88de7a0af00872d9d57e1d98bc2f035cb15a1c Mon Sep 17 00:00:00 2001 From: David Hewitt <1939362+davidhewitt@users.noreply.github.com> Date: Wed, 8 Feb 2023 14:23:57 +0000 Subject: [PATCH 045/247] gh-101614: Don't treat python3_d.dll as a Python DLL when checking extension modules for incompatibility (GH-101615) --- .../2023-02-07-18-22-54.gh-issue-101614.NjVP0n.rst | 1 + Python/dynload_win.c | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2023-02-07-18-22-54.gh-issue-101614.NjVP0n.rst diff --git a/Misc/NEWS.d/next/Windows/2023-02-07-18-22-54.gh-issue-101614.NjVP0n.rst b/Misc/NEWS.d/next/Windows/2023-02-07-18-22-54.gh-issue-101614.NjVP0n.rst new file mode 100644 index 00000000000000..8ed0995d78925b --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-02-07-18-22-54.gh-issue-101614.NjVP0n.rst @@ -0,0 +1 @@ +Correctly handle extensions built against debug binaries that reference ``python3_d.dll``. diff --git a/Python/dynload_win.c b/Python/dynload_win.c index c03bc5602bffee..7bd04d573df4ad 100644 --- a/Python/dynload_win.c +++ b/Python/dynload_win.c @@ -125,14 +125,15 @@ static char *GetPythonImport (HINSTANCE hModule) !strncmp(import_name,"python",6)) { char *pch; -#ifndef _DEBUG - /* In a release version, don't claim that python3.dll is - a Python DLL. */ + /* Don't claim that python3.dll is a Python DLL. */ +#ifdef _DEBUG + if (strcmp(import_name, "python3_d.dll") == 0) { +#else if (strcmp(import_name, "python3.dll") == 0) { +#endif import_data += 20; continue; } -#endif /* Ensure python prefix is followed only by numbers to the end of the basename */ From 86ebd5c3fa9ac0fba3b651f1d4abfca79614af5f Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Wed, 8 Feb 2023 09:34:24 -0500 Subject: [PATCH 046/247] gh-101196: Make isdir/isfile/exists faster on Windows (GH-101324) Co-authored-by: Eryk Sun --- Include/pyport.h | 4 + Lib/genericpath.py | 14 +- Lib/ntpath.py | 27 +- Lib/posixpath.py | 12 - Lib/test/test_ntpath.py | 32 +- Lib/test/test_os.py | 2 + ...-01-25-11-33-54.gh-issue-101196.wAX_2g.rst | 3 + Modules/clinic/posixmodule.c.h | 254 +++++++++++++- Modules/posixmodule.c | 310 ++++++++++++++++++ 9 files changed, 624 insertions(+), 34 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2023-01-25-11-33-54.gh-issue-101196.wAX_2g.rst diff --git a/Include/pyport.h b/Include/pyport.h index 22085049a30487..40092c2f81ad48 100644 --- a/Include/pyport.h +++ b/Include/pyport.h @@ -247,6 +247,10 @@ typedef Py_ssize_t Py_ssize_clean_t; #define S_ISCHR(x) (((x) & S_IFMT) == S_IFCHR) #endif +#ifndef S_ISLNK +#define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK) +#endif + #ifdef __cplusplus /* Move this down here since some C++ #include's don't like to be included inside an extern "C" */ diff --git a/Lib/genericpath.py b/Lib/genericpath.py index ce36451a3af01c..1bd5b3897c3af9 100644 --- a/Lib/genericpath.py +++ b/Lib/genericpath.py @@ -7,7 +7,7 @@ import stat __all__ = ['commonprefix', 'exists', 'getatime', 'getctime', 'getmtime', - 'getsize', 'isdir', 'isfile', 'samefile', 'sameopenfile', + 'getsize', 'isdir', 'isfile', 'islink', 'samefile', 'sameopenfile', 'samestat'] @@ -45,6 +45,18 @@ def isdir(s): return stat.S_ISDIR(st.st_mode) +# Is a path a symbolic link? +# This will always return false on systems where os.lstat doesn't exist. + +def islink(path): + """Test whether a path is a symbolic link""" + try: + st = os.lstat(path) + except (OSError, ValueError, AttributeError): + return False + return stat.S_ISLNK(st.st_mode) + + def getsize(filename): """Return the size of a file, reported by os.stat().""" return os.stat(filename).st_size diff --git a/Lib/ntpath.py b/Lib/ntpath.py index f9ee8e02a576b7..e93a5e69600973 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -276,19 +276,6 @@ def dirname(p): """Returns the directory component of a pathname""" return split(p)[0] -# Is a path a symbolic link? -# This will always return false on systems where os.lstat doesn't exist. - -def islink(path): - """Test whether a path is a symbolic link. - This will always return false for Windows prior to 6.0. - """ - try: - st = os.lstat(path) - except (OSError, ValueError, AttributeError): - return False - return stat.S_ISLNK(st.st_mode) - # Is a path a junction? @@ -870,11 +857,13 @@ def commonpath(paths): try: - # The genericpath.isdir implementation uses os.stat and checks the mode - # attribute to tell whether or not the path is a directory. - # This is overkill on Windows - just pass the path to GetFileAttributes - # and check the attribute from there. - from nt import _isdir as isdir + # The isdir(), isfile(), islink() and exists() implementations in + # genericpath use os.stat(). This is overkill on Windows. Use simpler + # builtin functions if they are available. + from nt import _path_isdir as isdir + from nt import _path_isfile as isfile + from nt import _path_islink as islink + from nt import _path_exists as exists except ImportError: - # Use genericpath.isdir as imported above. + # Use genericpath.* as imported above pass diff --git a/Lib/posixpath.py b/Lib/posixpath.py index 32b5d6e105dde9..e4f155e41a3221 100644 --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -187,18 +187,6 @@ def dirname(p): return head -# Is a path a symbolic link? -# This will always return false on systems where os.lstat doesn't exist. - -def islink(path): - """Test whether a path is a symbolic link""" - try: - st = os.lstat(path) - except (OSError, ValueError, AttributeError): - return False - return stat.S_ISLNK(st.st_mode) - - # Is a path a junction? def isjunction(path): diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py index bce38a534a6a98..b32900697874b1 100644 --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -1,9 +1,10 @@ +import inspect import ntpath import os import sys import unittest import warnings -from test.support import os_helper +from test.support import cpython_only, os_helper from test.support import TestFailed, is_emscripten from test.support.os_helper import FakePath from test import test_genericpath @@ -938,6 +939,35 @@ def test_isjunction(self): self.assertFalse(ntpath.isjunction('tmpdir')) self.assertPathEqual(ntpath.realpath('testjunc'), ntpath.realpath('tmpdir')) + @unittest.skipIf(sys.platform != 'win32', "drive letters are a windows concept") + def test_isfile_driveletter(self): + drive = os.environ.get('SystemDrive') + if drive is None or len(drive) != 2 or drive[1] != ':': + raise unittest.SkipTest('SystemDrive is not defined or malformed') + self.assertFalse(os.path.isfile('\\\\.\\' + drive)) + + @unittest.skipIf(sys.platform != 'win32', "windows only") + def test_con_device(self): + self.assertFalse(os.path.isfile(r"\\.\CON")) + self.assertFalse(os.path.isdir(r"\\.\CON")) + self.assertFalse(os.path.islink(r"\\.\CON")) + self.assertTrue(os.path.exists(r"\\.\CON")) + + @unittest.skipIf(sys.platform != 'win32', "Fast paths are only for win32") + @cpython_only + def test_fast_paths_in_use(self): + # There are fast paths of these functions implemented in posixmodule.c. + # Confirm that they are being used, and not the Python fallbacks in + # genericpath.py. + self.assertTrue(os.path.isdir is nt._path_isdir) + self.assertFalse(inspect.isfunction(os.path.isdir)) + self.assertTrue(os.path.isfile is nt._path_isfile) + self.assertFalse(inspect.isfunction(os.path.isfile)) + self.assertTrue(os.path.islink is nt._path_islink) + self.assertFalse(inspect.isfunction(os.path.islink)) + self.assertTrue(os.path.exists is nt._path_exists) + self.assertFalse(inspect.isfunction(os.path.exists)) + class NtCommonTest(test_genericpath.CommonTest, unittest.TestCase): pathmodule = ntpath diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 58e04dd1348fd1..387d2581c06fc6 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -742,6 +742,7 @@ def test_access_denied(self): ) result = os.stat(fname) self.assertNotEqual(result.st_size, 0) + self.assertTrue(os.path.isfile(fname)) @unittest.skipUnless(sys.platform == "win32", "Win32 specific tests") def test_stat_block_device(self): @@ -2860,6 +2861,7 @@ def test_appexeclink(self): self.assertEqual(st, os.stat(alias)) self.assertFalse(stat.S_ISLNK(st.st_mode)) self.assertEqual(st.st_reparse_tag, stat.IO_REPARSE_TAG_APPEXECLINK) + self.assertTrue(os.path.isfile(alias)) # testing the first one we see is sufficient break else: diff --git a/Misc/NEWS.d/next/Windows/2023-01-25-11-33-54.gh-issue-101196.wAX_2g.rst b/Misc/NEWS.d/next/Windows/2023-01-25-11-33-54.gh-issue-101196.wAX_2g.rst new file mode 100644 index 00000000000000..c61e9b90fb5373 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-01-25-11-33-54.gh-issue-101196.wAX_2g.rst @@ -0,0 +1,3 @@ +The functions ``os.path.isdir``, ``os.path.isfile``, ``os.path.islink`` and +``os.path.exists`` are now 13% to 28% faster on Windows, by making fewer Win32 +API calls. diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index d4722cc533cbab..5e04507ddd6917 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -1794,6 +1794,242 @@ os__path_splitroot(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py #endif /* defined(MS_WINDOWS) */ +#if defined(MS_WINDOWS) + +PyDoc_STRVAR(os__path_isdir__doc__, +"_path_isdir($module, /, path)\n" +"--\n" +"\n" +"Return true if the pathname refers to an existing directory."); + +#define OS__PATH_ISDIR_METHODDEF \ + {"_path_isdir", _PyCFunction_CAST(os__path_isdir), METH_FASTCALL|METH_KEYWORDS, os__path_isdir__doc__}, + +static PyObject * +os__path_isdir_impl(PyObject *module, PyObject *path); + +static PyObject * +os__path_isdir(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(path), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"path", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "_path_isdir", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *path; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + path = args[0]; + return_value = os__path_isdir_impl(module, path); + +exit: + return return_value; +} + +#endif /* defined(MS_WINDOWS) */ + +#if defined(MS_WINDOWS) + +PyDoc_STRVAR(os__path_isfile__doc__, +"_path_isfile($module, /, path)\n" +"--\n" +"\n" +"Test whether a path is a regular file"); + +#define OS__PATH_ISFILE_METHODDEF \ + {"_path_isfile", _PyCFunction_CAST(os__path_isfile), METH_FASTCALL|METH_KEYWORDS, os__path_isfile__doc__}, + +static PyObject * +os__path_isfile_impl(PyObject *module, PyObject *path); + +static PyObject * +os__path_isfile(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(path), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"path", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "_path_isfile", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *path; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + path = args[0]; + return_value = os__path_isfile_impl(module, path); + +exit: + return return_value; +} + +#endif /* defined(MS_WINDOWS) */ + +#if defined(MS_WINDOWS) + +PyDoc_STRVAR(os__path_exists__doc__, +"_path_exists($module, /, path)\n" +"--\n" +"\n" +"Test whether a path exists. Returns False for broken symbolic links"); + +#define OS__PATH_EXISTS_METHODDEF \ + {"_path_exists", _PyCFunction_CAST(os__path_exists), METH_FASTCALL|METH_KEYWORDS, os__path_exists__doc__}, + +static PyObject * +os__path_exists_impl(PyObject *module, PyObject *path); + +static PyObject * +os__path_exists(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(path), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"path", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "_path_exists", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *path; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + path = args[0]; + return_value = os__path_exists_impl(module, path); + +exit: + return return_value; +} + +#endif /* defined(MS_WINDOWS) */ + +#if defined(MS_WINDOWS) + +PyDoc_STRVAR(os__path_islink__doc__, +"_path_islink($module, /, path)\n" +"--\n" +"\n" +"Test whether a path is a symbolic link"); + +#define OS__PATH_ISLINK_METHODDEF \ + {"_path_islink", _PyCFunction_CAST(os__path_islink), METH_FASTCALL|METH_KEYWORDS, os__path_islink__doc__}, + +static PyObject * +os__path_islink_impl(PyObject *module, PyObject *path); + +static PyObject * +os__path_islink(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(path), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"path", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "_path_islink", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *path; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + path = args[0]; + return_value = os__path_islink_impl(module, path); + +exit: + return return_value; +} + +#endif /* defined(MS_WINDOWS) */ + PyDoc_STRVAR(os__path_normpath__doc__, "_path_normpath($module, /, path)\n" "--\n" @@ -11041,6 +11277,22 @@ os_waitstatus_to_exitcode(PyObject *module, PyObject *const *args, Py_ssize_t na #define OS__PATH_SPLITROOT_METHODDEF #endif /* !defined(OS__PATH_SPLITROOT_METHODDEF) */ +#ifndef OS__PATH_ISDIR_METHODDEF + #define OS__PATH_ISDIR_METHODDEF +#endif /* !defined(OS__PATH_ISDIR_METHODDEF) */ + +#ifndef OS__PATH_ISFILE_METHODDEF + #define OS__PATH_ISFILE_METHODDEF +#endif /* !defined(OS__PATH_ISFILE_METHODDEF) */ + +#ifndef OS__PATH_EXISTS_METHODDEF + #define OS__PATH_EXISTS_METHODDEF +#endif /* !defined(OS__PATH_EXISTS_METHODDEF) */ + +#ifndef OS__PATH_ISLINK_METHODDEF + #define OS__PATH_ISLINK_METHODDEF +#endif /* !defined(OS__PATH_ISLINK_METHODDEF) */ + #ifndef OS_NICE_METHODDEF #define OS_NICE_METHODDEF #endif /* !defined(OS_NICE_METHODDEF) */ @@ -11560,4 +11812,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=41eab6c3523792a9 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=a3f76228b549e8ec input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index b84fb0d280f4e3..cba6cea48b77e1 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -4490,6 +4490,311 @@ os__path_splitroot_impl(PyObject *module, path_t *path) } +/*[clinic input] +os._path_isdir + + path: 'O' + +Return true if the pathname refers to an existing directory. + +[clinic start generated code]*/ + +static PyObject * +os__path_isdir_impl(PyObject *module, PyObject *path) +/*[clinic end generated code: output=00faea0af309669d input=b1d2571cf7291aaf]*/ +{ + HANDLE hfile; + BOOL close_file = TRUE; + FILE_BASIC_INFO info; + path_t _path = PATH_T_INITIALIZE("isdir", "path", 0, 1); + int result; + + if (!path_converter(path, &_path)) { + path_cleanup(&_path); + if (PyErr_ExceptionMatches(PyExc_ValueError)) { + PyErr_Clear(); + Py_RETURN_FALSE; + } + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + if (_path.fd != -1) { + hfile = _Py_get_osfhandle_noraise(_path.fd); + close_file = FALSE; + } + else { + hfile = CreateFileW(_path.wide, FILE_READ_ATTRIBUTES, 0, NULL, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + } + if (hfile != INVALID_HANDLE_VALUE) { + if (GetFileInformationByHandleEx(hfile, FileBasicInfo, &info, + sizeof(info))) + { + result = info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY; + } + else { + result = 0; + } + if (close_file) { + CloseHandle(hfile); + } + } + else { + STRUCT_STAT st; + switch (GetLastError()) { + case ERROR_ACCESS_DENIED: + case ERROR_SHARING_VIOLATION: + case ERROR_CANT_ACCESS_FILE: + case ERROR_INVALID_PARAMETER: + if (STAT(_path.wide, &st)) { + result = 0; + } + else { + result = S_ISDIR(st.st_mode); + } + break; + default: + result = 0; + } + } + Py_END_ALLOW_THREADS + + path_cleanup(&_path); + if (result) { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + + +/*[clinic input] +os._path_isfile + + path: 'O' + +Test whether a path is a regular file + +[clinic start generated code]*/ + +static PyObject * +os__path_isfile_impl(PyObject *module, PyObject *path) +/*[clinic end generated code: output=2394ed7c4b5cfd85 input=de22d74960ade365]*/ +{ + HANDLE hfile; + BOOL close_file = TRUE; + FILE_BASIC_INFO info; + path_t _path = PATH_T_INITIALIZE("isfile", "path", 0, 1); + int result; + + if (!path_converter(path, &_path)) { + path_cleanup(&_path); + if (PyErr_ExceptionMatches(PyExc_ValueError)) { + PyErr_Clear(); + Py_RETURN_FALSE; + } + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + if (_path.fd != -1) { + hfile = _Py_get_osfhandle_noraise(_path.fd); + close_file = FALSE; + } + else { + hfile = CreateFileW(_path.wide, FILE_READ_ATTRIBUTES, 0, NULL, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + } + if (hfile != INVALID_HANDLE_VALUE) { + if (GetFileInformationByHandleEx(hfile, FileBasicInfo, &info, + sizeof(info))) + { + result = !(info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY); + } + else { + result = 0; + } + if (close_file) { + CloseHandle(hfile); + } + } + else { + STRUCT_STAT st; + switch (GetLastError()) { + case ERROR_ACCESS_DENIED: + case ERROR_SHARING_VIOLATION: + case ERROR_CANT_ACCESS_FILE: + case ERROR_INVALID_PARAMETER: + if (STAT(_path.wide, &st)) { + result = 0; + } + else { + result = S_ISREG(st.st_mode); + } + break; + default: + result = 0; + } + } + Py_END_ALLOW_THREADS + + path_cleanup(&_path); + if (result) { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + + +/*[clinic input] +os._path_exists + + path: 'O' + +Test whether a path exists. Returns False for broken symbolic links + +[clinic start generated code]*/ + +static PyObject * +os__path_exists_impl(PyObject *module, PyObject *path) +/*[clinic end generated code: output=f508c3b35e13a249 input=380f77cdfa0f7ae8]*/ +{ + HANDLE hfile; + BOOL close_file = TRUE; + path_t _path = PATH_T_INITIALIZE("exists", "path", 0, 1); + int result; + + if (!path_converter(path, &_path)) { + path_cleanup(&_path); + if (PyErr_ExceptionMatches(PyExc_ValueError)) { + PyErr_Clear(); + Py_RETURN_FALSE; + } + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + if (_path.fd != -1) { + hfile = _Py_get_osfhandle_noraise(_path.fd); + close_file = FALSE; + } + else { + hfile = CreateFileW(_path.wide, FILE_READ_ATTRIBUTES, 0, NULL, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + } + if (hfile != INVALID_HANDLE_VALUE) { + result = 1; + if (close_file) { + CloseHandle(hfile); + } + } + else { + STRUCT_STAT st; + switch (GetLastError()) { + case ERROR_ACCESS_DENIED: + case ERROR_SHARING_VIOLATION: + case ERROR_CANT_ACCESS_FILE: + case ERROR_INVALID_PARAMETER: + if (STAT(_path.wide, &st)) { + result = 0; + } + else { + result = 1; + } + break; + default: + result = 0; + } + } + Py_END_ALLOW_THREADS + + path_cleanup(&_path); + if (result) { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + + +/*[clinic input] +os._path_islink + + path: 'O' + +Test whether a path is a symbolic link + +[clinic start generated code]*/ + +static PyObject * +os__path_islink_impl(PyObject *module, PyObject *path) +/*[clinic end generated code: output=6d8640b1a390c054 input=38a3cb937ccf59bf]*/ +{ + HANDLE hfile; + BOOL close_file = TRUE; + FILE_ATTRIBUTE_TAG_INFO info; + path_t _path = PATH_T_INITIALIZE("islink", "path", 0, 1); + int result; + + if (!path_converter(path, &_path)) { + path_cleanup(&_path); + if (PyErr_ExceptionMatches(PyExc_ValueError)) { + PyErr_Clear(); + Py_RETURN_FALSE; + } + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + if (_path.fd != -1) { + hfile = _Py_get_osfhandle_noraise(_path.fd); + close_file = FALSE; + } + else { + hfile = CreateFileW(_path.wide, FILE_READ_ATTRIBUTES, 0, NULL, + OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, + NULL); + } + if (hfile != INVALID_HANDLE_VALUE) { + if (GetFileInformationByHandleEx(hfile, FileAttributeTagInfo, &info, + sizeof(info))) + { + result = (info.ReparseTag == IO_REPARSE_TAG_SYMLINK); + } + else { + result = 0; + } + if (close_file) { + CloseHandle(hfile); + } + } + else { + STRUCT_STAT st; + switch (GetLastError()) { + case ERROR_ACCESS_DENIED: + case ERROR_SHARING_VIOLATION: + case ERROR_CANT_ACCESS_FILE: + case ERROR_INVALID_PARAMETER: + if (LSTAT(_path.wide, &st)) { + result = 0; + } + else { + result = S_ISLNK(st.st_mode); + } + break; + default: + result = 0; + } + } + Py_END_ALLOW_THREADS + + path_cleanup(&_path); + if (result) { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + #endif /* MS_WINDOWS */ @@ -15150,6 +15455,11 @@ static PyMethodDef posix_methods[] = { OS_WAITSTATUS_TO_EXITCODE_METHODDEF OS_SETNS_METHODDEF OS_UNSHARE_METHODDEF + + OS__PATH_ISDIR_METHODDEF + OS__PATH_ISFILE_METHODDEF + OS__PATH_ISLINK_METHODDEF + OS__PATH_EXISTS_METHODDEF {NULL, NULL} /* Sentinel */ }; From 35dd55005ee9aea2843eff7f514ee689a0995df8 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Wed, 8 Feb 2023 18:49:04 +0300 Subject: [PATCH 047/247] gh-101670: typo fix in PyImport_AppendInittab() (GH-101672) --- Python/import.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/import.c b/Python/import.c index 1318c09d9b3212..795d368966481e 100644 --- a/Python/import.c +++ b/Python/import.c @@ -2699,7 +2699,7 @@ PyImport_AppendInittab(const char *name, PyObject* (*initfunc)(void)) struct _inittab newtab[2]; if (_PyRuntime.imports.inittab != NULL) { - Py_FatalError("PyImport_AppendInittab() may be be called after Py_Initialize()"); + Py_FatalError("PyImport_AppendInittab() may not be called after Py_Initialize()"); } memset(newtab, '\0', sizeof newtab); From 2a8bf2580441147f1a15e61229d669abc0ab86ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Wed, 8 Feb 2023 17:50:43 +0100 Subject: [PATCH 048/247] gh-100221: Fix creating dirs in `make sharedinstall` (GH-100329) Fix creating install directories in `make sharedinstall` if they exist already outside `DESTDIR`. The previous make rules assumed that the directories would be created via a dependency on a rule for `$(DESTSHARED)` that did not fire if the directory did exist outside `$(DESTDIR)`. While technically `$(DESTDIR)` could be prepended to the rule name, moving the rules for creating directories straight into the `sharedinstall` rule seems to fit the common practices better. Since the rule explicitly checks whether the individual directories exist anyway, there seems to be no reason to rely on make determining that implicitly as well. --- Makefile.pre.in | 21 ++++++++----------- ...-12-18-08-33-28.gh-issue-100221.K94Ct3.rst | 2 ++ 2 files changed, 11 insertions(+), 12 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2022-12-18-08-33-28.gh-issue-100221.K94Ct3.rst diff --git a/Makefile.pre.in b/Makefile.pre.in index 3641c4eeebeee3..2559df8e74952c 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1816,7 +1816,15 @@ commoninstall: check-clean-src @FRAMEWORKALTINSTALLFIRST@ \ # Install shared libraries enabled by Setup DESTDIRS= $(exec_prefix) $(LIBDIR) $(BINLIBDEST) $(DESTSHARED) -sharedinstall: $(DESTSHARED) all +sharedinstall: all + @for i in $(DESTDIRS); \ + do \ + if test ! -d $(DESTDIR)$$i; then \ + echo "Creating directory $$i"; \ + $(INSTALL) -d -m $(DIRMODE) $(DESTDIR)$$i; \ + else true; \ + fi; \ + done @for i in X $(SHAREDMODS); do \ if test $$i != X; then \ echo $(INSTALL_SHARED) $$i $(DESTSHARED)/`basename $$i`; \ @@ -1828,17 +1836,6 @@ sharedinstall: $(DESTSHARED) all fi; \ done - -$(DESTSHARED): - @for i in $(DESTDIRS); \ - do \ - if test ! -d $(DESTDIR)$$i; then \ - echo "Creating directory $$i"; \ - $(INSTALL) -d -m $(DIRMODE) $(DESTDIR)$$i; \ - else true; \ - fi; \ - done - # Install the interpreter with $(VERSION) affixed # This goes into $(exec_prefix) altbininstall: $(BUILDPYTHON) @FRAMEWORKPYTHONW@ diff --git a/Misc/NEWS.d/next/Build/2022-12-18-08-33-28.gh-issue-100221.K94Ct3.rst b/Misc/NEWS.d/next/Build/2022-12-18-08-33-28.gh-issue-100221.K94Ct3.rst new file mode 100644 index 00000000000000..27c948330cfc17 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2022-12-18-08-33-28.gh-issue-100221.K94Ct3.rst @@ -0,0 +1,2 @@ +Fix creating install directories in ``make sharedinstall`` if they exist +outside ``DESTDIR`` already. From d9de0792482d2ded364b0c7d2867b97a5da41b12 Mon Sep 17 00:00:00 2001 From: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> Date: Wed, 8 Feb 2023 23:32:15 +0530 Subject: [PATCH 049/247] GH-101696: invalidate type version tag in `_PyStaticType_Dealloc` (#101697) --- .../2023-02-08-17-13-31.gh-issue-101696.seJhTt.rst | 1 + Objects/typeobject.c | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-08-17-13-31.gh-issue-101696.seJhTt.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-08-17-13-31.gh-issue-101696.seJhTt.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-08-17-13-31.gh-issue-101696.seJhTt.rst new file mode 100644 index 00000000000000..ff2bbb4b564252 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-08-17-13-31.gh-issue-101696.seJhTt.rst @@ -0,0 +1 @@ +Invalidate type version tag in ``_PyStaticType_Dealloc`` for static types, avoiding bug where a false cache hit could crash the interpreter. Patch by Kumar Aditya. diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 59e0bf2995bac2..bf6ccdb77a90f0 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -4469,6 +4469,8 @@ _PyStaticType_Dealloc(PyTypeObject *type) } type->tp_flags &= ~Py_TPFLAGS_READY; + type->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG; + type->tp_version_tag = 0; if (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { _PyStaticType_ClearWeakRefs(type); From 616aec1ff148ba4570aa2d4b8ea420c715c206e4 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 8 Feb 2023 11:40:10 -0800 Subject: [PATCH 050/247] gh-98831: Modernize CALL and family (#101508) Includes a slight improvement to `DECREF_INPUTS()`. --- Python/bytecodes.c | 532 +++++++++++----------- Python/ceval.c | 6 - Python/ceval_macros.h | 3 + Python/generated_cases.c.h | 560 ++++++++++++++---------- Python/opcode_metadata.h | 112 ++--- Tools/cases_generator/generate_cases.py | 21 +- 6 files changed, 646 insertions(+), 588 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 1169d8d172dd57..2b9f12fefa14e9 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2360,70 +2360,87 @@ dummy_func( assert(oparg & 1); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_BOUND_METHOD_EXACT_ARGS) { - DEOPT_IF(is_method(stack_pointer, oparg), CALL); - PyObject *function = PEEK(oparg + 1); - DEOPT_IF(Py_TYPE(function) != &PyMethod_Type, CALL); - STAT_INC(CALL, hit); - PyObject *self = ((PyMethodObject *)function)->im_self; - PEEK(oparg + 1) = Py_NewRef(self); - PyObject *meth = ((PyMethodObject *)function)->im_func; - PEEK(oparg + 2) = Py_NewRef(meth); - Py_DECREF(function); - GO_TO_INSTRUCTION(CALL_PY_EXACT_ARGS); - } - inst(KW_NAMES, (--)) { assert(kwnames == NULL); assert(oparg < PyTuple_GET_SIZE(consts)); kwnames = GETITEM(consts, oparg); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL) { + // Cache layout: counter/1, func_version/2, min_args/1 + // Neither CALL_INTRINSIC_1 nor CALL_FUNCTION_EX are members! + family(call, INLINE_CACHE_ENTRIES_CALL) = { + CALL, + CALL_BOUND_METHOD_EXACT_ARGS, + CALL_PY_EXACT_ARGS, + CALL_PY_WITH_DEFAULTS, + CALL_NO_KW_TYPE_1, + CALL_NO_KW_STR_1, + CALL_NO_KW_TUPLE_1, + CALL_BUILTIN_CLASS, + CALL_NO_KW_BUILTIN_O, + CALL_NO_KW_BUILTIN_FAST, + CALL_BUILTIN_FAST_WITH_KEYWORDS, + CALL_NO_KW_LEN, + CALL_NO_KW_ISINSTANCE, + CALL_NO_KW_LIST_APPEND, + CALL_NO_KW_METHOD_DESCRIPTOR_O, + CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, + CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS, + CALL_NO_KW_METHOD_DESCRIPTOR_FAST, + }; + + // On entry, the stack is either + // [NULL, callable, arg1, arg2, ...] + // or + // [method, self, arg1, arg2, ...] + // (Some args may be keywords, see KW_NAMES, which sets 'kwnames'.) + // On exit, the stack is [result]. + // When calling Python, inline the call using DISPATCH_INLINED(). + inst(CALL, (unused/1, unused/2, unused/1, method, callable, args[oparg] -- res)) { + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } #if ENABLE_SPECIALIZATION _PyCallCache *cache = (_PyCallCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); - int is_meth = is_method(stack_pointer, oparg); - int nargs = oparg + is_meth; - PyObject *callable = PEEK(nargs + 1); next_instr--; - _Py_Specialize_Call(callable, next_instr, nargs, kwnames); + _Py_Specialize_Call(callable, next_instr, total_args, kwnames); DISPATCH_SAME_OPARG(); } STAT_INC(CALL, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); #endif /* ENABLE_SPECIALIZATION */ - int total_args, is_meth; - is_meth = is_method(stack_pointer, oparg); - PyObject *function = PEEK(oparg + 1); - if (!is_meth && Py_TYPE(function) == &PyMethod_Type) { - PyObject *self = ((PyMethodObject *)function)->im_self; - PEEK(oparg+1) = Py_NewRef(self); - PyObject *meth = ((PyMethodObject *)function)->im_func; - PEEK(oparg+2) = Py_NewRef(meth); - Py_DECREF(function); - is_meth = 1; - } - total_args = oparg + is_meth; - function = PEEK(total_args + 1); + if (!is_meth && Py_TYPE(callable) == &PyMethod_Type) { + is_meth = 1; // For consistenct; it's dead, though + args--; + total_args++; + PyObject *self = ((PyMethodObject *)callable)->im_self; + args[0] = Py_NewRef(self); + method = ((PyMethodObject *)callable)->im_func; + args[-1] = Py_NewRef(method); + Py_DECREF(callable); + callable = method; + } int positional_args = total_args - KWNAMES_LEN(); // Check if the call can be inlined or not - if (Py_TYPE(function) == &PyFunction_Type && + if (Py_TYPE(callable) == &PyFunction_Type && tstate->interp->eval_frame == NULL && - ((PyFunctionObject *)function)->vectorcall == _PyFunction_Vectorcall) + ((PyFunctionObject *)callable)->vectorcall == _PyFunction_Vectorcall) { - int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(function))->co_flags; - PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(function)); - STACK_SHRINK(total_args); + int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable))->co_flags; + PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable)); _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit( - tstate, (PyFunctionObject *)function, locals, - stack_pointer, positional_args, kwnames + tstate, (PyFunctionObject *)callable, locals, + args, positional_args, kwnames ); kwnames = NULL; - STACK_SHRINK(2-is_meth); + // Manipulate stack directly since we leave using DISPATCH_INLINED(). + STACK_SHRINK(oparg + 2); // The frame has stolen all the arguments from the stack, // so there is no need to clean them up. if (new_frame == NULL) { @@ -2433,190 +2450,180 @@ dummy_func( DISPATCH_INLINED(new_frame); } /* Callable is not a normal Python function */ - PyObject *res; if (cframe.use_tracing) { res = trace_call_function( - tstate, function, stack_pointer-total_args, + tstate, callable, args, positional_args, kwnames); } else { res = PyObject_Vectorcall( - function, stack_pointer-total_args, + callable, args, positional_args | PY_VECTORCALL_ARGUMENTS_OFFSET, kwnames); } kwnames = NULL; assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - Py_DECREF(function); - /* Clear the stack */ - STACK_SHRINK(total_args); + Py_DECREF(callable); for (int i = 0; i < total_args; i++) { - Py_DECREF(stack_pointer[i]); + Py_DECREF(args[i]); } - STACK_SHRINK(2-is_meth); - PUSH(res); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + ERROR_IF(res == NULL, error); CHECK_EVAL_BREAKER(); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_PY_EXACT_ARGS) { + // Start out with [NULL, bound_method, arg1, arg2, ...] + // Transform to [callable, self, arg1, arg2, ...] + // Then fall through to CALL_PY_EXACT_ARGS + inst(CALL_BOUND_METHOD_EXACT_ARGS, (unused/1, unused/2, unused/1, method, callable, unused[oparg] -- unused)) { + DEOPT_IF(method != NULL, CALL); + DEOPT_IF(Py_TYPE(callable) != &PyMethod_Type, CALL); + STAT_INC(CALL, hit); + PyObject *self = ((PyMethodObject *)callable)->im_self; + PEEK(oparg + 1) = Py_NewRef(self); // callable + PyObject *meth = ((PyMethodObject *)callable)->im_func; + PEEK(oparg + 2) = Py_NewRef(meth); // method + Py_DECREF(callable); + GO_TO_INSTRUCTION(CALL_PY_EXACT_ARGS); + } + + inst(CALL_PY_EXACT_ARGS, (unused/1, func_version/2, unused/1, method, callable, args[oparg] -- unused)) { assert(kwnames == NULL); DEOPT_IF(tstate->interp->eval_frame, CALL); - _PyCallCache *cache = (_PyCallCache *)next_instr; - int is_meth = is_method(stack_pointer, oparg); - int argcount = oparg + is_meth; - PyObject *callable = PEEK(argcount + 1); + int is_meth = method != NULL; + int argcount = oparg; + if (is_meth) { + callable = method; + args--; + argcount++; + } DEOPT_IF(!PyFunction_Check(callable), CALL); PyFunctionObject *func = (PyFunctionObject *)callable; - DEOPT_IF(func->func_version != read_u32(cache->func_version), CALL); + DEOPT_IF(func->func_version != func_version, CALL); PyCodeObject *code = (PyCodeObject *)func->func_code; DEOPT_IF(code->co_argcount != argcount, CALL); DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL); STAT_INC(CALL, hit); _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, func, argcount); - STACK_SHRINK(argcount); for (int i = 0; i < argcount; i++) { - new_frame->localsplus[i] = stack_pointer[i]; + new_frame->localsplus[i] = args[i]; } - STACK_SHRINK(2-is_meth); + // Manipulate stack directly since we leave using DISPATCH_INLINED(). + STACK_SHRINK(oparg + 2); JUMPBY(INLINE_CACHE_ENTRIES_CALL); DISPATCH_INLINED(new_frame); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_PY_WITH_DEFAULTS) { + inst(CALL_PY_WITH_DEFAULTS, (unused/1, func_version/2, min_args/1, method, callable, args[oparg] -- unused)) { assert(kwnames == NULL); DEOPT_IF(tstate->interp->eval_frame, CALL); - _PyCallCache *cache = (_PyCallCache *)next_instr; - int is_meth = is_method(stack_pointer, oparg); - int argcount = oparg + is_meth; - PyObject *callable = PEEK(argcount + 1); + int is_meth = method != NULL; + int argcount = oparg; + if (is_meth) { + callable = method; + args--; + argcount++; + } DEOPT_IF(!PyFunction_Check(callable), CALL); PyFunctionObject *func = (PyFunctionObject *)callable; - DEOPT_IF(func->func_version != read_u32(cache->func_version), CALL); + DEOPT_IF(func->func_version != func_version, CALL); PyCodeObject *code = (PyCodeObject *)func->func_code; DEOPT_IF(argcount > code->co_argcount, CALL); - int minargs = cache->min_args; - DEOPT_IF(argcount < minargs, CALL); + DEOPT_IF(argcount < min_args, CALL); DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL); STAT_INC(CALL, hit); _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, func, code->co_argcount); - STACK_SHRINK(argcount); for (int i = 0; i < argcount; i++) { - new_frame->localsplus[i] = stack_pointer[i]; + new_frame->localsplus[i] = args[i]; } for (int i = argcount; i < code->co_argcount; i++) { - PyObject *def = PyTuple_GET_ITEM(func->func_defaults, - i - minargs); + PyObject *def = PyTuple_GET_ITEM(func->func_defaults, i - min_args); new_frame->localsplus[i] = Py_NewRef(def); } - STACK_SHRINK(2-is_meth); + // Manipulate stack and cache directly since we leave using DISPATCH_INLINED(). + STACK_SHRINK(oparg + 2); JUMPBY(INLINE_CACHE_ENTRIES_CALL); DISPATCH_INLINED(new_frame); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_NO_KW_TYPE_1) { + inst(CALL_NO_KW_TYPE_1, (unused/1, unused/2, unused/1, null, callable, args[oparg] -- res)) { assert(kwnames == NULL); assert(cframe.use_tracing == 0); assert(oparg == 1); - DEOPT_IF(is_method(stack_pointer, 1), CALL); - PyObject *obj = TOP(); - PyObject *callable = SECOND(); + DEOPT_IF(null != NULL, CALL); + PyObject *obj = args[0]; DEOPT_IF(callable != (PyObject *)&PyType_Type, CALL); STAT_INC(CALL, hit); - JUMPBY(INLINE_CACHE_ENTRIES_CALL); - PyObject *res = Py_NewRef(Py_TYPE(obj)); - Py_DECREF(callable); + res = Py_NewRef(Py_TYPE(obj)); Py_DECREF(obj); - STACK_SHRINK(2); - SET_TOP(res); + Py_DECREF(&PyType_Type); // I.e., callable } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_NO_KW_STR_1) { + inst(CALL_NO_KW_STR_1, (unused/1, unused/2, unused/1, null, callable, args[oparg] -- res)) { assert(kwnames == NULL); assert(cframe.use_tracing == 0); assert(oparg == 1); - DEOPT_IF(is_method(stack_pointer, 1), CALL); - PyObject *callable = PEEK(2); + DEOPT_IF(null != NULL, CALL); DEOPT_IF(callable != (PyObject *)&PyUnicode_Type, CALL); STAT_INC(CALL, hit); - PyObject *arg = TOP(); - PyObject *res = PyObject_Str(arg); + PyObject *arg = args[0]; + res = PyObject_Str(arg); Py_DECREF(arg); - Py_DECREF(&PyUnicode_Type); - STACK_SHRINK(2); - SET_TOP(res); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + Py_DECREF(&PyUnicode_Type); // I.e., callable + ERROR_IF(res == NULL, error); CHECK_EVAL_BREAKER(); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_NO_KW_TUPLE_1) { + inst(CALL_NO_KW_TUPLE_1, (unused/1, unused/2, unused/1, null, callable, args[oparg] -- res)) { assert(kwnames == NULL); assert(oparg == 1); - DEOPT_IF(is_method(stack_pointer, 1), CALL); - PyObject *callable = PEEK(2); + DEOPT_IF(null != NULL, CALL); DEOPT_IF(callable != (PyObject *)&PyTuple_Type, CALL); STAT_INC(CALL, hit); - PyObject *arg = TOP(); - PyObject *res = PySequence_Tuple(arg); + PyObject *arg = args[0]; + res = PySequence_Tuple(arg); Py_DECREF(arg); - Py_DECREF(&PyTuple_Type); - STACK_SHRINK(2); - SET_TOP(res); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + Py_DECREF(&PyTuple_Type); // I.e., tuple + ERROR_IF(res == NULL, error); CHECK_EVAL_BREAKER(); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_BUILTIN_CLASS) { - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; + inst(CALL_BUILTIN_CLASS, (unused/1, unused/2, unused/1, method, callable, args[oparg] -- res)) { + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } int kwnames_len = KWNAMES_LEN(); - PyObject *callable = PEEK(total_args + 1); DEOPT_IF(!PyType_Check(callable), CALL); PyTypeObject *tp = (PyTypeObject *)callable; DEOPT_IF(tp->tp_vectorcall == NULL, CALL); STAT_INC(CALL, hit); - STACK_SHRINK(total_args); - PyObject *res = tp->tp_vectorcall((PyObject *)tp, stack_pointer, - total_args-kwnames_len, kwnames); + res = tp->tp_vectorcall((PyObject *)tp, args, + total_args - kwnames_len, kwnames); kwnames = NULL; /* Free the arguments. */ for (int i = 0; i < total_args; i++) { - Py_DECREF(stack_pointer[i]); + Py_DECREF(args[i]); } Py_DECREF(tp); - STACK_SHRINK(1-is_meth); - SET_TOP(res); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + ERROR_IF(res == NULL, error); CHECK_EVAL_BREAKER(); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_NO_KW_BUILTIN_O) { + inst(CALL_NO_KW_BUILTIN_O, (unused/1, unused/2, unused/1, method, callable, args[oparg] -- res)) { assert(cframe.use_tracing == 0); /* Builtin METH_O functions */ assert(kwnames == NULL); - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } DEOPT_IF(total_args != 1, CALL); - PyObject *callable = PEEK(total_args + 1); DEOPT_IF(!PyCFunction_CheckExact(callable), CALL); DEOPT_IF(PyCFunction_GET_FLAGS(callable) != METH_O, CALL); STAT_INC(CALL, hit); @@ -2626,81 +2633,74 @@ dummy_func( if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { goto error; } - PyObject *arg = TOP(); - PyObject *res = _PyCFunction_TrampolineCall(cfunc, PyCFunction_GET_SELF(callable), arg); + PyObject *arg = args[0]; + res = _PyCFunction_TrampolineCall(cfunc, PyCFunction_GET_SELF(callable), arg); _Py_LeaveRecursiveCallTstate(tstate); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); Py_DECREF(arg); Py_DECREF(callable); - STACK_SHRINK(2-is_meth); - SET_TOP(res); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + ERROR_IF(res == NULL, error); CHECK_EVAL_BREAKER(); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_NO_KW_BUILTIN_FAST) { + inst(CALL_NO_KW_BUILTIN_FAST, (unused/1, unused/2, unused/1, method, callable, args[oparg] -- res)) { assert(cframe.use_tracing == 0); /* Builtin METH_FASTCALL functions, without keywords */ assert(kwnames == NULL); - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; - PyObject *callable = PEEK(total_args + 1); + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } DEOPT_IF(!PyCFunction_CheckExact(callable), CALL); - DEOPT_IF(PyCFunction_GET_FLAGS(callable) != METH_FASTCALL, - CALL); + DEOPT_IF(PyCFunction_GET_FLAGS(callable) != METH_FASTCALL, CALL); STAT_INC(CALL, hit); PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable); - STACK_SHRINK(total_args); /* res = func(self, args, nargs) */ - PyObject *res = ((_PyCFunctionFast)(void(*)(void))cfunc)( + res = ((_PyCFunctionFast)(void(*)(void))cfunc)( PyCFunction_GET_SELF(callable), - stack_pointer, + args, total_args); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); /* Free the arguments. */ for (int i = 0; i < total_args; i++) { - Py_DECREF(stack_pointer[i]); + Py_DECREF(args[i]); } - STACK_SHRINK(2-is_meth); - PUSH(res); Py_DECREF(callable); - if (res == NULL) { + ERROR_IF(res == NULL, error); /* Not deopting because this doesn't mean our optimization was wrong. `res` can be NULL for valid reasons. Eg. getattr(x, 'invalid'). In those cases an exception is set, so we must handle it. */ - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); CHECK_EVAL_BREAKER(); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_BUILTIN_FAST_WITH_KEYWORDS) { + inst(CALL_BUILTIN_FAST_WITH_KEYWORDS, (unused/1, unused/2, unused/1, method, callable, args[oparg] -- res)) { assert(cframe.use_tracing == 0); /* Builtin METH_FASTCALL | METH_KEYWORDS functions */ - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; - PyObject *callable = PEEK(total_args + 1); + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } DEOPT_IF(!PyCFunction_CheckExact(callable), CALL); DEOPT_IF(PyCFunction_GET_FLAGS(callable) != (METH_FASTCALL | METH_KEYWORDS), CALL); STAT_INC(CALL, hit); - STACK_SHRINK(total_args); /* res = func(self, args, nargs, kwnames) */ _PyCFunctionFastWithKeywords cfunc = (_PyCFunctionFastWithKeywords)(void(*)(void)) PyCFunction_GET_FUNCTION(callable); - PyObject *res = cfunc( + res = cfunc( PyCFunction_GET_SELF(callable), - stack_pointer, + args, total_args - KWNAMES_LEN(), kwnames ); @@ -2709,117 +2709,109 @@ dummy_func( /* Free the arguments. */ for (int i = 0; i < total_args; i++) { - Py_DECREF(stack_pointer[i]); + Py_DECREF(args[i]); } - STACK_SHRINK(2-is_meth); - PUSH(res); Py_DECREF(callable); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + ERROR_IF(res == NULL, error); CHECK_EVAL_BREAKER(); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_NO_KW_LEN) { + inst(CALL_NO_KW_LEN, (unused/1, unused/2, unused/1, method, callable, args[oparg] -- res)) { assert(cframe.use_tracing == 0); assert(kwnames == NULL); /* len(o) */ - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } DEOPT_IF(total_args != 1, CALL); - PyObject *callable = PEEK(total_args + 1); PyInterpreterState *interp = _PyInterpreterState_GET(); DEOPT_IF(callable != interp->callable_cache.len, CALL); STAT_INC(CALL, hit); - PyObject *arg = TOP(); + PyObject *arg = args[0]; Py_ssize_t len_i = PyObject_Length(arg); if (len_i < 0) { goto error; } - PyObject *res = PyLong_FromSsize_t(len_i); + res = PyLong_FromSsize_t(len_i); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - STACK_SHRINK(2-is_meth); - SET_TOP(res); Py_DECREF(callable); Py_DECREF(arg); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + ERROR_IF(res == NULL, error); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_NO_KW_ISINSTANCE) { + inst(CALL_NO_KW_ISINSTANCE, (unused/1, unused/2, unused/1, method, callable, args[oparg] -- res)) { assert(cframe.use_tracing == 0); assert(kwnames == NULL); /* isinstance(o, o2) */ - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; - PyObject *callable = PEEK(total_args + 1); + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } DEOPT_IF(total_args != 2, CALL); PyInterpreterState *interp = _PyInterpreterState_GET(); DEOPT_IF(callable != interp->callable_cache.isinstance, CALL); STAT_INC(CALL, hit); - PyObject *cls = POP(); - PyObject *inst = TOP(); + PyObject *cls = args[1]; + PyObject *inst = args[0]; int retval = PyObject_IsInstance(inst, cls); if (retval < 0) { - Py_DECREF(cls); goto error; } - PyObject *res = PyBool_FromLong(retval); + res = PyBool_FromLong(retval); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - STACK_SHRINK(2-is_meth); - SET_TOP(res); Py_DECREF(inst); Py_DECREF(cls); Py_DECREF(callable); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + ERROR_IF(res == NULL, error); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_NO_KW_LIST_APPEND) { + // This is secretly a super-instruction + inst(CALL_NO_KW_LIST_APPEND, (unused/1, unused/2, unused/1, method, self, args[oparg] -- unused)) { assert(cframe.use_tracing == 0); assert(kwnames == NULL); assert(oparg == 1); - PyObject *callable = PEEK(3); + assert(method != NULL); PyInterpreterState *interp = _PyInterpreterState_GET(); - DEOPT_IF(callable != interp->callable_cache.list_append, CALL); - PyObject *list = SECOND(); - DEOPT_IF(!PyList_Check(list), CALL); + DEOPT_IF(method != interp->callable_cache.list_append, CALL); + DEOPT_IF(!PyList_Check(self), CALL); STAT_INC(CALL, hit); - PyObject *arg = POP(); - if (_PyList_AppendTakeRef((PyListObject *)list, arg) < 0) { - goto error; + if (_PyList_AppendTakeRef((PyListObject *)self, args[0]) < 0) { + goto pop_1_error; // Since arg is DECREF'ed already } - STACK_SHRINK(2); - Py_DECREF(list); - Py_DECREF(callable); + Py_DECREF(self); + Py_DECREF(method); + STACK_SHRINK(3); // CALL + POP_TOP JUMPBY(INLINE_CACHE_ENTRIES_CALL + 1); assert(_Py_OPCODE(next_instr[-1]) == POP_TOP); + DISPATCH(); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_NO_KW_METHOD_DESCRIPTOR_O) { + inst(CALL_NO_KW_METHOD_DESCRIPTOR_O, (unused/1, unused/2, unused/1, method, unused, args[oparg] -- res)) { assert(kwnames == NULL); - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + args--; + total_args++; + } PyMethodDescrObject *callable = (PyMethodDescrObject *)PEEK(total_args + 1); DEOPT_IF(total_args != 2, CALL); DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL); PyMethodDef *meth = callable->d_method; DEOPT_IF(meth->ml_flags != METH_O, CALL); - PyObject *arg = TOP(); - PyObject *self = SECOND(); + PyObject *arg = args[1]; + PyObject *self = args[0]; DEOPT_IF(!Py_IS_TYPE(self, callable->d_common.d_type), CALL); STAT_INC(CALL, hit); PyCFunction cfunc = meth->ml_meth; @@ -2828,69 +2820,62 @@ dummy_func( if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { goto error; } - PyObject *res = _PyCFunction_TrampolineCall(cfunc, self, arg); + res = _PyCFunction_TrampolineCall(cfunc, self, arg); _Py_LeaveRecursiveCallTstate(tstate); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); Py_DECREF(self); Py_DECREF(arg); - STACK_SHRINK(oparg + 1); - SET_TOP(res); Py_DECREF(callable); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + ERROR_IF(res == NULL, error); CHECK_EVAL_BREAKER(); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS) { - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; + inst(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, (unused/1, unused/2, unused/1, method, unused, args[oparg] -- res)) { + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + args--; + total_args++; + } PyMethodDescrObject *callable = (PyMethodDescrObject *)PEEK(total_args + 1); DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL); PyMethodDef *meth = callable->d_method; DEOPT_IF(meth->ml_flags != (METH_FASTCALL|METH_KEYWORDS), CALL); PyTypeObject *d_type = callable->d_common.d_type; - PyObject *self = PEEK(total_args); + PyObject *self = args[0]; DEOPT_IF(!Py_IS_TYPE(self, d_type), CALL); STAT_INC(CALL, hit); - int nargs = total_args-1; - STACK_SHRINK(nargs); + int nargs = total_args - 1; _PyCFunctionFastWithKeywords cfunc = (_PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth; - PyObject *res = cfunc(self, stack_pointer, nargs - KWNAMES_LEN(), - kwnames); + res = cfunc(self, args + 1, nargs - KWNAMES_LEN(), kwnames); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); kwnames = NULL; /* Free the arguments. */ - for (int i = 0; i < nargs; i++) { - Py_DECREF(stack_pointer[i]); + for (int i = 0; i < total_args; i++) { + Py_DECREF(args[i]); } - Py_DECREF(self); - STACK_SHRINK(2-is_meth); - SET_TOP(res); Py_DECREF(callable); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + ERROR_IF(res == NULL, error); CHECK_EVAL_BREAKER(); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS) { + inst(CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS, (unused/1, unused/2, unused/1, method, unused, args[oparg] -- res)) { assert(kwnames == NULL); assert(oparg == 0 || oparg == 1); - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + args--; + total_args++; + } DEOPT_IF(total_args != 1, CALL); PyMethodDescrObject *callable = (PyMethodDescrObject *)SECOND(); DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL); PyMethodDef *meth = callable->d_method; - PyObject *self = TOP(); + PyObject *self = args[0]; DEOPT_IF(!Py_IS_TYPE(self, callable->d_common.d_type), CALL); DEOPT_IF(meth->ml_flags != METH_NOARGS, CALL); STAT_INC(CALL, hit); @@ -2900,52 +2885,43 @@ dummy_func( if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { goto error; } - PyObject *res = _PyCFunction_TrampolineCall(cfunc, self, NULL); + res = _PyCFunction_TrampolineCall(cfunc, self, NULL); _Py_LeaveRecursiveCallTstate(tstate); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); Py_DECREF(self); - STACK_SHRINK(oparg + 1); - SET_TOP(res); Py_DECREF(callable); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + ERROR_IF(res == NULL, error); CHECK_EVAL_BREAKER(); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_NO_KW_METHOD_DESCRIPTOR_FAST) { + inst(CALL_NO_KW_METHOD_DESCRIPTOR_FAST, (unused/1, unused/2, unused/1, method, unused, args[oparg] -- res)) { assert(kwnames == NULL); - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + args--; + total_args++; + } PyMethodDescrObject *callable = (PyMethodDescrObject *)PEEK(total_args + 1); /* Builtin METH_FASTCALL methods, without keywords */ DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL); PyMethodDef *meth = callable->d_method; DEOPT_IF(meth->ml_flags != METH_FASTCALL, CALL); - PyObject *self = PEEK(total_args); + PyObject *self = args[0]; DEOPT_IF(!Py_IS_TYPE(self, callable->d_common.d_type), CALL); STAT_INC(CALL, hit); _PyCFunctionFast cfunc = (_PyCFunctionFast)(void(*)(void))meth->ml_meth; - int nargs = total_args-1; - STACK_SHRINK(nargs); - PyObject *res = cfunc(self, stack_pointer, nargs); + int nargs = total_args - 1; + res = cfunc(self, args + 1, nargs); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); /* Clear the stack of the arguments. */ - for (int i = 0; i < nargs; i++) { - Py_DECREF(stack_pointer[i]); + for (int i = 0; i < total_args; i++) { + Py_DECREF(args[i]); } - Py_DECREF(self); - STACK_SHRINK(2-is_meth); - SET_TOP(res); Py_DECREF(callable); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + ERROR_IF(res == NULL, error); CHECK_EVAL_BREAKER(); } @@ -3153,12 +3129,4 @@ dummy_func( // Future families go below this point // -family(call, INLINE_CACHE_ENTRIES_CALL) = { - CALL, CALL_PY_EXACT_ARGS, - CALL_PY_WITH_DEFAULTS, CALL_BOUND_METHOD_EXACT_ARGS, CALL_BUILTIN_CLASS, - CALL_BUILTIN_FAST_WITH_KEYWORDS, CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, CALL_NO_KW_BUILTIN_FAST, - CALL_NO_KW_BUILTIN_O, CALL_NO_KW_ISINSTANCE, CALL_NO_KW_LEN, - CALL_NO_KW_LIST_APPEND, CALL_NO_KW_METHOD_DESCRIPTOR_FAST, CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS, - CALL_NO_KW_METHOD_DESCRIPTOR_O, CALL_NO_KW_STR_1, CALL_NO_KW_TUPLE_1, - CALL_NO_KW_TYPE_1 }; family(store_fast) = { STORE_FAST, STORE_FAST__LOAD_FAST, STORE_FAST__STORE_FAST }; diff --git a/Python/ceval.c b/Python/ceval.c index a91f5baca8853e..611d62b0eba9af 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -688,12 +688,6 @@ static inline void _Py_LeaveRecursiveCallPy(PyThreadState *tstate) { } -// GH-89279: Must be a macro to be sure it's inlined by MSVC. -#define is_method(stack_pointer, args) (PEEK((args)+2) != NULL) - -#define KWNAMES_LEN() \ - (kwnames == NULL ? 0 : ((int)PyTuple_GET_SIZE(kwnames))) - /* Disable unused label warnings. They are handy for debugging, even if computed gotos aren't used. */ diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index d7a8f0beeec872..691bf8e1caae95 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -347,3 +347,6 @@ GETITEM(PyObject *v, Py_ssize_t i) { } while (0); #define NAME_ERROR_MSG "name '%.200s' is not defined" + +#define KWNAMES_LEN() \ + (kwnames == NULL ? 0 : ((int)PyTuple_GET_SIZE(kwnames))) diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 09eb6893ebf6b4..a224d4eb892785 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2994,19 +2994,6 @@ DISPATCH(); } - TARGET(CALL_BOUND_METHOD_EXACT_ARGS) { - DEOPT_IF(is_method(stack_pointer, oparg), CALL); - PyObject *function = PEEK(oparg + 1); - DEOPT_IF(Py_TYPE(function) != &PyMethod_Type, CALL); - STAT_INC(CALL, hit); - PyObject *self = ((PyMethodObject *)function)->im_self; - PEEK(oparg + 1) = Py_NewRef(self); - PyObject *meth = ((PyMethodObject *)function)->im_func; - PEEK(oparg + 2) = Py_NewRef(meth); - Py_DECREF(function); - GO_TO_INSTRUCTION(CALL_PY_EXACT_ARGS); - } - TARGET(KW_NAMES) { assert(kwnames == NULL); assert(oparg < PyTuple_GET_SIZE(consts)); @@ -3016,48 +3003,55 @@ TARGET(CALL) { PREDICTED(CALL); + static_assert(INLINE_CACHE_ENTRIES_CALL == 4, "incorrect cache size"); + PyObject **args = &PEEK(oparg); + PyObject *callable = PEEK(1 + oparg); + PyObject *method = PEEK(2 + oparg); + PyObject *res; + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } #if ENABLE_SPECIALIZATION _PyCallCache *cache = (_PyCallCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); - int is_meth = is_method(stack_pointer, oparg); - int nargs = oparg + is_meth; - PyObject *callable = PEEK(nargs + 1); next_instr--; - _Py_Specialize_Call(callable, next_instr, nargs, kwnames); + _Py_Specialize_Call(callable, next_instr, total_args, kwnames); DISPATCH_SAME_OPARG(); } STAT_INC(CALL, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); #endif /* ENABLE_SPECIALIZATION */ - int total_args, is_meth; - is_meth = is_method(stack_pointer, oparg); - PyObject *function = PEEK(oparg + 1); - if (!is_meth && Py_TYPE(function) == &PyMethod_Type) { - PyObject *self = ((PyMethodObject *)function)->im_self; - PEEK(oparg+1) = Py_NewRef(self); - PyObject *meth = ((PyMethodObject *)function)->im_func; - PEEK(oparg+2) = Py_NewRef(meth); - Py_DECREF(function); - is_meth = 1; - } - total_args = oparg + is_meth; - function = PEEK(total_args + 1); + if (!is_meth && Py_TYPE(callable) == &PyMethod_Type) { + is_meth = 1; // For consistenct; it's dead, though + args--; + total_args++; + PyObject *self = ((PyMethodObject *)callable)->im_self; + args[0] = Py_NewRef(self); + method = ((PyMethodObject *)callable)->im_func; + args[-1] = Py_NewRef(method); + Py_DECREF(callable); + callable = method; + } int positional_args = total_args - KWNAMES_LEN(); // Check if the call can be inlined or not - if (Py_TYPE(function) == &PyFunction_Type && + if (Py_TYPE(callable) == &PyFunction_Type && tstate->interp->eval_frame == NULL && - ((PyFunctionObject *)function)->vectorcall == _PyFunction_Vectorcall) + ((PyFunctionObject *)callable)->vectorcall == _PyFunction_Vectorcall) { - int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(function))->co_flags; - PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(function)); - STACK_SHRINK(total_args); + int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable))->co_flags; + PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable)); _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit( - tstate, (PyFunctionObject *)function, locals, - stack_pointer, positional_args, kwnames + tstate, (PyFunctionObject *)callable, locals, + args, positional_args, kwnames ); kwnames = NULL; - STACK_SHRINK(2-is_meth); + // Manipulate stack directly since we leave using DISPATCH_INLINED(). + STACK_SHRINK(oparg + 2); // The frame has stolen all the arguments from the stack, // so there is no need to clean them up. if (new_frame == NULL) { @@ -3067,189 +3061,234 @@ DISPATCH_INLINED(new_frame); } /* Callable is not a normal Python function */ - PyObject *res; if (cframe.use_tracing) { res = trace_call_function( - tstate, function, stack_pointer-total_args, + tstate, callable, args, positional_args, kwnames); } else { res = PyObject_Vectorcall( - function, stack_pointer-total_args, + callable, args, positional_args | PY_VECTORCALL_ARGUMENTS_OFFSET, kwnames); } kwnames = NULL; assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - Py_DECREF(function); - /* Clear the stack */ - STACK_SHRINK(total_args); + Py_DECREF(callable); for (int i = 0; i < total_args; i++) { - Py_DECREF(stack_pointer[i]); + Py_DECREF(args[i]); } - STACK_SHRINK(2-is_meth); - PUSH(res); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + POKE(1, res); + JUMPBY(4); CHECK_EVAL_BREAKER(); DISPATCH(); } + TARGET(CALL_BOUND_METHOD_EXACT_ARGS) { + PyObject *callable = PEEK(1 + oparg); + PyObject *method = PEEK(2 + oparg); + DEOPT_IF(method != NULL, CALL); + DEOPT_IF(Py_TYPE(callable) != &PyMethod_Type, CALL); + STAT_INC(CALL, hit); + PyObject *self = ((PyMethodObject *)callable)->im_self; + PEEK(oparg + 1) = Py_NewRef(self); // callable + PyObject *meth = ((PyMethodObject *)callable)->im_func; + PEEK(oparg + 2) = Py_NewRef(meth); // method + Py_DECREF(callable); + GO_TO_INSTRUCTION(CALL_PY_EXACT_ARGS); + } + TARGET(CALL_PY_EXACT_ARGS) { PREDICTED(CALL_PY_EXACT_ARGS); + PyObject **args = &PEEK(oparg); + PyObject *callable = PEEK(1 + oparg); + PyObject *method = PEEK(2 + oparg); + uint32_t func_version = read_u32(&next_instr[1].cache); assert(kwnames == NULL); DEOPT_IF(tstate->interp->eval_frame, CALL); - _PyCallCache *cache = (_PyCallCache *)next_instr; - int is_meth = is_method(stack_pointer, oparg); - int argcount = oparg + is_meth; - PyObject *callable = PEEK(argcount + 1); + int is_meth = method != NULL; + int argcount = oparg; + if (is_meth) { + callable = method; + args--; + argcount++; + } DEOPT_IF(!PyFunction_Check(callable), CALL); PyFunctionObject *func = (PyFunctionObject *)callable; - DEOPT_IF(func->func_version != read_u32(cache->func_version), CALL); + DEOPT_IF(func->func_version != func_version, CALL); PyCodeObject *code = (PyCodeObject *)func->func_code; DEOPT_IF(code->co_argcount != argcount, CALL); DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL); STAT_INC(CALL, hit); _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, func, argcount); - STACK_SHRINK(argcount); for (int i = 0; i < argcount; i++) { - new_frame->localsplus[i] = stack_pointer[i]; + new_frame->localsplus[i] = args[i]; } - STACK_SHRINK(2-is_meth); + // Manipulate stack directly since we leave using DISPATCH_INLINED(). + STACK_SHRINK(oparg + 2); JUMPBY(INLINE_CACHE_ENTRIES_CALL); DISPATCH_INLINED(new_frame); } TARGET(CALL_PY_WITH_DEFAULTS) { + PyObject **args = &PEEK(oparg); + PyObject *callable = PEEK(1 + oparg); + PyObject *method = PEEK(2 + oparg); + uint32_t func_version = read_u32(&next_instr[1].cache); + uint16_t min_args = read_u16(&next_instr[3].cache); assert(kwnames == NULL); DEOPT_IF(tstate->interp->eval_frame, CALL); - _PyCallCache *cache = (_PyCallCache *)next_instr; - int is_meth = is_method(stack_pointer, oparg); - int argcount = oparg + is_meth; - PyObject *callable = PEEK(argcount + 1); + int is_meth = method != NULL; + int argcount = oparg; + if (is_meth) { + callable = method; + args--; + argcount++; + } DEOPT_IF(!PyFunction_Check(callable), CALL); PyFunctionObject *func = (PyFunctionObject *)callable; - DEOPT_IF(func->func_version != read_u32(cache->func_version), CALL); + DEOPT_IF(func->func_version != func_version, CALL); PyCodeObject *code = (PyCodeObject *)func->func_code; DEOPT_IF(argcount > code->co_argcount, CALL); - int minargs = cache->min_args; - DEOPT_IF(argcount < minargs, CALL); + DEOPT_IF(argcount < min_args, CALL); DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL); STAT_INC(CALL, hit); _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, func, code->co_argcount); - STACK_SHRINK(argcount); for (int i = 0; i < argcount; i++) { - new_frame->localsplus[i] = stack_pointer[i]; + new_frame->localsplus[i] = args[i]; } for (int i = argcount; i < code->co_argcount; i++) { - PyObject *def = PyTuple_GET_ITEM(func->func_defaults, - i - minargs); + PyObject *def = PyTuple_GET_ITEM(func->func_defaults, i - min_args); new_frame->localsplus[i] = Py_NewRef(def); } - STACK_SHRINK(2-is_meth); + // Manipulate stack and cache directly since we leave using DISPATCH_INLINED(). + STACK_SHRINK(oparg + 2); JUMPBY(INLINE_CACHE_ENTRIES_CALL); DISPATCH_INLINED(new_frame); } TARGET(CALL_NO_KW_TYPE_1) { + PyObject **args = &PEEK(oparg); + PyObject *callable = PEEK(1 + oparg); + PyObject *null = PEEK(2 + oparg); + PyObject *res; assert(kwnames == NULL); assert(cframe.use_tracing == 0); assert(oparg == 1); - DEOPT_IF(is_method(stack_pointer, 1), CALL); - PyObject *obj = TOP(); - PyObject *callable = SECOND(); + DEOPT_IF(null != NULL, CALL); + PyObject *obj = args[0]; DEOPT_IF(callable != (PyObject *)&PyType_Type, CALL); STAT_INC(CALL, hit); - JUMPBY(INLINE_CACHE_ENTRIES_CALL); - PyObject *res = Py_NewRef(Py_TYPE(obj)); - Py_DECREF(callable); + res = Py_NewRef(Py_TYPE(obj)); Py_DECREF(obj); - STACK_SHRINK(2); - SET_TOP(res); + Py_DECREF(&PyType_Type); // I.e., callable + STACK_SHRINK(oparg); + STACK_SHRINK(1); + POKE(1, res); + JUMPBY(4); DISPATCH(); } TARGET(CALL_NO_KW_STR_1) { + PyObject **args = &PEEK(oparg); + PyObject *callable = PEEK(1 + oparg); + PyObject *null = PEEK(2 + oparg); + PyObject *res; assert(kwnames == NULL); assert(cframe.use_tracing == 0); assert(oparg == 1); - DEOPT_IF(is_method(stack_pointer, 1), CALL); - PyObject *callable = PEEK(2); + DEOPT_IF(null != NULL, CALL); DEOPT_IF(callable != (PyObject *)&PyUnicode_Type, CALL); STAT_INC(CALL, hit); - PyObject *arg = TOP(); - PyObject *res = PyObject_Str(arg); + PyObject *arg = args[0]; + res = PyObject_Str(arg); Py_DECREF(arg); - Py_DECREF(&PyUnicode_Type); - STACK_SHRINK(2); - SET_TOP(res); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + Py_DECREF(&PyUnicode_Type); // I.e., callable + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + POKE(1, res); + JUMPBY(4); CHECK_EVAL_BREAKER(); DISPATCH(); } TARGET(CALL_NO_KW_TUPLE_1) { + PyObject **args = &PEEK(oparg); + PyObject *callable = PEEK(1 + oparg); + PyObject *null = PEEK(2 + oparg); + PyObject *res; assert(kwnames == NULL); assert(oparg == 1); - DEOPT_IF(is_method(stack_pointer, 1), CALL); - PyObject *callable = PEEK(2); + DEOPT_IF(null != NULL, CALL); DEOPT_IF(callable != (PyObject *)&PyTuple_Type, CALL); STAT_INC(CALL, hit); - PyObject *arg = TOP(); - PyObject *res = PySequence_Tuple(arg); + PyObject *arg = args[0]; + res = PySequence_Tuple(arg); Py_DECREF(arg); - Py_DECREF(&PyTuple_Type); - STACK_SHRINK(2); - SET_TOP(res); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + Py_DECREF(&PyTuple_Type); // I.e., tuple + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + POKE(1, res); + JUMPBY(4); CHECK_EVAL_BREAKER(); DISPATCH(); } TARGET(CALL_BUILTIN_CLASS) { - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; + PyObject **args = &PEEK(oparg); + PyObject *callable = PEEK(1 + oparg); + PyObject *method = PEEK(2 + oparg); + PyObject *res; + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } int kwnames_len = KWNAMES_LEN(); - PyObject *callable = PEEK(total_args + 1); DEOPT_IF(!PyType_Check(callable), CALL); PyTypeObject *tp = (PyTypeObject *)callable; DEOPT_IF(tp->tp_vectorcall == NULL, CALL); STAT_INC(CALL, hit); - STACK_SHRINK(total_args); - PyObject *res = tp->tp_vectorcall((PyObject *)tp, stack_pointer, - total_args-kwnames_len, kwnames); + res = tp->tp_vectorcall((PyObject *)tp, args, + total_args - kwnames_len, kwnames); kwnames = NULL; /* Free the arguments. */ for (int i = 0; i < total_args; i++) { - Py_DECREF(stack_pointer[i]); + Py_DECREF(args[i]); } Py_DECREF(tp); - STACK_SHRINK(1-is_meth); - SET_TOP(res); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + POKE(1, res); + JUMPBY(4); CHECK_EVAL_BREAKER(); DISPATCH(); } TARGET(CALL_NO_KW_BUILTIN_O) { + PyObject **args = &PEEK(oparg); + PyObject *callable = PEEK(1 + oparg); + PyObject *method = PEEK(2 + oparg); + PyObject *res; assert(cframe.use_tracing == 0); /* Builtin METH_O functions */ assert(kwnames == NULL); - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } DEOPT_IF(total_args != 1, CALL); - PyObject *callable = PEEK(total_args + 1); DEOPT_IF(!PyCFunction_CheckExact(callable), CALL); DEOPT_IF(PyCFunction_GET_FLAGS(callable) != METH_O, CALL); STAT_INC(CALL, hit); @@ -3259,81 +3298,92 @@ if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { goto error; } - PyObject *arg = TOP(); - PyObject *res = _PyCFunction_TrampolineCall(cfunc, PyCFunction_GET_SELF(callable), arg); + PyObject *arg = args[0]; + res = _PyCFunction_TrampolineCall(cfunc, PyCFunction_GET_SELF(callable), arg); _Py_LeaveRecursiveCallTstate(tstate); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); Py_DECREF(arg); Py_DECREF(callable); - STACK_SHRINK(2-is_meth); - SET_TOP(res); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + POKE(1, res); + JUMPBY(4); CHECK_EVAL_BREAKER(); DISPATCH(); } TARGET(CALL_NO_KW_BUILTIN_FAST) { + PyObject **args = &PEEK(oparg); + PyObject *callable = PEEK(1 + oparg); + PyObject *method = PEEK(2 + oparg); + PyObject *res; assert(cframe.use_tracing == 0); /* Builtin METH_FASTCALL functions, without keywords */ assert(kwnames == NULL); - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; - PyObject *callable = PEEK(total_args + 1); + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } DEOPT_IF(!PyCFunction_CheckExact(callable), CALL); - DEOPT_IF(PyCFunction_GET_FLAGS(callable) != METH_FASTCALL, - CALL); + DEOPT_IF(PyCFunction_GET_FLAGS(callable) != METH_FASTCALL, CALL); STAT_INC(CALL, hit); PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable); - STACK_SHRINK(total_args); /* res = func(self, args, nargs) */ - PyObject *res = ((_PyCFunctionFast)(void(*)(void))cfunc)( + res = ((_PyCFunctionFast)(void(*)(void))cfunc)( PyCFunction_GET_SELF(callable), - stack_pointer, + args, total_args); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); /* Free the arguments. */ for (int i = 0; i < total_args; i++) { - Py_DECREF(stack_pointer[i]); + Py_DECREF(args[i]); } - STACK_SHRINK(2-is_meth); - PUSH(res); Py_DECREF(callable); - if (res == NULL) { + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } /* Not deopting because this doesn't mean our optimization was wrong. `res` can be NULL for valid reasons. Eg. getattr(x, 'invalid'). In those cases an exception is set, so we must handle it. */ - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + STACK_SHRINK(oparg); + STACK_SHRINK(1); + POKE(1, res); + JUMPBY(4); CHECK_EVAL_BREAKER(); DISPATCH(); } TARGET(CALL_BUILTIN_FAST_WITH_KEYWORDS) { + PyObject **args = &PEEK(oparg); + PyObject *callable = PEEK(1 + oparg); + PyObject *method = PEEK(2 + oparg); + PyObject *res; assert(cframe.use_tracing == 0); /* Builtin METH_FASTCALL | METH_KEYWORDS functions */ - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; - PyObject *callable = PEEK(total_args + 1); + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } DEOPT_IF(!PyCFunction_CheckExact(callable), CALL); DEOPT_IF(PyCFunction_GET_FLAGS(callable) != (METH_FASTCALL | METH_KEYWORDS), CALL); STAT_INC(CALL, hit); - STACK_SHRINK(total_args); /* res = func(self, args, nargs, kwnames) */ _PyCFunctionFastWithKeywords cfunc = (_PyCFunctionFastWithKeywords)(void(*)(void)) PyCFunction_GET_FUNCTION(callable); - PyObject *res = cfunc( + res = cfunc( PyCFunction_GET_SELF(callable), - stack_pointer, + args, total_args - KWNAMES_LEN(), kwnames ); @@ -3342,99 +3392,112 @@ /* Free the arguments. */ for (int i = 0; i < total_args; i++) { - Py_DECREF(stack_pointer[i]); + Py_DECREF(args[i]); } - STACK_SHRINK(2-is_meth); - PUSH(res); Py_DECREF(callable); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + POKE(1, res); + JUMPBY(4); CHECK_EVAL_BREAKER(); DISPATCH(); } TARGET(CALL_NO_KW_LEN) { + PyObject **args = &PEEK(oparg); + PyObject *callable = PEEK(1 + oparg); + PyObject *method = PEEK(2 + oparg); + PyObject *res; assert(cframe.use_tracing == 0); assert(kwnames == NULL); /* len(o) */ - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } DEOPT_IF(total_args != 1, CALL); - PyObject *callable = PEEK(total_args + 1); PyInterpreterState *interp = _PyInterpreterState_GET(); DEOPT_IF(callable != interp->callable_cache.len, CALL); STAT_INC(CALL, hit); - PyObject *arg = TOP(); + PyObject *arg = args[0]; Py_ssize_t len_i = PyObject_Length(arg); if (len_i < 0) { goto error; } - PyObject *res = PyLong_FromSsize_t(len_i); + res = PyLong_FromSsize_t(len_i); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - STACK_SHRINK(2-is_meth); - SET_TOP(res); Py_DECREF(callable); Py_DECREF(arg); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + POKE(1, res); + JUMPBY(4); DISPATCH(); } TARGET(CALL_NO_KW_ISINSTANCE) { + PyObject **args = &PEEK(oparg); + PyObject *callable = PEEK(1 + oparg); + PyObject *method = PEEK(2 + oparg); + PyObject *res; assert(cframe.use_tracing == 0); assert(kwnames == NULL); /* isinstance(o, o2) */ - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; - PyObject *callable = PEEK(total_args + 1); + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } DEOPT_IF(total_args != 2, CALL); PyInterpreterState *interp = _PyInterpreterState_GET(); DEOPT_IF(callable != interp->callable_cache.isinstance, CALL); STAT_INC(CALL, hit); - PyObject *cls = POP(); - PyObject *inst = TOP(); + PyObject *cls = args[1]; + PyObject *inst = args[0]; int retval = PyObject_IsInstance(inst, cls); if (retval < 0) { - Py_DECREF(cls); goto error; } - PyObject *res = PyBool_FromLong(retval); + res = PyBool_FromLong(retval); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - STACK_SHRINK(2-is_meth); - SET_TOP(res); Py_DECREF(inst); Py_DECREF(cls); Py_DECREF(callable); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + POKE(1, res); + JUMPBY(4); DISPATCH(); } TARGET(CALL_NO_KW_LIST_APPEND) { + PyObject **args = &PEEK(oparg); + PyObject *self = PEEK(1 + oparg); + PyObject *method = PEEK(2 + oparg); assert(cframe.use_tracing == 0); assert(kwnames == NULL); assert(oparg == 1); - PyObject *callable = PEEK(3); + assert(method != NULL); PyInterpreterState *interp = _PyInterpreterState_GET(); - DEOPT_IF(callable != interp->callable_cache.list_append, CALL); - PyObject *list = SECOND(); - DEOPT_IF(!PyList_Check(list), CALL); + DEOPT_IF(method != interp->callable_cache.list_append, CALL); + DEOPT_IF(!PyList_Check(self), CALL); STAT_INC(CALL, hit); - PyObject *arg = POP(); - if (_PyList_AppendTakeRef((PyListObject *)list, arg) < 0) { - goto error; + if (_PyList_AppendTakeRef((PyListObject *)self, args[0]) < 0) { + goto pop_1_error; // Since arg is DECREF'ed already } - STACK_SHRINK(2); - Py_DECREF(list); - Py_DECREF(callable); + Py_DECREF(self); + Py_DECREF(method); + STACK_SHRINK(3); // CALL + POP_TOP JUMPBY(INLINE_CACHE_ENTRIES_CALL + 1); assert(_Py_OPCODE(next_instr[-1]) == POP_TOP); @@ -3442,17 +3505,24 @@ } TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_O) { + PyObject **args = &PEEK(oparg); + PyObject *method = PEEK(2 + oparg); + PyObject *res; assert(kwnames == NULL); - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + args--; + total_args++; + } PyMethodDescrObject *callable = (PyMethodDescrObject *)PEEK(total_args + 1); DEOPT_IF(total_args != 2, CALL); DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL); PyMethodDef *meth = callable->d_method; DEOPT_IF(meth->ml_flags != METH_O, CALL); - PyObject *arg = TOP(); - PyObject *self = SECOND(); + PyObject *arg = args[1]; + PyObject *self = args[0]; DEOPT_IF(!Py_IS_TYPE(self, callable->d_common.d_type), CALL); STAT_INC(CALL, hit); PyCFunction cfunc = meth->ml_meth; @@ -3461,69 +3531,78 @@ if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { goto error; } - PyObject *res = _PyCFunction_TrampolineCall(cfunc, self, arg); + res = _PyCFunction_TrampolineCall(cfunc, self, arg); _Py_LeaveRecursiveCallTstate(tstate); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); Py_DECREF(self); Py_DECREF(arg); - STACK_SHRINK(oparg + 1); - SET_TOP(res); Py_DECREF(callable); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + POKE(1, res); + JUMPBY(4); CHECK_EVAL_BREAKER(); DISPATCH(); } TARGET(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS) { - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; + PyObject **args = &PEEK(oparg); + PyObject *method = PEEK(2 + oparg); + PyObject *res; + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + args--; + total_args++; + } PyMethodDescrObject *callable = (PyMethodDescrObject *)PEEK(total_args + 1); DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL); PyMethodDef *meth = callable->d_method; DEOPT_IF(meth->ml_flags != (METH_FASTCALL|METH_KEYWORDS), CALL); PyTypeObject *d_type = callable->d_common.d_type; - PyObject *self = PEEK(total_args); + PyObject *self = args[0]; DEOPT_IF(!Py_IS_TYPE(self, d_type), CALL); STAT_INC(CALL, hit); - int nargs = total_args-1; - STACK_SHRINK(nargs); + int nargs = total_args - 1; _PyCFunctionFastWithKeywords cfunc = (_PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth; - PyObject *res = cfunc(self, stack_pointer, nargs - KWNAMES_LEN(), - kwnames); + res = cfunc(self, args + 1, nargs - KWNAMES_LEN(), kwnames); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); kwnames = NULL; /* Free the arguments. */ - for (int i = 0; i < nargs; i++) { - Py_DECREF(stack_pointer[i]); + for (int i = 0; i < total_args; i++) { + Py_DECREF(args[i]); } - Py_DECREF(self); - STACK_SHRINK(2-is_meth); - SET_TOP(res); Py_DECREF(callable); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + POKE(1, res); + JUMPBY(4); CHECK_EVAL_BREAKER(); DISPATCH(); } TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS) { + PyObject **args = &PEEK(oparg); + PyObject *method = PEEK(2 + oparg); + PyObject *res; assert(kwnames == NULL); assert(oparg == 0 || oparg == 1); - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + args--; + total_args++; + } DEOPT_IF(total_args != 1, CALL); PyMethodDescrObject *callable = (PyMethodDescrObject *)SECOND(); DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL); PyMethodDef *meth = callable->d_method; - PyObject *self = TOP(); + PyObject *self = args[0]; DEOPT_IF(!Py_IS_TYPE(self, callable->d_common.d_type), CALL); DEOPT_IF(meth->ml_flags != METH_NOARGS, CALL); STAT_INC(CALL, hit); @@ -3533,52 +3612,55 @@ if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { goto error; } - PyObject *res = _PyCFunction_TrampolineCall(cfunc, self, NULL); + res = _PyCFunction_TrampolineCall(cfunc, self, NULL); _Py_LeaveRecursiveCallTstate(tstate); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); Py_DECREF(self); - STACK_SHRINK(oparg + 1); - SET_TOP(res); Py_DECREF(callable); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + POKE(1, res); + JUMPBY(4); CHECK_EVAL_BREAKER(); DISPATCH(); } TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_FAST) { + PyObject **args = &PEEK(oparg); + PyObject *method = PEEK(2 + oparg); + PyObject *res; assert(kwnames == NULL); - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + args--; + total_args++; + } PyMethodDescrObject *callable = (PyMethodDescrObject *)PEEK(total_args + 1); /* Builtin METH_FASTCALL methods, without keywords */ DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL); PyMethodDef *meth = callable->d_method; DEOPT_IF(meth->ml_flags != METH_FASTCALL, CALL); - PyObject *self = PEEK(total_args); + PyObject *self = args[0]; DEOPT_IF(!Py_IS_TYPE(self, callable->d_common.d_type), CALL); STAT_INC(CALL, hit); _PyCFunctionFast cfunc = (_PyCFunctionFast)(void(*)(void))meth->ml_meth; - int nargs = total_args-1; - STACK_SHRINK(nargs); - PyObject *res = cfunc(self, stack_pointer, nargs); + int nargs = total_args - 1; + res = cfunc(self, args + 1, nargs); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); /* Clear the stack of the arguments. */ - for (int i = 0; i < nargs; i++) { - Py_DECREF(stack_pointer[i]); + for (int i = 0; i < total_args; i++) { + Py_DECREF(args[i]); } - Py_DECREF(self); - STACK_SHRINK(2-is_meth); - SET_TOP(res); Py_DECREF(callable); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + POKE(1, res); + JUMPBY(4); CHECK_EVAL_BREAKER(); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index 054ef6c2998234..98791043f55271 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -286,44 +286,44 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { return 1; case LOAD_ATTR_METHOD_LAZY_DICT: return 1; - case CALL_BOUND_METHOD_EXACT_ARGS: - return -1; case KW_NAMES: return 0; case CALL: - return -1; + return oparg + 2; + case CALL_BOUND_METHOD_EXACT_ARGS: + return oparg + 2; case CALL_PY_EXACT_ARGS: - return -1; + return oparg + 2; case CALL_PY_WITH_DEFAULTS: - return -1; + return oparg + 2; case CALL_NO_KW_TYPE_1: - return -1; + return oparg + 2; case CALL_NO_KW_STR_1: - return -1; + return oparg + 2; case CALL_NO_KW_TUPLE_1: - return -1; + return oparg + 2; case CALL_BUILTIN_CLASS: - return -1; + return oparg + 2; case CALL_NO_KW_BUILTIN_O: - return -1; + return oparg + 2; case CALL_NO_KW_BUILTIN_FAST: - return -1; + return oparg + 2; case CALL_BUILTIN_FAST_WITH_KEYWORDS: - return -1; + return oparg + 2; case CALL_NO_KW_LEN: - return -1; + return oparg + 2; case CALL_NO_KW_ISINSTANCE: - return -1; + return oparg + 2; case CALL_NO_KW_LIST_APPEND: - return -1; + return oparg + 2; case CALL_NO_KW_METHOD_DESCRIPTOR_O: - return -1; + return oparg + 2; case CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS: - return -1; + return oparg + 2; case CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS: - return -1; + return oparg + 2; case CALL_NO_KW_METHOD_DESCRIPTOR_FAST: - return -1; + return oparg + 2; case CALL_FUNCTION_EX: return ((oparg & 1) ? 1 : 0) + 3; case MAKE_FUNCTION: @@ -634,44 +634,44 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { return ((oparg & 1) ? 1 : 0) + 1; case LOAD_ATTR_METHOD_LAZY_DICT: return ((oparg & 1) ? 1 : 0) + 1; - case CALL_BOUND_METHOD_EXACT_ARGS: - return -1; case KW_NAMES: return 0; case CALL: - return -1; + return 1; + case CALL_BOUND_METHOD_EXACT_ARGS: + return 1; case CALL_PY_EXACT_ARGS: - return -1; + return 1; case CALL_PY_WITH_DEFAULTS: - return -1; + return 1; case CALL_NO_KW_TYPE_1: - return -1; + return 1; case CALL_NO_KW_STR_1: - return -1; + return 1; case CALL_NO_KW_TUPLE_1: - return -1; + return 1; case CALL_BUILTIN_CLASS: - return -1; + return 1; case CALL_NO_KW_BUILTIN_O: - return -1; + return 1; case CALL_NO_KW_BUILTIN_FAST: - return -1; + return 1; case CALL_BUILTIN_FAST_WITH_KEYWORDS: - return -1; + return 1; case CALL_NO_KW_LEN: - return -1; + return 1; case CALL_NO_KW_ISINSTANCE: - return -1; + return 1; case CALL_NO_KW_LIST_APPEND: - return -1; + return 1; case CALL_NO_KW_METHOD_DESCRIPTOR_O: - return -1; + return 1; case CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS: - return -1; + return 1; case CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS: - return -1; + return 1; case CALL_NO_KW_METHOD_DESCRIPTOR_FAST: - return -1; + return 1; case CALL_FUNCTION_EX: return 1; case MAKE_FUNCTION: @@ -846,25 +846,25 @@ struct opcode_metadata { [LOAD_ATTR_METHOD_WITH_VALUES] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC00000000 }, [LOAD_ATTR_METHOD_NO_DICT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC00000000 }, [LOAD_ATTR_METHOD_LAZY_DICT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC00000000 }, - [CALL_BOUND_METHOD_EXACT_ARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [KW_NAMES] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_PY_EXACT_ARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_PY_WITH_DEFAULTS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_TYPE_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_STR_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_TUPLE_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_BUILTIN_CLASS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_BUILTIN_O] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_BUILTIN_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_BUILTIN_FAST_WITH_KEYWORDS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_LEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_ISINSTANCE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_LIST_APPEND] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_METHOD_DESCRIPTOR_O] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_METHOD_DESCRIPTOR_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_BOUND_METHOD_EXACT_ARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_PY_EXACT_ARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_PY_WITH_DEFAULTS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_NO_KW_TYPE_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_NO_KW_STR_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_NO_KW_TUPLE_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_BUILTIN_CLASS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_NO_KW_BUILTIN_O] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_NO_KW_BUILTIN_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_BUILTIN_FAST_WITH_KEYWORDS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_NO_KW_LEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_NO_KW_ISINSTANCE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_NO_KW_LIST_APPEND] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_NO_KW_METHOD_DESCRIPTOR_O] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_NO_KW_METHOD_DESCRIPTOR_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, [CALL_FUNCTION_EX] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [MAKE_FUNCTION] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [RETURN_GENERATOR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 9b5aa914cdee86..1fcfbb67709029 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -391,9 +391,11 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None # Write the body, substituting a goto for ERROR_IF() and other stuff assert dedent <= 0 extra = " " * -dedent + names_to_skip = self.unmoved_names | frozenset({UNUSED, "null"}) for line in self.block_text: if m := re.match(r"(\s*)ERROR_IF\((.+), (\w+)\);\s*(?://.*)?$", line): space, cond, label = m.groups() + space = extra + space # ERROR_IF() must pop the inputs from the stack. # The code block is responsible for DECREF()ing them. # NOTE: If the label doesn't exist, just add it to ceval.c. @@ -412,16 +414,25 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None symbolic = "" if symbolic: out.write_raw( - f"{extra}{space}if ({cond}) {{ STACK_SHRINK({symbolic}); goto {label}; }}\n" + f"{space}if ({cond}) {{ STACK_SHRINK({symbolic}); goto {label}; }}\n" ) else: - out.write_raw(f"{extra}{space}if ({cond}) goto {label};\n") + out.write_raw(f"{space}if ({cond}) goto {label};\n") elif m := re.match(r"(\s*)DECREF_INPUTS\(\);\s*(?://.*)?$", line): if not self.register: - space = m.group(1) + space = extra + m.group(1) for ieff in self.input_effects: - if ieff.name not in self.unmoved_names: - out.write_raw(f"{extra}{space}Py_DECREF({ieff.name});\n") + if ieff.name in names_to_skip: + continue + if ieff.size: + out.write_raw( + f"{space}for (int _i = {ieff.size}; --_i >= 0;) {{\n" + ) + out.write_raw(f"{space} Py_DECREF({ieff.name}[_i]);\n") + out.write_raw(f"{space}}}\n") + else: + decref = "XDECREF" if ieff.cond else "DECREF" + out.write_raw(f"{space}Py_{decref}({ieff.name});\n") else: out.write_raw(extra + line) From de3669ebcb33ca8e3373fbbaed646c5f287979b8 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 8 Feb 2023 21:25:42 +0100 Subject: [PATCH 051/247] gh-101277: Port more itertools static types to heap types (#101304) Add accumulate, compress, count, filterfalse, pairwise, product, and zip_longest types to module state. --- Modules/clinic/itertoolsmodule.c.h | 6 +- Modules/itertoolsmodule.c | 521 +++++++++++------------------ 2 files changed, 200 insertions(+), 327 deletions(-) diff --git a/Modules/clinic/itertoolsmodule.c.h b/Modules/clinic/itertoolsmodule.c.h index be44246cc9705a..d15d5f0890ca98 100644 --- a/Modules/clinic/itertoolsmodule.c.h +++ b/Modules/clinic/itertoolsmodule.c.h @@ -102,7 +102,7 @@ static PyObject * pairwise_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; - PyTypeObject *base_tp = &pairwise_type; + PyTypeObject *base_tp = clinic_state()->pairwise_type; PyObject *iterable; if ((type == base_tp || type->tp_init == base_tp->tp_init) && @@ -821,7 +821,7 @@ static PyObject * itertools_filterfalse(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; - PyTypeObject *base_tp = &filterfalse_type; + PyTypeObject *base_tp = clinic_state()->filterfalse_type; PyObject *func; PyObject *seq; @@ -913,4 +913,4 @@ itertools_count(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=b86fcd99bd32145e input=a9049054013a1b77]*/ +/*[clinic end generated code: output=a08b58d7dac825da input=a9049054013a1b77]*/ diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index c9baa47e2c0edd..ce8720d0fd9228 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -12,15 +12,22 @@ */ typedef struct { + PyTypeObject *accumulate_type; PyTypeObject *combinations_type; + PyTypeObject *compress_type; + PyTypeObject *count_type; PyTypeObject *cwr_type; PyTypeObject *cycle_type; PyTypeObject *dropwhile_type; + PyTypeObject *filterfalse_type; PyTypeObject *groupby_type; PyTypeObject *_grouper_type; + PyTypeObject *pairwise_type; PyTypeObject *permutations_type; + PyTypeObject *product_type; PyTypeObject *starmap_type; PyTypeObject *takewhile_type; + PyTypeObject *ziplongest_type; } itertools_state; static inline itertools_state * @@ -48,7 +55,6 @@ find_state_by_type(PyTypeObject *tp) assert(mod != NULL); return get_module_state(mod); } -#define clinic_state() (find_state_by_type(type)) /*[clinic input] module itertools @@ -65,23 +71,19 @@ class itertools.chain "chainobject *" "&chain_type" class itertools.combinations "combinationsobject *" "clinic_state()->combinations_type" class itertools.combinations_with_replacement "cwr_object *" "clinic_state()->cwr_type" class itertools.permutations "permutationsobject *" "clinic_state()->permutations_type" -class itertools.accumulate "accumulateobject *" "&accumulate_type" -class itertools.compress "compressobject *" "&compress_type" -class itertools.filterfalse "filterfalseobject *" "&filterfalse_type" -class itertools.count "countobject *" "&count_type" -class itertools.pairwise "pairwiseobject *" "&pairwise_type" +class itertools.accumulate "accumulateobject *" "clinic_state()->accumulate_type" +class itertools.compress "compressobject *" "clinic_state()->compress_type" +class itertools.filterfalse "filterfalseobject *" "clinic_state()->filterfalse_type" +class itertools.count "countobject *" "clinic_state()->count_type" +class itertools.pairwise "pairwiseobject *" "clinic_state()->pairwise_type" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=1790ac655869a651]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=28ffff5c0c93eed7]*/ static PyTypeObject teedataobject_type; static PyTypeObject tee_type; static PyTypeObject batched_type; -static PyTypeObject accumulate_type; -static PyTypeObject compress_type; -static PyTypeObject filterfalse_type; -static PyTypeObject count_type; -static PyTypeObject pairwise_type; +#define clinic_state() (find_state_by_type(type)) #define clinic_state_by_cls() (get_module_state_by_cls(base_tp)) #include "clinic/itertoolsmodule.c.h" #undef clinic_state_by_cls @@ -308,15 +310,18 @@ pairwise_new_impl(PyTypeObject *type, PyObject *iterable) static void pairwise_dealloc(pairwiseobject *po) { + PyTypeObject *tp = Py_TYPE(po); PyObject_GC_UnTrack(po); Py_XDECREF(po->it); Py_XDECREF(po->old); - Py_TYPE(po)->tp_free(po); + tp->tp_free(po); + Py_DECREF(tp); } static int pairwise_traverse(pairwiseobject *po, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(po)); Py_VISIT(po->it); Py_VISIT(po->old); return 0; @@ -351,48 +356,25 @@ pairwise_next(pairwiseobject *po) return result; } -static PyTypeObject pairwise_type = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) - "itertools.pairwise", /* tp_name */ - sizeof(pairwiseobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)pairwise_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - pairwise_new__doc__, /* tp_doc */ - (traverseproc)pairwise_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)pairwise_next, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - PyType_GenericAlloc, /* tp_alloc */ - pairwise_new, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Slot pairwise_slots[] = { + {Py_tp_dealloc, pairwise_dealloc}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_doc, (void *)pairwise_new__doc__}, + {Py_tp_traverse, pairwise_traverse}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, pairwise_next}, + {Py_tp_alloc, PyType_GenericAlloc}, + {Py_tp_new, pairwise_new}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec pairwise_spec = { + .name = "itertools.pairwise", + .basicsize = sizeof(pairwiseobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = pairwise_slots, }; @@ -2300,8 +2282,6 @@ typedef struct { int stopped; /* set to 1 when the iterator is exhausted */ } productobject; -static PyTypeObject product_type; - static PyObject * product_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { @@ -2388,12 +2368,14 @@ product_new(PyTypeObject *type, PyObject *args, PyObject *kwds) static void product_dealloc(productobject *lz) { + PyTypeObject *tp = Py_TYPE(lz); PyObject_GC_UnTrack(lz); Py_XDECREF(lz->pools); Py_XDECREF(lz->result); if (lz->indices != NULL) PyMem_Free(lz->indices); - Py_TYPE(lz)->tp_free(lz); + tp->tp_free(lz); + Py_DECREF(tp); } static PyObject * @@ -2409,6 +2391,7 @@ PyDoc_STRVAR(sizeof_doc, "Returns size in memory, in bytes."); static int product_traverse(productobject *lz, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(lz)); Py_VISIT(lz->pools); Py_VISIT(lz->result); return 0; @@ -2600,48 +2583,25 @@ product(A, repeat=4) means the same as product(A, A, A, A).\n\n\ product('ab', range(3)) --> ('a',0) ('a',1) ('a',2) ('b',0) ('b',1) ('b',2)\n\ product((0,1), (0,1), (0,1)) --> (0,0,0) (0,0,1) (0,1,0) (0,1,1) (1,0,0) ..."); -static PyTypeObject product_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "itertools.product", /* tp_name */ - sizeof(productobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)product_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - product_doc, /* tp_doc */ - (traverseproc)product_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)product_next, /* tp_iternext */ - product_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - product_new, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Slot product_slots[] = { + {Py_tp_dealloc, product_dealloc}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_doc, (void *)product_doc}, + {Py_tp_traverse, product_traverse}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, product_next}, + {Py_tp_methods, product_methods}, + {Py_tp_new, product_new}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec product_spec = { + .name = "itertools.product", + .basicsize = sizeof(productobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = product_slots, }; @@ -3658,17 +3618,20 @@ itertools_accumulate_impl(PyTypeObject *type, PyObject *iterable, static void accumulate_dealloc(accumulateobject *lz) { + PyTypeObject *tp = Py_TYPE(lz); PyObject_GC_UnTrack(lz); Py_XDECREF(lz->binop); Py_XDECREF(lz->total); Py_XDECREF(lz->it); Py_XDECREF(lz->initial); - Py_TYPE(lz)->tp_free(lz); + tp->tp_free(lz); + Py_DECREF(tp); } static int accumulate_traverse(accumulateobject *lz, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(lz)); Py_VISIT(lz->binop); Py_VISIT(lz->it); Py_VISIT(lz->total); @@ -3762,48 +3725,25 @@ static PyMethodDef accumulate_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyTypeObject accumulate_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "itertools.accumulate", /* tp_name */ - sizeof(accumulateobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)accumulate_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - itertools_accumulate__doc__, /* tp_doc */ - (traverseproc)accumulate_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)accumulate_next, /* tp_iternext */ - accumulate_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - itertools_accumulate, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Slot accumulate_slots[] = { + {Py_tp_dealloc, accumulate_dealloc}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_doc, (void *)itertools_accumulate__doc__}, + {Py_tp_traverse, accumulate_traverse}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, accumulate_next}, + {Py_tp_methods, accumulate_methods}, + {Py_tp_new, itertools_accumulate}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec accumulate_spec = { + .name = "itertools.accumulate", + .basicsize = sizeof(accumulateobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = accumulate_slots, }; @@ -3864,15 +3804,18 @@ itertools_compress_impl(PyTypeObject *type, PyObject *seq1, PyObject *seq2) static void compress_dealloc(compressobject *lz) { + PyTypeObject *tp = Py_TYPE(lz); PyObject_GC_UnTrack(lz); Py_XDECREF(lz->data); Py_XDECREF(lz->selectors); - Py_TYPE(lz)->tp_free(lz); + tp->tp_free(lz); + Py_DECREF(tp); } static int compress_traverse(compressobject *lz, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(lz)); Py_VISIT(lz->data); Py_VISIT(lz->selectors); return 0; @@ -3927,48 +3870,25 @@ static PyMethodDef compress_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyTypeObject compress_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "itertools.compress", /* tp_name */ - sizeof(compressobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)compress_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - itertools_compress__doc__, /* tp_doc */ - (traverseproc)compress_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)compress_next, /* tp_iternext */ - compress_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - itertools_compress, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Slot compress_slots[] = { + {Py_tp_dealloc, compress_dealloc}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_doc, (void *)itertools_compress__doc__}, + {Py_tp_traverse, compress_traverse}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, compress_next}, + {Py_tp_methods, compress_methods}, + {Py_tp_new, itertools_compress}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec compress_spec = { + .name = "itertools.compress", + .basicsize = sizeof(compressobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = compress_slots, }; @@ -4018,15 +3938,18 @@ itertools_filterfalse_impl(PyTypeObject *type, PyObject *func, PyObject *seq) static void filterfalse_dealloc(filterfalseobject *lz) { + PyTypeObject *tp = Py_TYPE(lz); PyObject_GC_UnTrack(lz); Py_XDECREF(lz->func); Py_XDECREF(lz->it); - Py_TYPE(lz)->tp_free(lz); + tp->tp_free(lz); + Py_DECREF(tp); } static int filterfalse_traverse(filterfalseobject *lz, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(lz)); Py_VISIT(lz->it); Py_VISIT(lz->func); return 0; @@ -4078,48 +4001,25 @@ static PyMethodDef filterfalse_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyTypeObject filterfalse_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "itertools.filterfalse", /* tp_name */ - sizeof(filterfalseobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)filterfalse_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - itertools_filterfalse__doc__, /* tp_doc */ - (traverseproc)filterfalse_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)filterfalse_next, /* tp_iternext */ - filterfalse_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - itertools_filterfalse, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Slot filterfalse_slots[] = { + {Py_tp_dealloc, filterfalse_dealloc}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_doc, (void *)itertools_filterfalse__doc__}, + {Py_tp_traverse, filterfalse_traverse}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, filterfalse_next}, + {Py_tp_methods, filterfalse_methods}, + {Py_tp_new, itertools_filterfalse}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec filterfalse_spec = { + .name = "itertools.filterfalse", + .basicsize = sizeof(filterfalseobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = filterfalse_slots, }; @@ -4245,15 +4145,18 @@ itertools_count_impl(PyTypeObject *type, PyObject *long_cnt, static void count_dealloc(countobject *lz) { + PyTypeObject *tp = Py_TYPE(lz); PyObject_GC_UnTrack(lz); Py_XDECREF(lz->long_cnt); Py_XDECREF(lz->long_step); - Py_TYPE(lz)->tp_free(lz); + tp->tp_free(lz); + Py_DECREF(tp); } static int count_traverse(countobject *lz, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(lz)); Py_VISIT(lz->long_cnt); Py_VISIT(lz->long_step); return 0; @@ -4327,48 +4230,26 @@ static PyMethodDef count_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyTypeObject count_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "itertools.count", /* tp_name */ - sizeof(countobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)count_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - (reprfunc)count_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - itertools_count__doc__, /* tp_doc */ - (traverseproc)count_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)count_next, /* tp_iternext */ - count_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - itertools_count, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Slot count_slots[] = { + {Py_tp_dealloc, count_dealloc}, + {Py_tp_repr, count_repr}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_doc, (void *)itertools_count__doc__}, + {Py_tp_traverse, count_traverse}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, count_next}, + {Py_tp_methods, count_methods}, + {Py_tp_new, itertools_count}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec count_spec = { + .name = "itertools.count", + .basicsize = sizeof(countobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = count_slots, }; @@ -4536,8 +4417,6 @@ typedef struct { PyObject *fillvalue; } ziplongestobject; -static PyTypeObject ziplongest_type; - static PyObject * zip_longest_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { @@ -4609,16 +4488,19 @@ zip_longest_new(PyTypeObject *type, PyObject *args, PyObject *kwds) static void zip_longest_dealloc(ziplongestobject *lz) { + PyTypeObject *tp = Py_TYPE(lz); PyObject_GC_UnTrack(lz); Py_XDECREF(lz->ittuple); Py_XDECREF(lz->result); Py_XDECREF(lz->fillvalue); - Py_TYPE(lz)->tp_free(lz); + tp->tp_free(lz); + Py_DECREF(tp); } static int zip_longest_traverse(ziplongestobject *lz, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(lz)); Py_VISIT(lz->ittuple); Py_VISIT(lz->result); Py_VISIT(lz->fillvalue); @@ -4752,48 +4634,25 @@ are exhausted, the fillvalue is substituted in their place. The fillvalue\n\ defaults to None or can be specified by a keyword argument.\n\ "); -static PyTypeObject ziplongest_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "itertools.zip_longest", /* tp_name */ - sizeof(ziplongestobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)zip_longest_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - zip_longest_doc, /* tp_doc */ - (traverseproc)zip_longest_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)zip_longest_next, /* tp_iternext */ - zip_longest_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - zip_longest_new, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Slot ziplongest_slots[] = { + {Py_tp_dealloc, zip_longest_dealloc}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_doc, (void *)zip_longest_doc}, + {Py_tp_traverse, zip_longest_traverse}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, zip_longest_next}, + {Py_tp_methods, zip_longest_methods}, + {Py_tp_new, zip_longest_new}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec ziplongest_spec = { + .name = "itertools.zip_longest", + .basicsize = sizeof(ziplongestobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = ziplongest_slots, }; @@ -4835,15 +4694,22 @@ static int itertoolsmodule_traverse(PyObject *mod, visitproc visit, void *arg) { itertools_state *state = get_module_state(mod); + Py_VISIT(state->accumulate_type); Py_VISIT(state->combinations_type); + Py_VISIT(state->compress_type); + Py_VISIT(state->count_type); Py_VISIT(state->cwr_type); Py_VISIT(state->cycle_type); Py_VISIT(state->dropwhile_type); + Py_VISIT(state->filterfalse_type); Py_VISIT(state->groupby_type); Py_VISIT(state->_grouper_type); + Py_VISIT(state->pairwise_type); Py_VISIT(state->permutations_type); + Py_VISIT(state->product_type); Py_VISIT(state->starmap_type); Py_VISIT(state->takewhile_type); + Py_VISIT(state->ziplongest_type); return 0; } @@ -4851,15 +4717,22 @@ static int itertoolsmodule_clear(PyObject *mod) { itertools_state *state = get_module_state(mod); + Py_CLEAR(state->accumulate_type); Py_CLEAR(state->combinations_type); + Py_CLEAR(state->compress_type); + Py_CLEAR(state->count_type); Py_CLEAR(state->cwr_type); Py_CLEAR(state->cycle_type); Py_CLEAR(state->dropwhile_type); + Py_CLEAR(state->filterfalse_type); Py_CLEAR(state->groupby_type); Py_CLEAR(state->_grouper_type); + Py_CLEAR(state->pairwise_type); Py_CLEAR(state->permutations_type); + Py_CLEAR(state->product_type); Py_CLEAR(state->starmap_type); Py_CLEAR(state->takewhile_type); + Py_CLEAR(state->ziplongest_type); return 0; } @@ -4884,27 +4757,27 @@ static int itertoolsmodule_exec(PyObject *mod) { itertools_state *state = get_module_state(mod); + ADD_TYPE(mod, state->accumulate_type, &accumulate_spec); ADD_TYPE(mod, state->combinations_type, &combinations_spec); + ADD_TYPE(mod, state->compress_type, &compress_spec); + ADD_TYPE(mod, state->count_type, &count_spec); ADD_TYPE(mod, state->cwr_type, &cwr_spec); ADD_TYPE(mod, state->cycle_type, &cycle_spec); ADD_TYPE(mod, state->dropwhile_type, &dropwhile_spec); + ADD_TYPE(mod, state->filterfalse_type, &filterfalse_spec); ADD_TYPE(mod, state->groupby_type, &groupby_spec); ADD_TYPE(mod, state->_grouper_type, &_grouper_spec); + ADD_TYPE(mod, state->pairwise_type, &pairwise_spec); ADD_TYPE(mod, state->permutations_type, &permutations_spec); + ADD_TYPE(mod, state->product_type, &product_spec); ADD_TYPE(mod, state->starmap_type, &starmap_spec); ADD_TYPE(mod, state->takewhile_type, &takewhile_spec); + ADD_TYPE(mod, state->ziplongest_type, &ziplongest_spec); PyTypeObject *typelist[] = { - &accumulate_type, &batched_type, &islice_type, &chain_type, - &compress_type, - &filterfalse_type, - &count_type, - &ziplongest_type, - &pairwise_type, - &product_type, &repeat_type, &tee_type, &teedataobject_type From 23751ed826ee63fb486e874ec25934ea87dd8519 Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Thu, 9 Feb 2023 02:12:19 +0400 Subject: [PATCH 052/247] gh-101283: Improved fallback logic for subprocess with shell=True on Windows (GH-101286) --- Doc/library/subprocess.rst | 40 +++++++++++++++++++ Lib/subprocess.py | 16 +++++++- ...-01-24-16-12-00.gh-issue-101283.9tqu39.rst | 3 ++ 3 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Security/2023-01-24-16-12-00.gh-issue-101283.9tqu39.rst diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst index a87369a2461a54..c93319e7011c20 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -111,6 +111,14 @@ underlying :class:`Popen` interface can be used directly. Added the *text* parameter, as a more understandable alias of *universal_newlines*. Added the *capture_output* parameter. + .. versionchanged:: 3.11.2 + + Changed Windows shell search order for ``shell=True``. The current + directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and + ``%SystemRoot%\System32\cmd.exe``. As a result, dropping a + malicious program named ``cmd.exe`` into a current directory no + longer works. + .. class:: CompletedProcess The return value from :func:`run`, representing a process that has finished. @@ -487,6 +495,14 @@ functions. *executable* parameter accepts a bytes and :term:`path-like object` on Windows. + .. versionchanged:: 3.11.2 + + Changed Windows shell search order for ``shell=True``. The current + directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and + ``%SystemRoot%\System32\cmd.exe``. As a result, dropping a + malicious program named ``cmd.exe`` into a current directory no + longer works. + *stdin*, *stdout* and *stderr* specify the executed program's standard input, standard output and standard error file handles, respectively. Valid values are ``None``, :data:`PIPE`, :data:`DEVNULL`, an existing file descriptor (a @@ -1158,6 +1174,14 @@ calls these functions. .. versionchanged:: 3.3 *timeout* was added. + .. versionchanged:: 3.11.2 + + Changed Windows shell search order for ``shell=True``. The current + directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and + ``%SystemRoot%\System32\cmd.exe``. As a result, dropping a + malicious program named ``cmd.exe`` into a current directory no + longer works. + .. function:: check_call(args, *, stdin=None, stdout=None, stderr=None, \ shell=False, cwd=None, timeout=None, \ **other_popen_kwargs) @@ -1190,6 +1214,14 @@ calls these functions. .. versionchanged:: 3.3 *timeout* was added. + .. versionchanged:: 3.11.2 + + Changed Windows shell search order for ``shell=True``. The current + directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and + ``%SystemRoot%\System32\cmd.exe``. As a result, dropping a + malicious program named ``cmd.exe`` into a current directory no + longer works. + .. function:: check_output(args, *, stdin=None, stderr=None, shell=False, \ cwd=None, encoding=None, errors=None, \ @@ -1245,6 +1277,14 @@ calls these functions. .. versionadded:: 3.7 *text* was added as a more readable alias for *universal_newlines*. + .. versionchanged:: 3.11.2 + + Changed Windows shell search order for ``shell=True``. The current + directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and + ``%SystemRoot%\System32\cmd.exe``. As a result, dropping a + malicious program named ``cmd.exe`` into a current directory no + longer works. + .. _subprocess-replacements: diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 9cadd1bf8e622c..fa527d50ebb44d 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -1480,7 +1480,21 @@ def _execute_child(self, args, executable, preexec_fn, close_fds, if shell: startupinfo.dwFlags |= _winapi.STARTF_USESHOWWINDOW startupinfo.wShowWindow = _winapi.SW_HIDE - comspec = os.environ.get("COMSPEC", "cmd.exe") + if not executable: + # gh-101283: without a fully-qualified path, before Windows + # checks the system directories, it first looks in the + # application directory, and also the current directory if + # NeedCurrentDirectoryForExePathW(ExeName) is true, so try + # to avoid executing unqualified "cmd.exe". + comspec = os.environ.get('ComSpec') + if not comspec: + system_root = os.environ.get('SystemRoot', '') + comspec = os.path.join(system_root, 'System32', 'cmd.exe') + if not os.path.isabs(comspec): + raise FileNotFoundError('shell not found: neither %ComSpec% nor %SystemRoot% is set') + if os.path.isabs(comspec): + executable = comspec + args = '{} /c "{}"'.format (comspec, args) if cwd is not None: diff --git a/Misc/NEWS.d/next/Security/2023-01-24-16-12-00.gh-issue-101283.9tqu39.rst b/Misc/NEWS.d/next/Security/2023-01-24-16-12-00.gh-issue-101283.9tqu39.rst new file mode 100644 index 00000000000000..0efdfa10234185 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2023-01-24-16-12-00.gh-issue-101283.9tqu39.rst @@ -0,0 +1,3 @@ +:class:`subprocess.Popen` now uses a safer approach to find +``cmd.exe`` when launching with ``shell=True``. Patch by Eryk Sun, +based on a patch by Oleg Iarygin. From 20cf32e761fb9eaccc142415b389998896869263 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Wed, 8 Feb 2023 23:38:56 +0000 Subject: [PATCH 053/247] gh-101283: Fix use of unbound variable (GH-101712) --- Lib/subprocess.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/subprocess.py b/Lib/subprocess.py index fa527d50ebb44d..1f203bd00d3500 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -1494,6 +1494,8 @@ def _execute_child(self, args, executable, preexec_fn, close_fds, raise FileNotFoundError('shell not found: neither %ComSpec% nor %SystemRoot% is set') if os.path.isabs(comspec): executable = comspec + else: + comspec = executable args = '{} /c "{}"'.format (comspec, args) From 0e0c5d8baaa6aa91f4221c5aa57d5586e58e8652 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Wed, 8 Feb 2023 23:52:03 +0000 Subject: [PATCH 054/247] gh-101283: Version was just released, so should be changed in 3.11.3 (GH-101719) --- Doc/library/subprocess.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst index c93319e7011c20..d792a43eeb271f 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -111,7 +111,7 @@ underlying :class:`Popen` interface can be used directly. Added the *text* parameter, as a more understandable alias of *universal_newlines*. Added the *capture_output* parameter. - .. versionchanged:: 3.11.2 + .. versionchanged:: 3.11.3 Changed Windows shell search order for ``shell=True``. The current directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and @@ -495,7 +495,7 @@ functions. *executable* parameter accepts a bytes and :term:`path-like object` on Windows. - .. versionchanged:: 3.11.2 + .. versionchanged:: 3.11.3 Changed Windows shell search order for ``shell=True``. The current directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and @@ -1174,7 +1174,7 @@ calls these functions. .. versionchanged:: 3.3 *timeout* was added. - .. versionchanged:: 3.11.2 + .. versionchanged:: 3.11.3 Changed Windows shell search order for ``shell=True``. The current directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and @@ -1214,7 +1214,7 @@ calls these functions. .. versionchanged:: 3.3 *timeout* was added. - .. versionchanged:: 3.11.2 + .. versionchanged:: 3.11.3 Changed Windows shell search order for ``shell=True``. The current directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and @@ -1277,7 +1277,7 @@ calls these functions. .. versionadded:: 3.7 *text* was added as a more readable alias for *universal_newlines*. - .. versionchanged:: 3.11.2 + .. versionchanged:: 3.11.3 Changed Windows shell search order for ``shell=True``. The current directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and From 65b7b6bd23ea789357777f3a0a6f25a79bb04177 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 8 Feb 2023 16:23:19 -0800 Subject: [PATCH 055/247] gh-98831: Use opcode metadata for stack_effect() (#101704) * Write output and metadata in a single run This halves the time to run the cases generator (most of the time goes into parsing the input). * Declare or define opcode metadata based on NEED_OPCODE_TABLES * Use generated metadata for stack_effect() * compile.o depends on opcode_metadata.h * Return -1 from _PyOpcode_num_popped/pushed for unknown opcode --- Makefile.pre.in | 17 +- Python/compile.c | 258 ++++-------------------- Python/opcode_metadata.h | 25 ++- Tools/cases_generator/generate_cases.py | 44 ++-- 4 files changed, 88 insertions(+), 256 deletions(-) diff --git a/Makefile.pre.in b/Makefile.pre.in index 2559df8e74952c..7a84b953d97962 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1445,24 +1445,21 @@ regen-opcode-targets: .PHONY: regen-cases regen-cases: - # Regenerate Python/generated_cases.c.h from Python/bytecodes.c + # Regenerate Python/generated_cases.c.h + # and Python/opcode_metadata.h + # from Python/bytecodes.c # using Tools/cases_generator/generate_cases.py PYTHONPATH=$(srcdir)/Tools/cases_generator \ $(PYTHON_FOR_REGEN) \ $(srcdir)/Tools/cases_generator/generate_cases.py \ -i $(srcdir)/Python/bytecodes.c \ - -o $(srcdir)/Python/generated_cases.c.h.new + -o $(srcdir)/Python/generated_cases.c.h.new \ + -m $(srcdir)/Python/opcode_metadata.h.new $(UPDATE_FILE) $(srcdir)/Python/generated_cases.c.h $(srcdir)/Python/generated_cases.c.h.new - # Regenerate Python/opcode_metadata.h from Python/bytecodes.c - # using Tools/cases_generator/generate_cases.py --metadata - PYTHONPATH=$(srcdir)/Tools/cases_generator \ - $(PYTHON_FOR_REGEN) \ - $(srcdir)/Tools/cases_generator/generate_cases.py \ - --metadata \ - -i $(srcdir)/Python/bytecodes.c \ - -o $(srcdir)/Python/opcode_metadata.h.new $(UPDATE_FILE) $(srcdir)/Python/opcode_metadata.h $(srcdir)/Python/opcode_metadata.h.new +Python/compile.o: $(srcdir)/Python/opcode_metadata.h + Python/ceval.o: \ $(srcdir)/Python/ceval_macros.h \ $(srcdir)/Python/condvar.h \ diff --git a/Python/compile.c b/Python/compile.c index df2dffb95bbd7e..a3c915c3c14a96 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -1074,135 +1074,49 @@ basicblock_next_instr(basicblock *b) static int stack_effect(int opcode, int oparg, int jump) { - switch (opcode) { - case NOP: - case EXTENDED_ARG: - case RESUME: - case CACHE: - return 0; - - /* Stack manipulation */ - case POP_TOP: - return -1; - case SWAP: - return 0; - case END_FOR: - return -2; - - /* Unary operators */ - case UNARY_NEGATIVE: - case UNARY_NOT: - case UNARY_INVERT: - return 0; - - case SET_ADD: - case LIST_APPEND: - return -1; - case MAP_ADD: - return -2; - - case BINARY_SUBSCR: - return -1; - case BINARY_SLICE: - return -2; - case STORE_SUBSCR: - return -3; - case STORE_SLICE: - return -4; - case DELETE_SUBSCR: - return -2; - - case GET_ITER: - return 0; - - case LOAD_BUILD_CLASS: - return 1; + if (0 <= opcode && opcode <= MAX_REAL_OPCODE) { + if (_PyOpcode_Deopt[opcode] != opcode) { + // Specialized instructions are not supported. + return PY_INVALID_STACK_EFFECT; + } + int popped, pushed; + if (jump > 0) { + popped = _PyOpcode_num_popped(opcode, oparg, true); + pushed = _PyOpcode_num_pushed(opcode, oparg, true); + } + else { + popped = _PyOpcode_num_popped(opcode, oparg, false); + pushed = _PyOpcode_num_pushed(opcode, oparg, false); + } + if (popped < 0 || pushed < 0) { + return PY_INVALID_STACK_EFFECT; + } + if (jump >= 0) { + return pushed - popped; + } + if (jump < 0) { + // Compute max(pushed - popped, alt_pushed - alt_popped) + int alt_popped = _PyOpcode_num_popped(opcode, oparg, true); + int alt_pushed = _PyOpcode_num_pushed(opcode, oparg, true); + if (alt_popped < 0 || alt_pushed < 0) { + return PY_INVALID_STACK_EFFECT; + } + int diff = pushed - popped; + int alt_diff = alt_pushed - alt_popped; + if (alt_diff > diff) { + return alt_diff; + } + return diff; + } + } - case RETURN_VALUE: - return -1; - case RETURN_CONST: - return 0; - case SETUP_ANNOTATIONS: - return 0; - case YIELD_VALUE: - return 0; + // Pseudo ops + switch (opcode) { case POP_BLOCK: - return 0; - case POP_EXCEPT: - return -1; - - case STORE_NAME: - return -1; - case DELETE_NAME: - return 0; - case UNPACK_SEQUENCE: - return oparg-1; - case UNPACK_EX: - return (oparg&0xFF) + (oparg>>8); - case FOR_ITER: - return 1; - case SEND: - return jump > 0 ? -1 : 0; - case STORE_ATTR: - return -2; - case DELETE_ATTR: - return -1; - case STORE_GLOBAL: - return -1; - case DELETE_GLOBAL: - return 0; - case LOAD_CONST: - return 1; - case LOAD_NAME: - return 1; - case BUILD_TUPLE: - case BUILD_LIST: - case BUILD_SET: - case BUILD_STRING: - return 1-oparg; - case BUILD_MAP: - return 1 - 2*oparg; - case BUILD_CONST_KEY_MAP: - return -oparg; - case LOAD_ATTR: - return (oparg & 1); - case COMPARE_OP: - case IS_OP: - case CONTAINS_OP: - return -1; - case CHECK_EXC_MATCH: - return 0; - case CHECK_EG_MATCH: - return 0; - case IMPORT_NAME: - return -1; - case IMPORT_FROM: - return 1; - - /* Jumps */ - case JUMP_FORWARD: - case JUMP_BACKWARD: case JUMP: - case JUMP_BACKWARD_NO_INTERRUPT: case JUMP_NO_INTERRUPT: return 0; - case JUMP_IF_TRUE_OR_POP: - case JUMP_IF_FALSE_OR_POP: - return jump ? 0 : -1; - - case POP_JUMP_IF_NONE: - case POP_JUMP_IF_NOT_NONE: - case POP_JUMP_IF_FALSE: - case POP_JUMP_IF_TRUE: - return -1; - - case COMPARE_AND_BRANCH: - return -2; - - case LOAD_GLOBAL: - return (oparg & 1) + 1; - /* Exception handling pseudo-instructions */ case SETUP_FINALLY: /* 0 in the normal flow. @@ -1218,109 +1132,13 @@ stack_effect(int opcode, int oparg, int jump) * of __(a)enter__ and push 2 values before jumping to the handler * if an exception be raised. */ return jump ? 1 : 0; - case PREP_RERAISE_STAR: - return -1; - case RERAISE: - return -1; - case PUSH_EXC_INFO: - return 1; - - case WITH_EXCEPT_START: - return 1; - - case LOAD_FAST: - case LOAD_FAST_CHECK: - return 1; - case STORE_FAST: - return -1; - case DELETE_FAST: - return 0; - - case RETURN_GENERATOR: - return 0; - case RAISE_VARARGS: - return -oparg; - - /* Functions and calls */ - case KW_NAMES: - return 0; - case CALL: - return -1-oparg; - case CALL_INTRINSIC_1: - return 0; - case CALL_FUNCTION_EX: - return -2 - ((oparg & 0x01) != 0); - case MAKE_FUNCTION: - return 0 - ((oparg & 0x01) != 0) - ((oparg & 0x02) != 0) - - ((oparg & 0x04) != 0) - ((oparg & 0x08) != 0); - case BUILD_SLICE: - if (oparg == 3) - return -2; - else - return -1; - - /* Closures */ - case MAKE_CELL: - case COPY_FREE_VARS: - return 0; - case LOAD_CLOSURE: - return 1; - case LOAD_DEREF: - case LOAD_CLASSDEREF: - return 1; - case STORE_DEREF: - return -1; - case DELETE_DEREF: - return 0; - - /* Iterators and generators */ - case GET_AWAITABLE: - return 0; - - case BEFORE_ASYNC_WITH: - case BEFORE_WITH: - return 1; - case GET_AITER: - return 0; - case GET_ANEXT: - return 1; - case GET_YIELD_FROM_ITER: - return 0; - case END_ASYNC_FOR: - return -2; - case CLEANUP_THROW: - return -2; - case FORMAT_VALUE: - /* If there's a fmt_spec on the stack, we go from 2->1, - else 1->1. */ - return (oparg & FVS_MASK) == FVS_HAVE_SPEC ? -1 : 0; case LOAD_METHOD: return 1; - case LOAD_ASSERTION_ERROR: - return 1; - case LIST_EXTEND: - case SET_UPDATE: - case DICT_MERGE: - case DICT_UPDATE: - return -1; - case MATCH_CLASS: - return -2; - case GET_LEN: - case MATCH_MAPPING: - case MATCH_SEQUENCE: - case MATCH_KEYS: - return 1; - case COPY: - case PUSH_NULL: - return 1; - case BINARY_OP: - return -1; - case INTERPRETER_EXIT: - return -1; default: return PY_INVALID_STACK_EFFECT; } + return PY_INVALID_STACK_EFFECT; /* not reachable */ } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index 98791043f55271..db1dfd37a90132 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -2,8 +2,10 @@ // from Python/bytecodes.c // Do not edit! -#ifndef NDEBUG -static int +#ifndef NEED_OPCODE_TABLES +extern int _PyOpcode_num_popped(int opcode, int oparg, bool jump); +#else +int _PyOpcode_num_popped(int opcode, int oparg, bool jump) { switch(opcode) { case NOP: @@ -345,13 +347,15 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { case CACHE: return 0; default: - Py_UNREACHABLE(); + return -1; } } #endif -#ifndef NDEBUG -static int +#ifndef NEED_OPCODE_TABLES +extern int _PyOpcode_num_pushed(int opcode, int oparg, bool jump); +#else +int _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { switch(opcode) { case NOP: @@ -693,10 +697,11 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { case CACHE: return 0; default: - Py_UNREACHABLE(); + return -1; } } #endif + enum Direction { DIR_NONE, DIR_READ, DIR_WRITE }; enum InstructionFormat { INSTR_FMT_IB, INSTR_FMT_IBC, INSTR_FMT_IBC0, INSTR_FMT_IBC000, INSTR_FMT_IBC0000, INSTR_FMT_IBC00000000, INSTR_FMT_IBIB, INSTR_FMT_IX, INSTR_FMT_IXC, INSTR_FMT_IXC000 }; struct opcode_metadata { @@ -705,7 +710,12 @@ struct opcode_metadata { enum Direction dir_op3; bool valid_entry; enum InstructionFormat instr_format; -} _PyOpcode_opcode_metadata[256] = { +}; + +#ifndef NEED_OPCODE_TABLES +extern const struct opcode_metadata _PyOpcode_opcode_metadata[256]; +#else +const struct opcode_metadata _PyOpcode_opcode_metadata[256] = { [NOP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [RESUME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [LOAD_CLOSURE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, @@ -876,3 +886,4 @@ struct opcode_metadata { [EXTENDED_ARG] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [CACHE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, }; +#endif diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 1fcfbb67709029..aa8e14075c8738 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -43,10 +43,7 @@ "-o", "--output", type=str, help="Generated code", default=DEFAULT_OUTPUT ) arg_parser.add_argument( - "-m", - "--metadata", - action="store_true", - help=f"Generate metadata instead, changes output default to {DEFAULT_METADATA_OUTPUT}", + "-m", "--metadata", type=str, help="Generated metadata", default=DEFAULT_METADATA_OUTPUT ) @@ -498,13 +495,15 @@ class Analyzer: filename: str output_filename: str + metadata_filename: str src: str errors: int = 0 - def __init__(self, filename: str, output_filename: str): + def __init__(self, filename: str, output_filename: str, metadata_filename: str): """Read the input file.""" self.filename = filename self.output_filename = output_filename + self.metadata_filename = metadata_filename with open(filename) as f: self.src = f.read() @@ -889,21 +888,25 @@ def write_stack_effect_functions(self) -> None: def write_function( direction: str, data: list[tuple[AnyInstruction, str]] ) -> None: - self.out.emit("\n#ifndef NDEBUG") - self.out.emit("static int") + self.out.emit("") + self.out.emit("#ifndef NEED_OPCODE_TABLES") + self.out.emit(f"extern int _PyOpcode_num_{direction}(int opcode, int oparg, bool jump);") + self.out.emit("#else") + self.out.emit("int") self.out.emit(f"_PyOpcode_num_{direction}(int opcode, int oparg, bool jump) {{") self.out.emit(" switch(opcode) {") for instr, effect in data: self.out.emit(f" case {instr.name}:") self.out.emit(f" return {effect};") self.out.emit(" default:") - self.out.emit(" Py_UNREACHABLE();") + self.out.emit(" return -1;") self.out.emit(" }") self.out.emit("}") self.out.emit("#endif") write_function("popped", popped_data) write_function("pushed", pushed_data) + self.out.emit("") def write_metadata(self) -> None: """Write instruction metadata to output file.""" @@ -924,7 +927,7 @@ def write_metadata(self) -> None: # Turn it into a list of enum definitions. format_enums = [INSTR_FMT_PREFIX + format for format in sorted(all_formats)] - with open(self.output_filename, "w") as f: + with open(self.metadata_filename, "w") as f: # Write provenance header f.write(f"// This file is generated by {THIS} --metadata\n") f.write(f"// from {os.path.relpath(self.filename, ROOT)}\n") @@ -935,7 +938,7 @@ def write_metadata(self) -> None: self.write_stack_effect_functions() - # Write variable definition + # Write type definitions self.out.emit("enum Direction { DIR_NONE, DIR_READ, DIR_WRITE };") self.out.emit(f"enum InstructionFormat {{ {', '.join(format_enums)} }};") self.out.emit("struct opcode_metadata {") @@ -945,7 +948,14 @@ def write_metadata(self) -> None: self.out.emit("enum Direction dir_op3;") self.out.emit("bool valid_entry;") self.out.emit("enum InstructionFormat instr_format;") - self.out.emit("} _PyOpcode_opcode_metadata[256] = {") + self.out.emit("};") + self.out.emit("") + + # Write metadata array declaration + self.out.emit("#ifndef NEED_OPCODE_TABLES") + self.out.emit("extern const struct opcode_metadata _PyOpcode_opcode_metadata[256];") + self.out.emit("#else") + self.out.emit("const struct opcode_metadata _PyOpcode_opcode_metadata[256] = {") # Write metadata for each instruction for thing in self.everything: @@ -962,6 +972,7 @@ def write_metadata(self) -> None: # Write end of array self.out.emit("};") + self.out.emit("#endif") def write_metadata_for_inst(self, instr: Instruction) -> None: """Write metadata for a single instruction.""" @@ -1184,18 +1195,13 @@ def variable_used(node: parser.Node, name: str) -> bool: def main(): """Parse command line, parse input, analyze, write output.""" args = arg_parser.parse_args() # Prints message and sys.exit(2) on error - if args.metadata: - if args.output == DEFAULT_OUTPUT: - args.output = DEFAULT_METADATA_OUTPUT - a = Analyzer(args.input, args.output) # Raises OSError if input unreadable + a = Analyzer(args.input, args.output, args.metadata) # Raises OSError if input unreadable a.parse() # Raises SyntaxError on failure a.analyze() # Prints messages and sets a.errors on failure if a.errors: sys.exit(f"Found {a.errors} errors") - if args.metadata: - a.write_metadata() - else: - a.write_instructions() # Raises OSError if output can't be written + a.write_instructions() # Raises OSError if output can't be written + a.write_metadata() if __name__ == "__main__": From 244d4cd9d22d73fb3c0938937c4f435bd68f32d4 Mon Sep 17 00:00:00 2001 From: Soumendra Ganguly <67527439+8vasu@users.noreply.github.com> Date: Wed, 8 Feb 2023 19:00:17 -0600 Subject: [PATCH 056/247] gh-85984: Remove legacy Lib/pty.py code. (#92365) Refactored the implementation of pty.fork to use os.login_tty. A DeprecationWarning is now raised by pty.master_open() and pty.slave_open(). They were undocumented and deprecated long long ago in the docstring in favor of pty.openpty. Signed-off-by: Soumendra Ganguly Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Gregory P. Smith --- Doc/whatsnew/3.12.rst | 4 ++++ Lib/pty.py | 20 +++++++------------ ...3-02-05-21-40-15.gh-issue-85984.Kfzbb2.rst | 4 ++++ 3 files changed, 15 insertions(+), 13 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-02-05-21-40-15.gh-issue-85984.Kfzbb2.rst diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index b723b70154f08d..45a5e5062d55b6 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -512,6 +512,10 @@ Pending Removal in Python 3.14 :func:`~multiprocessing.set_start_method` APIs to explicitly specify when your code *requires* ``'fork'``. See :ref:`multiprocessing-start-methods`. +* :mod:`pty` has two undocumented ``master_open()`` and ``slave_open()`` + functions that have been deprecated since Python 2 but only gained a + proper :exc:`DeprecationWarning` in 3.12. Remove them in 3.14. + Pending Removal in Future Versions ---------------------------------- diff --git a/Lib/pty.py b/Lib/pty.py index 03073f07c92c05..6571050886bd1d 100644 --- a/Lib/pty.py +++ b/Lib/pty.py @@ -40,6 +40,9 @@ def master_open(): Open a pty master and return the fd, and the filename of the slave end. Deprecated, use openpty() instead.""" + import warnings + warnings.warn("Use pty.openpty() instead.", DeprecationWarning, stacklevel=2) # Remove API in 3.14 + try: master_fd, slave_fd = os.openpty() except (AttributeError, OSError): @@ -69,6 +72,9 @@ def slave_open(tty_name): opened filedescriptor. Deprecated, use openpty() instead.""" + import warnings + warnings.warn("Use pty.openpty() instead.", DeprecationWarning, stacklevel=2) # Remove API in 3.14 + result = os.open(tty_name, os.O_RDWR) try: from fcntl import ioctl, I_PUSH @@ -101,20 +107,8 @@ def fork(): master_fd, slave_fd = openpty() pid = os.fork() if pid == CHILD: - # Establish a new session. - os.setsid() os.close(master_fd) - - # Slave becomes stdin/stdout/stderr of child. - os.dup2(slave_fd, STDIN_FILENO) - os.dup2(slave_fd, STDOUT_FILENO) - os.dup2(slave_fd, STDERR_FILENO) - if slave_fd > STDERR_FILENO: - os.close(slave_fd) - - # Explicitly open the tty to make it become a controlling tty. - tmp_fd = os.open(os.ttyname(STDOUT_FILENO), os.O_RDWR) - os.close(tmp_fd) + os.login_tty(slave_fd) else: os.close(slave_fd) diff --git a/Misc/NEWS.d/next/Library/2023-02-05-21-40-15.gh-issue-85984.Kfzbb2.rst b/Misc/NEWS.d/next/Library/2023-02-05-21-40-15.gh-issue-85984.Kfzbb2.rst new file mode 100644 index 00000000000000..c91829f2c739af --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-05-21-40-15.gh-issue-85984.Kfzbb2.rst @@ -0,0 +1,4 @@ +Refactored the implementation of :func:`pty.fork` to use :func:`os.login_tty`. + +A :exc:`DeprecationWarning` is now raised by ``pty.master_open()`` and ``pty.slave_open()``. They were +undocumented and deprecated long long ago in the docstring in favor of :func:`pty.openpty`. From 58395759b04273edccf3d199606088e0703ae6b1 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Thu, 9 Feb 2023 11:40:52 +0300 Subject: [PATCH 057/247] gh-101678: refactor the math module to use special functions from c11 (GH-101679) Shouldn't affect users, hence no news. Automerge-Triggered-By: GH:mdickinson --- Modules/_math.h | 5 +- Modules/mathmodule.c | 187 ++----------------------------------------- 2 files changed, 8 insertions(+), 184 deletions(-) diff --git a/Modules/_math.h b/Modules/_math.h index 4a6bc223ef5fb5..2285b64747c0bd 100644 --- a/Modules/_math.h +++ b/Modules/_math.h @@ -7,8 +7,9 @@ static double _Py_log1p(double x) { - /* Some platforms supply a log1p function but don't respect the sign of - zero: log1p(-0.0) gives 0.0 instead of the correct result of -0.0. + /* Some platforms (e.g. MacOS X 10.8, see gh-59682) supply a log1p function + but don't respect the sign of zero: log1p(-0.0) gives 0.0 instead of + the correct result of -0.0. To save fiddling with configure tests and platform checks, we handle the special case of zero input directly on all platforms. diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 992957e675a7a3..939954c95d9ff2 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -101,10 +101,6 @@ get_math_module_state(PyObject *module) static const double pi = 3.141592653589793238462643383279502884197; static const double logpi = 1.144729885849400174143427351353058711647; -#if !defined(HAVE_ERF) || !defined(HAVE_ERFC) -static const double sqrtpi = 1.772453850905516027298167483341145182798; -#endif /* !defined(HAVE_ERF) || !defined(HAVE_ERFC) */ - /* Version of PyFloat_AsDouble() with in-line fast paths for exact floats and integers. Gives a substantial @@ -162,7 +158,9 @@ m_sinpi(double x) return copysign(1.0, x)*r; } -/* Implementation of the real gamma function. In extensive but non-exhaustive +/* Implementation of the real gamma function. Kept here to work around + issues (see e.g. gh-70309) with quality of libm's tgamma/lgamma implementations + on various platforms (Windows, MacOS). In extensive but non-exhaustive random tests, this function proved accurate to within <= 10 ulps across the entire float domain. Note that accuracy may depend on the quality of the system math functions, the pow function in particular. Special cases @@ -458,163 +456,6 @@ m_lgamma(double x) return r; } -#if !defined(HAVE_ERF) || !defined(HAVE_ERFC) - -/* - Implementations of the error function erf(x) and the complementary error - function erfc(x). - - Method: we use a series approximation for erf for small x, and a continued - fraction approximation for erfc(x) for larger x; - combined with the relations erf(-x) = -erf(x) and erfc(x) = 1.0 - erf(x), - this gives us erf(x) and erfc(x) for all x. - - The series expansion used is: - - erf(x) = x*exp(-x*x)/sqrt(pi) * [ - 2/1 + 4/3 x**2 + 8/15 x**4 + 16/105 x**6 + ...] - - The coefficient of x**(2k-2) here is 4**k*factorial(k)/factorial(2*k). - This series converges well for smallish x, but slowly for larger x. - - The continued fraction expansion used is: - - erfc(x) = x*exp(-x*x)/sqrt(pi) * [1/(0.5 + x**2 -) 0.5/(2.5 + x**2 - ) - 3.0/(4.5 + x**2 - ) 7.5/(6.5 + x**2 - ) ...] - - after the first term, the general term has the form: - - k*(k-0.5)/(2*k+0.5 + x**2 - ...). - - This expansion converges fast for larger x, but convergence becomes - infinitely slow as x approaches 0.0. The (somewhat naive) continued - fraction evaluation algorithm used below also risks overflow for large x; - but for large x, erfc(x) == 0.0 to within machine precision. (For - example, erfc(30.0) is approximately 2.56e-393). - - Parameters: use series expansion for abs(x) < ERF_SERIES_CUTOFF and - continued fraction expansion for ERF_SERIES_CUTOFF <= abs(x) < - ERFC_CONTFRAC_CUTOFF. ERFC_SERIES_TERMS and ERFC_CONTFRAC_TERMS are the - numbers of terms to use for the relevant expansions. */ - -#define ERF_SERIES_CUTOFF 1.5 -#define ERF_SERIES_TERMS 25 -#define ERFC_CONTFRAC_CUTOFF 30.0 -#define ERFC_CONTFRAC_TERMS 50 - -/* - Error function, via power series. - - Given a finite float x, return an approximation to erf(x). - Converges reasonably fast for small x. -*/ - -static double -m_erf_series(double x) -{ - double x2, acc, fk, result; - int i, saved_errno; - - x2 = x * x; - acc = 0.0; - fk = (double)ERF_SERIES_TERMS + 0.5; - for (i = 0; i < ERF_SERIES_TERMS; i++) { - acc = 2.0 + x2 * acc / fk; - fk -= 1.0; - } - /* Make sure the exp call doesn't affect errno; - see m_erfc_contfrac for more. */ - saved_errno = errno; - result = acc * x * exp(-x2) / sqrtpi; - errno = saved_errno; - return result; -} - -/* - Complementary error function, via continued fraction expansion. - - Given a positive float x, return an approximation to erfc(x). Converges - reasonably fast for x large (say, x > 2.0), and should be safe from - overflow if x and nterms are not too large. On an IEEE 754 machine, with x - <= 30.0, we're safe up to nterms = 100. For x >= 30.0, erfc(x) is smaller - than the smallest representable nonzero float. */ - -static double -m_erfc_contfrac(double x) -{ - double x2, a, da, p, p_last, q, q_last, b, result; - int i, saved_errno; - - if (x >= ERFC_CONTFRAC_CUTOFF) - return 0.0; - - x2 = x*x; - a = 0.0; - da = 0.5; - p = 1.0; p_last = 0.0; - q = da + x2; q_last = 1.0; - for (i = 0; i < ERFC_CONTFRAC_TERMS; i++) { - double temp; - a += da; - da += 2.0; - b = da + x2; - temp = p; p = b*p - a*p_last; p_last = temp; - temp = q; q = b*q - a*q_last; q_last = temp; - } - /* Issue #8986: On some platforms, exp sets errno on underflow to zero; - save the current errno value so that we can restore it later. */ - saved_errno = errno; - result = p / q * x * exp(-x2) / sqrtpi; - errno = saved_errno; - return result; -} - -#endif /* !defined(HAVE_ERF) || !defined(HAVE_ERFC) */ - -/* Error function erf(x), for general x */ - -static double -m_erf(double x) -{ -#ifdef HAVE_ERF - return erf(x); -#else - double absx, cf; - - if (Py_IS_NAN(x)) - return x; - absx = fabs(x); - if (absx < ERF_SERIES_CUTOFF) - return m_erf_series(x); - else { - cf = m_erfc_contfrac(absx); - return x > 0.0 ? 1.0 - cf : cf - 1.0; - } -#endif -} - -/* Complementary error function erfc(x), for general x. */ - -static double -m_erfc(double x) -{ -#ifdef HAVE_ERFC - return erfc(x); -#else - double absx, cf; - - if (Py_IS_NAN(x)) - return x; - absx = fabs(x); - if (absx < ERF_SERIES_CUTOFF) - return 1.0 - m_erf_series(x); - else { - cf = m_erfc_contfrac(absx); - return x > 0.0 ? cf : 2.0 - cf; - } -#endif -} - /* wrapper for atan2 that deals directly with special cases before delegating to the platform libm for the remaining cases. This @@ -801,25 +642,7 @@ m_log2(double x) } if (x > 0.0) { -#ifdef HAVE_LOG2 return log2(x); -#else - double m; - int e; - m = frexp(x, &e); - /* We want log2(m * 2**e) == log(m) / log(2) + e. Care is needed when - * x is just greater than 1.0: in that case e is 1, log(m) is negative, - * and we get significant cancellation error from the addition of - * log(m) / log(2) to e. The slight rewrite of the expression below - * avoids this problem. - */ - if (x >= 1.0) { - return log(2.0 * m) / log(2.0) + (e - 1); - } - else { - return log(m) / log(2.0) + e; - } -#endif } else if (x == 0.0) { errno = EDOM; @@ -1261,10 +1084,10 @@ FUNC1(cos, cos, 0, FUNC1(cosh, cosh, 1, "cosh($module, x, /)\n--\n\n" "Return the hyperbolic cosine of x.") -FUNC1A(erf, m_erf, +FUNC1A(erf, erf, "erf($module, x, /)\n--\n\n" "Error function at x.") -FUNC1A(erfc, m_erfc, +FUNC1A(erfc, erfc, "erfc($module, x, /)\n--\n\n" "Complementary error function at x.") FUNC1(exp, exp, 1, From 45fa12aec8f508c224a1521cfe3ae597f1026264 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Thu, 9 Feb 2023 12:40:13 +0300 Subject: [PATCH 058/247] gh-101678: Merge math_1_to_whatever() and math_1() (#101730) `math_1_to_whatever()` is no longer useful, since all existing uses of it convert to `float`. Earlier versions of Python used `math_1_to_whatever` with an integer target; see gh-16991 for the PR where that use was removed. --- Modules/mathmodule.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 939954c95d9ff2..544560e8322c72 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -875,9 +875,7 @@ is_error(double x) */ static PyObject * -math_1_to_whatever(PyObject *arg, double (*func) (double), - PyObject *(*from_double_func) (double), - int can_overflow) +math_1(PyObject *arg, double (*func) (double), int can_overflow) { double x, r; x = PyFloat_AsDouble(arg); @@ -903,7 +901,7 @@ math_1_to_whatever(PyObject *arg, double (*func) (double), /* this branch unnecessary on most platforms */ return NULL; - return (*from_double_func)(r); + return PyFloat_FromDouble(r); } /* variant of math_1, to be used when the function being wrapped is known to @@ -951,12 +949,6 @@ math_1a(PyObject *arg, double (*func) (double)) OverflowError. */ -static PyObject * -math_1(PyObject *arg, double (*func) (double), int can_overflow) -{ - return math_1_to_whatever(arg, func, PyFloat_FromDouble, can_overflow); -} - static PyObject * math_2(PyObject *const *args, Py_ssize_t nargs, double (*func) (double, double), const char *funcname) From 1c49e61b9b18d550b9c5cff69a1dd3bb218e544a Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Thu, 9 Feb 2023 21:01:32 +0900 Subject: [PATCH 059/247] no-issue: Add Dong-hee Na as the cjkcodecs codeowner (gh-101731) --- .github/CODEOWNERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 8dd07d911f5b18..fc1bb3388976d5 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -151,6 +151,8 @@ Lib/ast.py @isidentical **/*sysconfig* @FFY00 +**/*cjkcodecs* @corona10 + # macOS /Mac/ @python/macos-team **/*osx_support* @python/macos-team From ecfd2d37c529c1952dc11fabe1758156924de67a Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 9 Feb 2023 13:05:53 +0000 Subject: [PATCH 060/247] GH-99293: Document that `Py_TPFLAGS_VALID_VERSION_TAG` shouldn't be used. (#GH-101736) Document that Py_TPFLAGS_VALID_VERSION_TAG shouldn't be used. --- Doc/c-api/typeobj.rst | 10 ++++++++++ .../2023-02-09-10-38-20.gh-issue-99293.mFqfpp.rst | 2 ++ 2 files changed, 12 insertions(+) create mode 100644 Misc/NEWS.d/next/C API/2023-02-09-10-38-20.gh-issue-99293.mFqfpp.rst diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 644830b940b417..fd8f49ccb1caab 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -1313,6 +1313,16 @@ and :c:type:`PyType_Type` effectively act as defaults.) .. versionadded:: 3.10 + .. data:: Py_TPFLAGS_VALID_VERSION_TAG + + Internal. Do not set or unset this flag. + To indicate that a class has changed call :c:func:`PyType_Modified` + + .. warning:: + This flag is present in header files, but is an internal feature and should + not be used. It will be removed in a future version of CPython + + .. c:member:: const char* PyTypeObject.tp_doc An optional pointer to a NUL-terminated C string giving the docstring for this diff --git a/Misc/NEWS.d/next/C API/2023-02-09-10-38-20.gh-issue-99293.mFqfpp.rst b/Misc/NEWS.d/next/C API/2023-02-09-10-38-20.gh-issue-99293.mFqfpp.rst new file mode 100644 index 00000000000000..8c0f05543747dc --- /dev/null +++ b/Misc/NEWS.d/next/C API/2023-02-09-10-38-20.gh-issue-99293.mFqfpp.rst @@ -0,0 +1,2 @@ +Document that the Py_TPFLAGS_VALID_VERSION_TAG is an internal feature, +should not be used, and will be removed. From cb2411886a181d25e0cff2c870f331d2949874d5 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Thu, 9 Feb 2023 19:49:02 +0300 Subject: [PATCH 061/247] gh-101670: typo fix in PyImport_ExtendInittab() (#101723) Co-authored-by: Eric Snow --- Python/import.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/import.c b/Python/import.c index 795d368966481e..302255d76edcd5 100644 --- a/Python/import.c +++ b/Python/import.c @@ -2651,7 +2651,7 @@ PyImport_ExtendInittab(struct _inittab *newtab) int res = 0; if (_PyRuntime.imports.inittab != NULL) { - Py_FatalError("PyImport_ExtendInittab() may be be called after Py_Initialize()"); + Py_FatalError("PyImport_ExtendInittab() may not be called after Py_Initialize()"); } /* Count the number of entries in both tables */ From f23371fbc9bb283207ecebf8efd81a22538b4327 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Thu, 9 Feb 2023 17:15:19 +0000 Subject: [PATCH 062/247] LibFFI build requires x64 Cygwin, and skip the ARM build (GH-101743) --- PCbuild/prepare_libffi.bat | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PCbuild/prepare_libffi.bat b/PCbuild/prepare_libffi.bat index 7e7842a2fc97a4..ef36c36e058a15 100644 --- a/PCbuild/prepare_libffi.bat +++ b/PCbuild/prepare_libffi.bat @@ -60,7 +60,7 @@ goto :Usage if NOT DEFINED BUILD_X64 if NOT DEFINED BUILD_X86 if NOT DEFINED BUILD_ARM32 if NOT DEFINED BUILD_ARM64 ( set BUILD_X64=1 set BUILD_X86=1 - set BUILD_ARM32=1 + set BUILD_ARM32=0 set BUILD_ARM64=1 set COPY_LICENSE=1 ) @@ -204,7 +204,7 @@ if NOT DEFINED CYG_CACHE (set CYG_CACHE=C:/cygwin/var/cache/setup) if NOT DEFINED CYG_MIRROR (set CYG_MIRROR=http://mirrors.kernel.org/sourceware/cygwin/) powershell -c "md $env:CYG_ROOT -ErrorAction SilentlyContinue" -powershell -c "$setup = $env:CYG_ROOT+'/setup.exe'; if (!(Test-Path $setup)){invoke-webrequest https://cygwin.com/setup-x86.exe -outfile $setup} +powershell -c "$setup = $env:CYG_ROOT+'/setup.exe'; if (!(Test-Path $setup)){invoke-webrequest https://cygwin.com/setup-x86_64.exe -outfile $setup} %CYG_ROOT%/setup.exe -qnNdO -R "%CYG_ROOT%" -s "%CYG_MIRROR%" -l "%CYG_CACHE%" -P make -P autoconf -P automake -P libtool -P dejagnu endlocal From 6d92373f500eb81a175516b3abb16e21f0806c1f Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Thu, 9 Feb 2023 21:36:24 +0400 Subject: [PATCH 063/247] gh-101283: Fix 'versionchanged' for the shell=True fallback on Windows in 3.12 (GH-101728) --- Doc/library/subprocess.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst index d792a43eeb271f..ccc431b2d92e07 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -111,7 +111,7 @@ underlying :class:`Popen` interface can be used directly. Added the *text* parameter, as a more understandable alias of *universal_newlines*. Added the *capture_output* parameter. - .. versionchanged:: 3.11.3 + .. versionchanged:: 3.12 Changed Windows shell search order for ``shell=True``. The current directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and @@ -495,7 +495,7 @@ functions. *executable* parameter accepts a bytes and :term:`path-like object` on Windows. - .. versionchanged:: 3.11.3 + .. versionchanged:: 3.12 Changed Windows shell search order for ``shell=True``. The current directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and @@ -1174,7 +1174,7 @@ calls these functions. .. versionchanged:: 3.3 *timeout* was added. - .. versionchanged:: 3.11.3 + .. versionchanged:: 3.12 Changed Windows shell search order for ``shell=True``. The current directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and @@ -1214,7 +1214,7 @@ calls these functions. .. versionchanged:: 3.3 *timeout* was added. - .. versionchanged:: 3.11.3 + .. versionchanged:: 3.12 Changed Windows shell search order for ``shell=True``. The current directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and @@ -1277,7 +1277,7 @@ calls these functions. .. versionadded:: 3.7 *text* was added as a more readable alias for *universal_newlines*. - .. versionchanged:: 3.11.3 + .. versionchanged:: 3.12 Changed Windows shell search order for ``shell=True``. The current directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and From b41c47cd0606e8273aef4813e83fe2deaf9ab33b Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Thu, 9 Feb 2023 09:40:51 -0800 Subject: [PATCH 064/247] gh-101726: Update the OpenSSL version to 1.1.1t (GH-101727) Fixes CVE-2023-0286 (High) and a couple of Medium security issues. https://www.openssl.org/news/secadv/20230207.txt --- .azure-pipelines/ci.yml | 4 ++-- .azure-pipelines/pr.yml | 4 ++-- .github/workflows/build.yml | 6 +++--- Mac/BuildScript/build-installer.py | 6 +++--- .../Security/2023-02-08-22-03-04.gh-issue-101727.9P5eZz.rst | 4 ++++ PCbuild/get_externals.bat | 4 ++-- PCbuild/python.props | 4 ++-- PCbuild/readme.txt | 2 +- Tools/ssl/multissltests.py | 4 ++-- 9 files changed, 21 insertions(+), 17 deletions(-) create mode 100644 Misc/NEWS.d/next/Security/2023-02-08-22-03-04.gh-issue-101727.9P5eZz.rst diff --git a/.azure-pipelines/ci.yml b/.azure-pipelines/ci.yml index e45dc2d4365999..6302b547982118 100644 --- a/.azure-pipelines/ci.yml +++ b/.azure-pipelines/ci.yml @@ -57,7 +57,7 @@ jobs: variables: testRunTitle: '$(build.sourceBranchName)-linux' testRunPlatform: linux - openssl_version: 1.1.1q + openssl_version: 1.1.1t steps: - template: ./posix-steps.yml @@ -83,7 +83,7 @@ jobs: variables: testRunTitle: '$(Build.SourceBranchName)-linux-coverage' testRunPlatform: linux-coverage - openssl_version: 1.1.1q + openssl_version: 1.1.1t steps: - template: ./posix-steps.yml diff --git a/.azure-pipelines/pr.yml b/.azure-pipelines/pr.yml index af94ebf78c8488..5f7218768c18af 100644 --- a/.azure-pipelines/pr.yml +++ b/.azure-pipelines/pr.yml @@ -57,7 +57,7 @@ jobs: variables: testRunTitle: '$(system.pullRequest.TargetBranch)-linux' testRunPlatform: linux - openssl_version: 1.1.1q + openssl_version: 1.1.1t steps: - template: ./posix-steps.yml @@ -83,7 +83,7 @@ jobs: variables: testRunTitle: '$(Build.SourceBranchName)-linux-coverage' testRunPlatform: linux-coverage - openssl_version: 1.1.1q + openssl_version: 1.1.1t steps: - template: ./posix-steps.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f798992d8af61c..97ea2d94598e2c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -176,7 +176,7 @@ jobs: needs: check_source if: needs.check_source.outputs.run_tests == 'true' env: - OPENSSL_VER: 1.1.1s + OPENSSL_VER: 1.1.1t PYTHONSTRICTEXTENSIONBUILD: 1 steps: - uses: actions/checkout@v3 @@ -235,7 +235,7 @@ jobs: strategy: fail-fast: false matrix: - openssl_ver: [1.1.1s, 3.0.7, 3.1.0-beta1] + openssl_ver: [1.1.1t, 3.0.8, 3.1.0-beta1] env: OPENSSL_VER: ${{ matrix.openssl_ver }} MULTISSL_DIR: ${{ github.workspace }}/multissl @@ -282,7 +282,7 @@ jobs: needs: check_source if: needs.check_source.outputs.run_tests == 'true' env: - OPENSSL_VER: 1.1.1s + OPENSSL_VER: 1.1.1t PYTHONSTRICTEXTENSIONBUILD: 1 ASAN_OPTIONS: detect_leaks=0:allocator_may_return_null=1:handle_segv=0 steps: diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index cf97b5558c2ddc..048cb01379607e 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.1s", - url="https://www.openssl.org/source/openssl-1.1.1s.tar.gz", - checksum='c5ac01e760ee6ff0dab61d6b2bbd30146724d063eb322180c6f18a6f74e4b6aa', + name="OpenSSL 1.1.1t", + url="https://www.openssl.org/source/openssl-1.1.1t.tar.gz", + checksum='8dee9b24bdb1dcbf0c3d1e9b02fb8f6bf22165e807f45adeb7c9677536859d3b', buildrecipe=build_universal_openssl, configure=None, install=None, diff --git a/Misc/NEWS.d/next/Security/2023-02-08-22-03-04.gh-issue-101727.9P5eZz.rst b/Misc/NEWS.d/next/Security/2023-02-08-22-03-04.gh-issue-101727.9P5eZz.rst new file mode 100644 index 00000000000000..43acc82063fd7a --- /dev/null +++ b/Misc/NEWS.d/next/Security/2023-02-08-22-03-04.gh-issue-101727.9P5eZz.rst @@ -0,0 +1,4 @@ +Updated the OpenSSL version used in Windows and macOS binary release builds +to 1.1.1t to address CVE-2023-0286, CVE-2022-4303, and CVE-2022-4303 per +`the OpenSSL 2023-02-07 security advisory +`_. diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index 0a41d131a3e887..d4d96bd49d72c6 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.3 -if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1s +if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1t set libraries=%libraries% sqlite-3.39.4.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.13.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.13.0 @@ -77,7 +77,7 @@ echo.Fetching external binaries... set binaries= if NOT "%IncludeLibffi%"=="false" set binaries=%binaries% libffi-3.4.3 -if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.1s +if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.1t if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.13.0 if NOT "%IncludeSSLSrc%"=="false" set binaries=%binaries% nasm-2.11.06 diff --git a/PCbuild/python.props b/PCbuild/python.props index 57360e57baba66..5926c7ded4708d 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -74,8 +74,8 @@ $(ExternalsDir)libffi-3.4.3\ $(libffiDir)$(ArchName)\ $(libffiOutDir)include - $(ExternalsDir)openssl-1.1.1s\ - $(ExternalsDir)openssl-bin-1.1.1s\$(ArchName)\ + $(ExternalsDir)openssl-1.1.1t\ + $(ExternalsDir)openssl-bin-1.1.1t\$(ArchName)\ $(opensslOutDir)include $(ExternalsDir)\nasm-2.11.06\ $(ExternalsDir)\zlib-1.2.13\ diff --git a/PCbuild/readme.txt b/PCbuild/readme.txt index 3ed26a47b066b9..347be8aeeca398 100644 --- a/PCbuild/readme.txt +++ b/PCbuild/readme.txt @@ -169,7 +169,7 @@ _lzma Homepage: https://tukaani.org/xz/ _ssl - Python wrapper for version 1.1.1q of the OpenSSL secure sockets + Python wrapper for version 1.1.1t of the OpenSSL secure sockets library, which is downloaded from our binaries repository at https://github.com/python/cpython-bin-deps. diff --git a/Tools/ssl/multissltests.py b/Tools/ssl/multissltests.py index 5ad597c8347e56..c0fbee9ca6f98f 100755 --- a/Tools/ssl/multissltests.py +++ b/Tools/ssl/multissltests.py @@ -46,8 +46,8 @@ ] OPENSSL_RECENT_VERSIONS = [ - "1.1.1s", - "3.0.7" + "1.1.1t", + "3.0.8" ] LIBRESSL_OLD_VERSIONS = [ From 272da55affe6f2b3b73ff5474e1246089517d051 Mon Sep 17 00:00:00 2001 From: Ikko Eltociear Ashimine Date: Fri, 10 Feb 2023 02:45:58 +0900 Subject: [PATCH 065/247] Fix typo in `test_fstring.py` (#101600) --- Lib/test/test_fstring.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py index 318f38a6ed5b14..a50056da116e32 100644 --- a/Lib/test/test_fstring.py +++ b/Lib/test/test_fstring.py @@ -667,7 +667,7 @@ def test_missing_expression(self): "f'''{\t\f\r\n}'''", ]) - # Different error messeges are raised when a specfier ('!', ':' or '=') is used after an empty expression + # Different error messages are raised when a specfier ('!', ':' or '=') is used after an empty expression self.assertAllRaise(SyntaxError, "f-string: expression required before '!'", ["f'{!r}'", "f'{ !r}'", From f1f3af7b8245e61a2e0abef03b2c6c5902ed7df8 Mon Sep 17 00:00:00 2001 From: "Partha P. Mukherjee" Date: Thu, 9 Feb 2023 12:46:40 -0500 Subject: [PATCH 066/247] GH-101228: Fix typo in docstring for read method of `_io.TextIOWrapper` class (#101227) --- Modules/_io/textio.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 32ab8a44c62151..ea2ea32c336954 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -56,10 +56,10 @@ textiobase_detach(PyObject *self, PyObject *Py_UNUSED(ignored)) } PyDoc_STRVAR(textiobase_read_doc, - "Read at most n characters from stream.\n" + "Read at most size characters from stream.\n" "\n" - "Read from underlying buffer until we have n characters or we hit EOF.\n" - "If n is negative or omitted, read until EOF.\n" + "Read from underlying buffer until we have size characters or we hit EOF.\n" + "If size is negative or omitted, read until EOF.\n" ); static PyObject * From 5b946d371979a772120e6ee7d37f9b735769d433 Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Fri, 10 Feb 2023 08:30:03 +0900 Subject: [PATCH 067/247] gh-101430: Update tracemalloc to handle presize properly. (gh-101745) --- ...-02-10-01-15-57.gh-issue-101430.T3Gegb.rst | 2 ++ Modules/_tracemalloc.c | 24 +++++++------------ Objects/object.c | 9 ++----- 3 files changed, 12 insertions(+), 23 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-10-01-15-57.gh-issue-101430.T3Gegb.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-10-01-15-57.gh-issue-101430.T3Gegb.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-10-01-15-57.gh-issue-101430.T3Gegb.rst new file mode 100644 index 00000000000000..e617d85242144e --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-10-01-15-57.gh-issue-101430.T3Gegb.rst @@ -0,0 +1,2 @@ +Update :mod:`tracemalloc` to handle presize of object properly. Patch by +Dong-hee Na. diff --git a/Modules/_tracemalloc.c b/Modules/_tracemalloc.c index 9826ad2935beaa..d69c5636486da9 100644 --- a/Modules/_tracemalloc.c +++ b/Modules/_tracemalloc.c @@ -2,6 +2,7 @@ #include "pycore_fileutils.h" // _Py_write_noraise() #include "pycore_gc.h" // PyGC_Head #include "pycore_hashtable.h" // _Py_hashtable_t +#include "pycore_object.h" // _PyType_PreHeaderSize #include "pycore_pymem.h" // _Py_tracemalloc_config #include "pycore_runtime.h" // _Py_ID() #include "pycore_traceback.h" @@ -1400,20 +1401,16 @@ _tracemalloc__get_object_traceback(PyObject *module, PyObject *obj) /*[clinic end generated code: output=41ee0553a658b0aa input=29495f1b21c53212]*/ { PyTypeObject *type; - void *ptr; traceback_t *traceback; type = Py_TYPE(obj); - if (PyType_IS_GC(type)) { - ptr = (void *)((char *)obj - sizeof(PyGC_Head)); - } - else { - ptr = (void *)obj; - } + const size_t presize = _PyType_PreHeaderSize(type); + uintptr_t ptr = (uintptr_t)((char *)obj - presize); - traceback = tracemalloc_get_traceback(DEFAULT_DOMAIN, (uintptr_t)ptr); - if (traceback == NULL) + traceback = tracemalloc_get_traceback(DEFAULT_DOMAIN, ptr); + if (traceback == NULL) { Py_RETURN_NONE; + } return traceback_to_pyobject(traceback, NULL); } @@ -1723,14 +1720,9 @@ _PyTraceMalloc_NewReference(PyObject *op) return -1; } - uintptr_t ptr; PyTypeObject *type = Py_TYPE(op); - if (PyType_IS_GC(type)) { - ptr = (uintptr_t)((char *)op - sizeof(PyGC_Head)); - } - else { - ptr = (uintptr_t)op; - } + const size_t presize = _PyType_PreHeaderSize(type); + uintptr_t ptr = (uintptr_t)((char *)op - presize); int res = -1; diff --git a/Objects/object.c b/Objects/object.c index 7817c04ef5f5be..446c7b1f5f0302 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -2387,14 +2387,9 @@ _PyObject_AssertFailed(PyObject *obj, const char *expr, const char *msg, /* Display the traceback where the object has been allocated. Do it before dumping repr(obj), since repr() is more likely to crash than dumping the traceback. */ - void *ptr; PyTypeObject *type = Py_TYPE(obj); - if (_PyType_IS_GC(type)) { - ptr = (void *)((char *)obj - sizeof(PyGC_Head)); - } - else { - ptr = (void *)obj; - } + const size_t presize = _PyType_PreHeaderSize(type); + void *ptr = (void *)((char *)obj - presize); _PyMem_DumpTraceback(fileno(stderr), ptr); /* This might succeed or fail, but we're about to abort, so at least From 34c50ceb1e2d40f7faab673d2033ecaafd3c611a Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Fri, 10 Feb 2023 06:00:58 +0300 Subject: [PATCH 068/247] gh-101747: Fix refleak in new `OrderedDict` repr (GH-101748) --- Objects/odictobject.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Objects/odictobject.c b/Objects/odictobject.c index ab2bbed35873de..215a8af54fb266 100644 --- a/Objects/odictobject.c +++ b/Objects/odictobject.c @@ -1385,6 +1385,7 @@ odict_repr(PyODictObject *self) result = PyUnicode_FromFormat("%s(%R)", _PyType_Name(Py_TYPE(self)), dcopy); + Py_DECREF(dcopy); Done: Py_ReprLeave((PyObject *)self); From 448c7d154e72506158d0a7a766e9f1cb8adf3dec Mon Sep 17 00:00:00 2001 From: abel1502 <32196516+abel1502@users.noreply.github.com> Date: Fri, 10 Feb 2023 06:10:46 +0300 Subject: [PATCH 069/247] Fix some typos in asdl_c.py (GH-101757) --- Parser/asdl_c.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Parser/asdl_c.py b/Parser/asdl_c.py index 3e307610b635f4..db0e597b7a5aa4 100755 --- a/Parser/asdl_c.py +++ b/Parser/asdl_c.py @@ -73,7 +73,7 @@ def reflow_c_string(s, depth): def is_simple(sum_type): """Return True if a sum is a simple. - A sum is simple if it's types have no fields and itself + A sum is simple if its types have no fields and itself doesn't have any attributes. Instances of these types are cached at C level, and they act like singletons when propagating parser generated nodes into Python level, e.g. @@ -352,7 +352,7 @@ def visitSum(self, sum, name): self.visit(t, name, sum.attributes) def get_args(self, fields): - """Return list of C argument into, one for each field. + """Return list of C argument info, one for each field. Argument info is 3-tuple of a C type, variable name, and flag that is true if type can be NULL. From d40a23c0a11060ba7fa076d50980c18a11a13a40 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Fri, 10 Feb 2023 08:25:02 +0100 Subject: [PATCH 070/247] gh-101759: Update macOS installer to SQLite 3.40.1 (#101761) --- Mac/BuildScript/build-installer.py | 6 +++--- .../macOS/2023-02-09-22-07-17.gh-issue-101759.B0JP2H.rst | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/macOS/2023-02-09-22-07-17.gh-issue-101759.B0JP2H.rst diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index 048cb01379607e..5ea2cfc5183ef5 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -359,9 +359,9 @@ def library_recipes(): ), ), dict( - name="SQLite 3.39.4", - url="https://sqlite.org/2022/sqlite-autoconf-3390400.tar.gz", - checksum="44b7e6691b0954086f717a6c43b622a5", + name="SQLite 3.40.1", + url="https://sqlite.org/2022/sqlite-autoconf-3400100.tar.gz", + checksum="5498af3a357753d473ee713e363fa5b7", extra_cflags=('-Os ' '-DSQLITE_ENABLE_FTS5 ' '-DSQLITE_ENABLE_FTS4 ' diff --git a/Misc/NEWS.d/next/macOS/2023-02-09-22-07-17.gh-issue-101759.B0JP2H.rst b/Misc/NEWS.d/next/macOS/2023-02-09-22-07-17.gh-issue-101759.B0JP2H.rst new file mode 100644 index 00000000000000..fc53d08bffc4fd --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2023-02-09-22-07-17.gh-issue-101759.B0JP2H.rst @@ -0,0 +1 @@ +Update macOS installer to SQLite 3.40.1. From 826bf0e6957fd0addc321d1baee1fa846e9457eb Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Fri, 10 Feb 2023 12:58:14 +0100 Subject: [PATCH 071/247] gh-101277: Finalise isolating itertools (GH-101305) Add repeat, islice, chain, tee, teedataobject, and batched types to module state. Automerge-Triggered-By: GH:erlend-aasland --- ...-01-25-00-14-52.gh-issue-101277.FceHX7.rst | 2 + Modules/clinic/itertoolsmodule.c.h | 6 +- Modules/itertoolsmodule.c | 534 +++++++----------- Tools/c-analyzer/cpython/globals-to-fix.tsv | 22 - 4 files changed, 220 insertions(+), 344 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-01-25-00-14-52.gh-issue-101277.FceHX7.rst diff --git a/Misc/NEWS.d/next/Library/2023-01-25-00-14-52.gh-issue-101277.FceHX7.rst b/Misc/NEWS.d/next/Library/2023-01-25-00-14-52.gh-issue-101277.FceHX7.rst new file mode 100644 index 00000000000000..e09c0e09fb388f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-25-00-14-52.gh-issue-101277.FceHX7.rst @@ -0,0 +1,2 @@ +Remove global state from :mod:`itertools` module (:pep:`687`). Patches by +Erlend E. Aasland. diff --git a/Modules/clinic/itertoolsmodule.c.h b/Modules/clinic/itertoolsmodule.c.h index d15d5f0890ca98..32278bf715aa98 100644 --- a/Modules/clinic/itertoolsmodule.c.h +++ b/Modules/clinic/itertoolsmodule.c.h @@ -232,7 +232,7 @@ static PyObject * itertools_teedataobject(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; - PyTypeObject *base_tp = &teedataobject_type; + PyTypeObject *base_tp = clinic_state()->teedataobject_type; PyObject *it; PyObject *values; PyObject *next; @@ -270,7 +270,7 @@ static PyObject * itertools__tee(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; - PyTypeObject *base_tp = &tee_type; + PyTypeObject *base_tp = clinic_state()->tee_type; PyObject *iterable; if ((type == base_tp || type->tp_init == base_tp->tp_init) && @@ -913,4 +913,4 @@ itertools_count(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=a08b58d7dac825da input=a9049054013a1b77]*/ +/*[clinic end generated code: output=111cbd102c2a23c9 input=a9049054013a1b77]*/ diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index ce8720d0fd9228..6986695e47b1ae 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -5,6 +5,7 @@ #include "pycore_moduleobject.h" // _PyModule_GetState() #include "pycore_object.h" // _PyObject_GC_TRACK() #include "pycore_tuple.h" // _PyTuple_ITEMS() +#include "structmember.h" // PyMemberDef #include // offsetof() /* Itertools module written and maintained @@ -13,6 +14,8 @@ typedef struct { PyTypeObject *accumulate_type; + PyTypeObject *batched_type; + PyTypeObject *chain_type; PyTypeObject *combinations_type; PyTypeObject *compress_type; PyTypeObject *count_type; @@ -22,11 +25,15 @@ typedef struct { PyTypeObject *filterfalse_type; PyTypeObject *groupby_type; PyTypeObject *_grouper_type; + PyTypeObject *islice_type; PyTypeObject *pairwise_type; PyTypeObject *permutations_type; PyTypeObject *product_type; + PyTypeObject *repeat_type; PyTypeObject *starmap_type; PyTypeObject *takewhile_type; + PyTypeObject *tee_type; + PyTypeObject *teedataobject_type; PyTypeObject *ziplongest_type; } itertools_state; @@ -60,14 +67,14 @@ find_state_by_type(PyTypeObject *tp) module itertools class itertools.groupby "groupbyobject *" "clinic_state()->groupby_type" class itertools._grouper "_grouperobject *" "clinic_state()->_grouper_type" -class itertools.teedataobject "teedataobject *" "&teedataobject_type" -class itertools._tee "teeobject *" "&tee_type" -class itertools.batched "batchedobject *" "&batched_type" +class itertools.teedataobject "teedataobject *" "clinic_state()->teedataobject_type" +class itertools._tee "teeobject *" "clinic_state()->tee_type" +class itertools.batched "batchedobject *" "clinic_state()->batched_type" class itertools.cycle "cycleobject *" "clinic_state()->cycle_type" class itertools.dropwhile "dropwhileobject *" "clinic_state()->dropwhile_type" class itertools.takewhile "takewhileobject *" "clinic_state()->takewhile_type" class itertools.starmap "starmapobject *" "clinic_state()->starmap_type" -class itertools.chain "chainobject *" "&chain_type" +class itertools.chain "chainobject *" "clinic_state()->chain_type" class itertools.combinations "combinationsobject *" "clinic_state()->combinations_type" class itertools.combinations_with_replacement "cwr_object *" "clinic_state()->cwr_type" class itertools.permutations "permutationsobject *" "clinic_state()->permutations_type" @@ -77,11 +84,7 @@ class itertools.filterfalse "filterfalseobject *" "clinic_state()->filterfalse_t class itertools.count "countobject *" "clinic_state()->count_type" class itertools.pairwise "pairwiseobject *" "clinic_state()->pairwise_type" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=28ffff5c0c93eed7]*/ - -static PyTypeObject teedataobject_type; -static PyTypeObject tee_type; -static PyTypeObject batched_type; +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=aa48fe4de9d4080f]*/ #define clinic_state() (find_state_by_type(type)) #define clinic_state_by_cls() (get_module_state_by_cls(base_tp)) @@ -162,17 +165,18 @@ batched_new_impl(PyTypeObject *type, PyObject *iterable, Py_ssize_t n) static void batched_dealloc(batchedobject *bo) { + PyTypeObject *tp = Py_TYPE(bo); PyObject_GC_UnTrack(bo); Py_XDECREF(bo->it); - Py_TYPE(bo)->tp_free(bo); + tp->tp_free(bo); + Py_DECREF(tp); } static int batched_traverse(batchedobject *bo, visitproc visit, void *arg) { - if (bo->it != NULL) { - Py_VISIT(bo->it); - } + Py_VISIT(Py_TYPE(bo)); + Py_VISIT(bo->it); return 0; } @@ -222,48 +226,25 @@ batched_next(batchedobject *bo) return result; } -static PyTypeObject batched_type = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) - "itertools.batched", /* tp_name */ - sizeof(batchedobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)batched_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - batched_new__doc__, /* tp_doc */ - (traverseproc)batched_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)batched_next, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - PyType_GenericAlloc, /* tp_alloc */ - batched_new, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Slot batched_slots[] = { + {Py_tp_dealloc, batched_dealloc}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_doc, (void *)batched_new__doc__}, + {Py_tp_traverse, batched_traverse}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, batched_next}, + {Py_tp_alloc, PyType_GenericAlloc}, + {Py_tp_new, batched_new}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec batched_spec = { + .name = "itertools.batched", + .basicsize = sizeof(batchedobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = batched_slots, }; @@ -737,14 +718,15 @@ typedef struct { teedataobject *dataobj; int index; /* 0 <= index <= LINKCELLS */ PyObject *weakreflist; + itertools_state *state; } teeobject; static PyObject * -teedataobject_newinternal(PyObject *it) +teedataobject_newinternal(itertools_state *state, PyObject *it) { teedataobject *tdo; - tdo = PyObject_GC_New(teedataobject, &teedataobject_type); + tdo = PyObject_GC_New(teedataobject, state->teedataobject_type); if (tdo == NULL) return NULL; @@ -757,10 +739,10 @@ teedataobject_newinternal(PyObject *it) } static PyObject * -teedataobject_jumplink(teedataobject *tdo) +teedataobject_jumplink(itertools_state *state, teedataobject *tdo) { if (tdo->nextlink == NULL) - tdo->nextlink = teedataobject_newinternal(tdo->it); + tdo->nextlink = teedataobject_newinternal(state, tdo->it); return Py_XNewRef(tdo->nextlink); } @@ -796,6 +778,7 @@ teedataobject_traverse(teedataobject *tdo, visitproc visit, void * arg) { int i; + Py_VISIT(Py_TYPE(tdo)); Py_VISIT(tdo->it); for (i = 0; i < tdo->numread; i++) Py_VISIT(tdo->values[i]); @@ -804,9 +787,9 @@ teedataobject_traverse(teedataobject *tdo, visitproc visit, void * arg) } static void -teedataobject_safe_decref(PyObject *obj) +teedataobject_safe_decref(PyObject *obj, PyTypeObject *tdo_type) { - while (obj && Py_IS_TYPE(obj, &teedataobject_type) && + while (obj && Py_IS_TYPE(obj, tdo_type) && Py_REFCNT(obj) == 1) { PyObject *nextlink = ((teedataobject *)obj)->nextlink; ((teedataobject *)obj)->nextlink = NULL; @@ -826,16 +809,19 @@ teedataobject_clear(teedataobject *tdo) Py_CLEAR(tdo->values[i]); tmp = tdo->nextlink; tdo->nextlink = NULL; - teedataobject_safe_decref(tmp); + itertools_state *state = get_module_state_by_cls(Py_TYPE(tdo)); + teedataobject_safe_decref(tmp, state->teedataobject_type); return 0; } static void teedataobject_dealloc(teedataobject *tdo) { + PyTypeObject *tp = Py_TYPE(tdo); PyObject_GC_UnTrack(tdo); teedataobject_clear(tdo); PyObject_GC_Del(tdo); + Py_DECREF(tp); } static PyObject * @@ -874,9 +860,10 @@ itertools_teedataobject_impl(PyTypeObject *type, PyObject *it, teedataobject *tdo; Py_ssize_t i, len; - assert(type == &teedataobject_type); + itertools_state *state = get_module_state_by_cls(type); + assert(type == state->teedataobject_type); - tdo = (teedataobject *)teedataobject_newinternal(it); + tdo = (teedataobject *)teedataobject_newinternal(state, it); if (!tdo) return NULL; @@ -892,7 +879,7 @@ itertools_teedataobject_impl(PyTypeObject *type, PyObject *it, if (len == LINKCELLS) { if (next != Py_None) { - if (!Py_IS_TYPE(next, &teedataobject_type)) + if (!Py_IS_TYPE(next, state->teedataobject_type)) goto err; assert(tdo->nextlink == NULL); tdo->nextlink = Py_NewRef(next); @@ -915,47 +902,24 @@ static PyMethodDef teedataobject_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyTypeObject teedataobject_type = { - PyVarObject_HEAD_INIT(0, 0) /* Must fill in type value later */ - "itertools._tee_dataobject", /* tp_name */ - sizeof(teedataobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)teedataobject_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - itertools_teedataobject__doc__, /* tp_doc */ - (traverseproc)teedataobject_traverse, /* tp_traverse */ - (inquiry)teedataobject_clear, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - teedataobject_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - itertools_teedataobject, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Slot teedataobject_slots[] = { + {Py_tp_dealloc, teedataobject_dealloc}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_doc, (void *)itertools_teedataobject__doc__}, + {Py_tp_traverse, teedataobject_traverse}, + {Py_tp_clear, teedataobject_clear}, + {Py_tp_methods, teedataobject_methods}, + {Py_tp_new, itertools_teedataobject}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec teedataobject_spec = { + .name = "itertools._tee_dataobject", + .basicsize = sizeof(teedataobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = teedataobject_slots, }; @@ -965,7 +929,7 @@ tee_next(teeobject *to) PyObject *value, *link; if (to->index >= LINKCELLS) { - link = teedataobject_jumplink(to->dataobj); + link = teedataobject_jumplink(to->state, to->dataobj); if (link == NULL) return NULL; Py_SETREF(to->dataobj, (teedataobject *)link); @@ -981,6 +945,7 @@ tee_next(teeobject *to) static int tee_traverse(teeobject *to, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(to)); Py_VISIT((PyObject *)to->dataobj); return 0; } @@ -990,12 +955,13 @@ tee_copy(teeobject *to, PyObject *Py_UNUSED(ignored)) { teeobject *newto; - newto = PyObject_GC_New(teeobject, &tee_type); + newto = PyObject_GC_New(teeobject, Py_TYPE(to)); if (newto == NULL) return NULL; newto->dataobj = (teedataobject*)Py_NewRef(to->dataobj); newto->index = to->index; newto->weakreflist = NULL; + newto->state = to->state; PyObject_GC_Track(newto); return (PyObject *)newto; } @@ -1003,7 +969,7 @@ tee_copy(teeobject *to, PyObject *Py_UNUSED(ignored)) PyDoc_STRVAR(teecopy_doc, "Returns an independent iterator."); static PyObject * -tee_fromiterable(PyObject *iterable) +tee_fromiterable(itertools_state *state, PyObject *iterable) { teeobject *to; PyObject *it; @@ -1011,17 +977,17 @@ tee_fromiterable(PyObject *iterable) it = PyObject_GetIter(iterable); if (it == NULL) return NULL; - if (PyObject_TypeCheck(it, &tee_type)) { + if (PyObject_TypeCheck(it, state->tee_type)) { to = (teeobject *)tee_copy((teeobject *)it, NULL); goto done; } - PyObject *dataobj = teedataobject_newinternal(it); + PyObject *dataobj = teedataobject_newinternal(state, it); if (!dataobj) { to = NULL; goto done; } - to = PyObject_GC_New(teeobject, &tee_type); + to = PyObject_GC_New(teeobject, state->tee_type); if (to == NULL) { Py_DECREF(dataobj); goto done; @@ -1029,6 +995,7 @@ tee_fromiterable(PyObject *iterable) to->dataobj = (teedataobject *)dataobj; to->index = 0; to->weakreflist = NULL; + to->state = state; PyObject_GC_Track(to); done: Py_DECREF(it); @@ -1047,7 +1014,8 @@ static PyObject * itertools__tee_impl(PyTypeObject *type, PyObject *iterable) /*[clinic end generated code: output=b02d3fd26c810c3f input=adc0779d2afe37a2]*/ { - return tee_fromiterable(iterable); + itertools_state *state = get_module_state_by_cls(type); + return tee_fromiterable(state, iterable); } static int @@ -1062,9 +1030,11 @@ tee_clear(teeobject *to) static void tee_dealloc(teeobject *to) { + PyTypeObject *tp = Py_TYPE(to); PyObject_GC_UnTrack(to); tee_clear(to); PyObject_GC_Del(to); + Py_DECREF(tp); } static PyObject * @@ -1082,7 +1052,8 @@ tee_setstate(teeobject *to, PyObject *state) PyErr_SetString(PyExc_TypeError, "state is not a tuple"); return NULL; } - if (!PyArg_ParseTuple(state, "O!i", &teedataobject_type, &tdo, &index)) { + PyTypeObject *tdo_type = to->state->teedataobject_type; + if (!PyArg_ParseTuple(state, "O!i", tdo_type, &tdo, &index)) { return NULL; } if (index < 0 || index > LINKCELLS) { @@ -1102,47 +1073,31 @@ static PyMethodDef tee_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyTypeObject tee_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "itertools._tee", /* tp_name */ - sizeof(teeobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)tee_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - itertools__tee__doc__, /* tp_doc */ - (traverseproc)tee_traverse, /* tp_traverse */ - (inquiry)tee_clear, /* tp_clear */ - 0, /* tp_richcompare */ - offsetof(teeobject, weakreflist), /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)tee_next, /* tp_iternext */ - tee_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - itertools__tee, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyMemberDef tee_members[] = { + {"__weaklistoffset__", T_PYSSIZET, offsetof(teeobject, weakreflist), READONLY}, + {NULL}, +}; + +static PyType_Slot tee_slots[] = { + {Py_tp_dealloc, tee_dealloc}, + {Py_tp_doc, (void *)itertools__tee__doc__}, + {Py_tp_traverse, tee_traverse}, + {Py_tp_clear, tee_clear}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, tee_next}, + {Py_tp_methods, tee_methods}, + {Py_tp_members, tee_members}, + {Py_tp_new, itertools__tee}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec tee_spec = { + .name = "itertools._tee", + .basicsize = sizeof(teeobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = tee_slots, }; /*[clinic input] @@ -1184,7 +1139,8 @@ itertools_tee_impl(PyObject *module, PyObject *iterable, Py_ssize_t n) copyable = it; } else { - copyable = tee_fromiterable(it); + itertools_state *state = get_module_state(module); + copyable = tee_fromiterable(state, it); Py_DECREF(it); if (copyable == NULL) { Py_DECREF(result); @@ -1682,8 +1638,6 @@ typedef struct { Py_ssize_t cnt; } isliceobject; -static PyTypeObject islice_type; - static PyObject * islice_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { @@ -1693,7 +1647,9 @@ islice_new(PyTypeObject *type, PyObject *args, PyObject *kwds) Py_ssize_t numargs; isliceobject *lz; - if ((type == &islice_type || type->tp_init == islice_type.tp_init) && + itertools_state *st = find_state_by_type(type); + PyTypeObject *islice_type = st->islice_type; + if ((type == islice_type || type->tp_init == islice_type->tp_init) && !_PyArg_NoKeywords("islice", kwds)) return NULL; @@ -1772,14 +1728,17 @@ islice_new(PyTypeObject *type, PyObject *args, PyObject *kwds) static void islice_dealloc(isliceobject *lz) { + PyTypeObject *tp = Py_TYPE(lz); PyObject_GC_UnTrack(lz); Py_XDECREF(lz->it); - Py_TYPE(lz)->tp_free(lz); + tp->tp_free(lz); + Py_DECREF(tp); } static int islice_traverse(isliceobject *lz, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(lz)); Py_VISIT(lz->it); return 0; } @@ -1885,48 +1844,25 @@ specified as another value, step determines how many values are\n\ skipped between successive calls. Works like a slice() on a list\n\ but returns an iterator."); -static PyTypeObject islice_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "itertools.islice", /* tp_name */ - sizeof(isliceobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)islice_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - islice_doc, /* tp_doc */ - (traverseproc)islice_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)islice_next, /* tp_iternext */ - islice_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - islice_new, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Slot islice_slots[] = { + {Py_tp_dealloc, islice_dealloc}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_doc, (void *)islice_doc}, + {Py_tp_traverse, islice_traverse}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, islice_next}, + {Py_tp_methods, islice_methods}, + {Py_tp_new, islice_new}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec islice_spec = { + .name = "itertools.islice", + .basicsize = sizeof(isliceobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = islice_slots, }; @@ -2056,8 +1992,6 @@ typedef struct { PyObject *active; /* Currently running input iterator */ } chainobject; -static PyTypeObject chain_type; - static PyObject * chain_new_internal(PyTypeObject *type, PyObject *source) { @@ -2079,7 +2013,9 @@ chain_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyObject *source; - if ((type == &chain_type || type->tp_init == chain_type.tp_init) && + itertools_state *state = find_state_by_type(type); + PyTypeObject *chain_type = state->chain_type; + if ((type == chain_type || type->tp_init == chain_type->tp_init) && !_PyArg_NoKeywords("chain", kwds)) return NULL; @@ -2114,15 +2050,18 @@ itertools_chain_from_iterable(PyTypeObject *type, PyObject *arg) static void chain_dealloc(chainobject *lz) { + PyTypeObject *tp = Py_TYPE(lz); PyObject_GC_UnTrack(lz); Py_XDECREF(lz->active); Py_XDECREF(lz->source); - Py_TYPE(lz)->tp_free(lz); + tp->tp_free(lz); + Py_DECREF(tp); } static int chain_traverse(chainobject *lz, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(lz)); Py_VISIT(lz->source); Py_VISIT(lz->active); return 0; @@ -2227,48 +2166,25 @@ static PyMethodDef chain_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyTypeObject chain_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "itertools.chain", /* tp_name */ - sizeof(chainobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)chain_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - chain_doc, /* tp_doc */ - (traverseproc)chain_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)chain_next, /* tp_iternext */ - chain_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - chain_new, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Slot chain_slots[] = { + {Py_tp_dealloc, chain_dealloc}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_doc, (void *)chain_doc}, + {Py_tp_traverse, chain_traverse}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, chain_next}, + {Py_tp_methods, chain_methods}, + {Py_tp_new, chain_new}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec chain_spec = { + .name = "itertools.chain", + .basicsize = sizeof(chainobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = chain_slots, }; @@ -3574,6 +3490,7 @@ typedef struct { PyObject *it; PyObject *binop; PyObject *initial; + itertools_state *state; } accumulateobject; /*[clinic input] @@ -3612,6 +3529,7 @@ itertools_accumulate_impl(PyTypeObject *type, PyObject *iterable, lz->total = NULL; lz->it = it; lz->initial = Py_XNewRef(initial); + lz->state = find_state_by_type(type); return (PyObject *)lz; } @@ -3674,13 +3592,13 @@ accumulate_next(accumulateobject *lz) static PyObject * accumulate_reduce(accumulateobject *lz, PyObject *Py_UNUSED(ignored)) { + itertools_state *state = lz->state; + if (lz->initial != Py_None) { PyObject *it; assert(lz->total == NULL); - if (PyType_Ready(&chain_type) < 0) - return NULL; - it = PyObject_CallFunction((PyObject *)&chain_type, "(O)O", + it = PyObject_CallFunction((PyObject *)(state->chain_type), "(O)O", lz->initial, lz->it); if (it == NULL) return NULL; @@ -3690,11 +3608,7 @@ accumulate_reduce(accumulateobject *lz, PyObject *Py_UNUSED(ignored)) if (lz->total == Py_None) { PyObject *it; - if (PyType_Ready(&chain_type) < 0) - return NULL; - if (PyType_Ready(&islice_type) < 0) - return NULL; - it = PyObject_CallFunction((PyObject *)&chain_type, "(O)O", + it = PyObject_CallFunction((PyObject *)(state->chain_type), "(O)O", lz->total, lz->it); if (it == NULL) return NULL; @@ -3702,7 +3616,8 @@ accumulate_reduce(accumulateobject *lz, PyObject *Py_UNUSED(ignored)) it, lz->binop ? lz->binop : Py_None); if (it == NULL) return NULL; - return Py_BuildValue("O(NiO)", &islice_type, it, 1, Py_None); + + return Py_BuildValue("O(NiO)", state->islice_type, it, 1, Py_None); } return Py_BuildValue("O(OO)O", Py_TYPE(lz), lz->it, lz->binop?lz->binop:Py_None, @@ -4261,8 +4176,6 @@ typedef struct { Py_ssize_t cnt; } repeatobject; -static PyTypeObject repeat_type; - static PyObject * repeat_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { @@ -4292,14 +4205,17 @@ repeat_new(PyTypeObject *type, PyObject *args, PyObject *kwds) static void repeat_dealloc(repeatobject *ro) { + PyTypeObject *tp = Py_TYPE(ro); PyObject_GC_UnTrack(ro); Py_XDECREF(ro->element); - Py_TYPE(ro)->tp_free(ro); + tp->tp_free(ro); + Py_DECREF(tp); } static int repeat_traverse(repeatobject *ro, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(ro)); Py_VISIT(ro->element); return 0; } @@ -4361,48 +4277,26 @@ PyDoc_STRVAR(repeat_doc, for the specified number of times. If not specified, returns the object\n\ endlessly."); -static PyTypeObject repeat_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "itertools.repeat", /* tp_name */ - sizeof(repeatobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)repeat_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - (reprfunc)repeat_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - repeat_doc, /* tp_doc */ - (traverseproc)repeat_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)repeat_next, /* tp_iternext */ - repeat_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - repeat_new, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Slot repeat_slots[] = { + {Py_tp_dealloc, repeat_dealloc}, + {Py_tp_repr, repeat_repr}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_doc, (void *)repeat_doc}, + {Py_tp_traverse, repeat_traverse}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, repeat_next}, + {Py_tp_methods, repeat_methods}, + {Py_tp_new, repeat_new}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec repeat_spec = { + .name = "itertools.repeat", + .basicsize = sizeof(repeatobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = repeat_slots, }; @@ -4695,6 +4589,8 @@ itertoolsmodule_traverse(PyObject *mod, visitproc visit, void *arg) { itertools_state *state = get_module_state(mod); Py_VISIT(state->accumulate_type); + Py_VISIT(state->batched_type); + Py_VISIT(state->chain_type); Py_VISIT(state->combinations_type); Py_VISIT(state->compress_type); Py_VISIT(state->count_type); @@ -4704,11 +4600,15 @@ itertoolsmodule_traverse(PyObject *mod, visitproc visit, void *arg) Py_VISIT(state->filterfalse_type); Py_VISIT(state->groupby_type); Py_VISIT(state->_grouper_type); + Py_VISIT(state->islice_type); Py_VISIT(state->pairwise_type); Py_VISIT(state->permutations_type); Py_VISIT(state->product_type); + Py_VISIT(state->repeat_type); Py_VISIT(state->starmap_type); Py_VISIT(state->takewhile_type); + Py_VISIT(state->tee_type); + Py_VISIT(state->teedataobject_type); Py_VISIT(state->ziplongest_type); return 0; } @@ -4718,6 +4618,8 @@ itertoolsmodule_clear(PyObject *mod) { itertools_state *state = get_module_state(mod); Py_CLEAR(state->accumulate_type); + Py_CLEAR(state->batched_type); + Py_CLEAR(state->chain_type); Py_CLEAR(state->combinations_type); Py_CLEAR(state->compress_type); Py_CLEAR(state->count_type); @@ -4727,11 +4629,15 @@ itertoolsmodule_clear(PyObject *mod) Py_CLEAR(state->filterfalse_type); Py_CLEAR(state->groupby_type); Py_CLEAR(state->_grouper_type); + Py_CLEAR(state->islice_type); Py_CLEAR(state->pairwise_type); Py_CLEAR(state->permutations_type); Py_CLEAR(state->product_type); + Py_CLEAR(state->repeat_type); Py_CLEAR(state->starmap_type); Py_CLEAR(state->takewhile_type); + Py_CLEAR(state->tee_type); + Py_CLEAR(state->teedataobject_type); Py_CLEAR(state->ziplongest_type); return 0; } @@ -4758,6 +4664,8 @@ itertoolsmodule_exec(PyObject *mod) { itertools_state *state = get_module_state(mod); ADD_TYPE(mod, state->accumulate_type, &accumulate_spec); + ADD_TYPE(mod, state->batched_type, &batched_spec); + ADD_TYPE(mod, state->chain_type, &chain_spec); ADD_TYPE(mod, state->combinations_type, &combinations_spec); ADD_TYPE(mod, state->compress_type, &compress_spec); ADD_TYPE(mod, state->count_type, &count_spec); @@ -4767,30 +4675,18 @@ itertoolsmodule_exec(PyObject *mod) ADD_TYPE(mod, state->filterfalse_type, &filterfalse_spec); ADD_TYPE(mod, state->groupby_type, &groupby_spec); ADD_TYPE(mod, state->_grouper_type, &_grouper_spec); + ADD_TYPE(mod, state->islice_type, &islice_spec); ADD_TYPE(mod, state->pairwise_type, &pairwise_spec); ADD_TYPE(mod, state->permutations_type, &permutations_spec); ADD_TYPE(mod, state->product_type, &product_spec); + ADD_TYPE(mod, state->repeat_type, &repeat_spec); ADD_TYPE(mod, state->starmap_type, &starmap_spec); ADD_TYPE(mod, state->takewhile_type, &takewhile_spec); + ADD_TYPE(mod, state->tee_type, &tee_spec); + ADD_TYPE(mod, state->teedataobject_type, &teedataobject_spec); ADD_TYPE(mod, state->ziplongest_type, &ziplongest_spec); - PyTypeObject *typelist[] = { - &batched_type, - &islice_type, - &chain_type, - &repeat_type, - &tee_type, - &teedataobject_type - }; - - Py_SET_TYPE(&teedataobject_type, &PyType_Type); - - for (size_t i = 0; i < Py_ARRAY_LENGTH(typelist); i++) { - if (PyModule_AddType(mod, typelist[i]) < 0) { - return -1; - } - } - + Py_SET_TYPE(state->teedataobject_type, &PyType_Type); return 0; } diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index cf2d5c368f1bda..52ea0b4901d4bb 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -335,28 +335,6 @@ Modules/_testcapi/vectorcall.c - MethodDescriptorBase_Type - Modules/_testcapi/vectorcall.c - MethodDescriptorDerived_Type - Modules/_testcapi/vectorcall.c - MethodDescriptorNopGet_Type - Modules/_testcapi/vectorcall.c - MethodDescriptor2_Type - -Modules/itertoolsmodule.c - _grouper_type - -Modules/itertoolsmodule.c - accumulate_type - -Modules/itertoolsmodule.c - batched_type - -Modules/itertoolsmodule.c - chain_type - -Modules/itertoolsmodule.c - combinations_type - -Modules/itertoolsmodule.c - compress_type - -Modules/itertoolsmodule.c - count_type - -Modules/itertoolsmodule.c - cwr_type - -Modules/itertoolsmodule.c - cycle_type - -Modules/itertoolsmodule.c - dropwhile_type - -Modules/itertoolsmodule.c - filterfalse_type - -Modules/itertoolsmodule.c - groupby_type - -Modules/itertoolsmodule.c - islice_type - -Modules/itertoolsmodule.c - pairwise_type - -Modules/itertoolsmodule.c - permutations_type - -Modules/itertoolsmodule.c - product_type - -Modules/itertoolsmodule.c - repeat_type - -Modules/itertoolsmodule.c - starmap_type - -Modules/itertoolsmodule.c - takewhile_type - -Modules/itertoolsmodule.c - tee_type - -Modules/itertoolsmodule.c - teedataobject_type - -Modules/itertoolsmodule.c - ziplongest_type - ################################## From 5d15224011217487e1a174c144af0e5f5826c17c Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Fri, 10 Feb 2023 17:38:26 +0100 Subject: [PATCH 072/247] gh-101759: Update Windows installer to SQLite 3.40.1 (#101762) --- .../next/Windows/2023-02-09-22-09-27.gh-issue-101759.zFlqSH.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-02-09-22-09-27.gh-issue-101759.zFlqSH.rst diff --git a/Misc/NEWS.d/next/Windows/2023-02-09-22-09-27.gh-issue-101759.zFlqSH.rst b/Misc/NEWS.d/next/Windows/2023-02-09-22-09-27.gh-issue-101759.zFlqSH.rst new file mode 100644 index 00000000000000..62bcac34397d2e --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-02-09-22-09-27.gh-issue-101759.zFlqSH.rst @@ -0,0 +1 @@ +Update Windows installer to SQLite 3.40.1. diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index d4d96bd49d72c6..2c424517eae4b1 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.3 if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1t -set libraries=%libraries% sqlite-3.39.4.0 +set libraries=%libraries% sqlite-3.40.1.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.13.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.13.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tix-8.4.3.6 diff --git a/PCbuild/python.props b/PCbuild/python.props index 5926c7ded4708d..28ee74d8594759 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -68,7 +68,7 @@ - $(ExternalsDir)sqlite-3.39.4.0\ + $(ExternalsDir)sqlite-3.40.1.0\ $(ExternalsDir)bzip2-1.0.8\ $(ExternalsDir)xz-5.2.5\ $(ExternalsDir)libffi-3.4.3\ diff --git a/PCbuild/readme.txt b/PCbuild/readme.txt index 347be8aeeca398..4c799b64c461c1 100644 --- a/PCbuild/readme.txt +++ b/PCbuild/readme.txt @@ -188,7 +188,7 @@ _ssl again when building. _sqlite3 - Wraps SQLite 3.39.4, which is itself built by sqlite3.vcxproj + Wraps SQLite 3.40.1, which is itself built by sqlite3.vcxproj Homepage: https://www.sqlite.org/ _tkinter From 366b94905869d680b3f1d4801fb497e78811e511 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Fri, 10 Feb 2023 16:49:29 +0000 Subject: [PATCH 073/247] gh-101517: make bdb avoid looking up in linecache with lineno=None (#101787) --- Lib/bdb.py | 7 ++++--- Lib/test/test_bdb.py | 6 ++++++ .../Library/2023-02-10-16-02-29.gh-issue-101517.r7S2u8.rst | 1 + 3 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-02-10-16-02-29.gh-issue-101517.r7S2u8.rst diff --git a/Lib/bdb.py b/Lib/bdb.py index 81fbb8514acb6f..7f9b09514ffd00 100644 --- a/Lib/bdb.py +++ b/Lib/bdb.py @@ -570,9 +570,10 @@ def format_stack_entry(self, frame_lineno, lprefix=': '): rv = frame.f_locals['__return__'] s += '->' s += reprlib.repr(rv) - line = linecache.getline(filename, lineno, frame.f_globals) - if line: - s += lprefix + line.strip() + if lineno is not None: + line = linecache.getline(filename, lineno, frame.f_globals) + if line: + s += lprefix + line.strip() return s # The following methods can be called by clients to use diff --git a/Lib/test/test_bdb.py b/Lib/test/test_bdb.py index 87a5ac308a12df..042c2daea7f797 100644 --- a/Lib/test/test_bdb.py +++ b/Lib/test/test_bdb.py @@ -1203,5 +1203,11 @@ def main(): tracer.runcall(tfunc_import) +class TestRegressions(unittest.TestCase): + def test_format_stack_entry_no_lineno(self): + # See gh-101517 + Bdb().format_stack_entry((sys._getframe(), None)) + + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2023-02-10-16-02-29.gh-issue-101517.r7S2u8.rst b/Misc/NEWS.d/next/Library/2023-02-10-16-02-29.gh-issue-101517.r7S2u8.rst new file mode 100644 index 00000000000000..a5f6bdfa5ac2f0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-10-16-02-29.gh-issue-101517.r7S2u8.rst @@ -0,0 +1 @@ +Fixed bug where :mod:`bdb` looks up the source line with :mod:`linecache` with a ``lineno=None``, which causes it to fail with an unhandled exception. From e1aadedf099e645fd2eb1aa8bdcde5a105cee95d Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Fri, 10 Feb 2023 16:57:30 +0000 Subject: [PATCH 074/247] gh-101763: Update bundled copy of libffi to 3.4.4 on Windows (GH-101784) --- .../Windows/2023-02-10-14-26-05.gh-issue-101763.RPaj7r.rst | 1 + PCbuild/get_externals.bat | 4 ++-- PCbuild/python.props | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2023-02-10-14-26-05.gh-issue-101763.RPaj7r.rst diff --git a/Misc/NEWS.d/next/Windows/2023-02-10-14-26-05.gh-issue-101763.RPaj7r.rst b/Misc/NEWS.d/next/Windows/2023-02-10-14-26-05.gh-issue-101763.RPaj7r.rst new file mode 100644 index 00000000000000..e7e5a73afeb532 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-02-10-14-26-05.gh-issue-101763.RPaj7r.rst @@ -0,0 +1 @@ +Updates copy of libffi bundled with Windows installs to 3.4.4. diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index 2c424517eae4b1..128241393f9f09 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -52,7 +52,7 @@ echo.Fetching external libraries... set libraries= set libraries=%libraries% bzip2-1.0.8 -if NOT "%IncludeLibffiSrc%"=="false" set libraries=%libraries% libffi-3.4.3 +if NOT "%IncludeLibffiSrc%"=="false" set libraries=%libraries% libffi-3.4.4 if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1t set libraries=%libraries% sqlite-3.40.1.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.13.0 @@ -76,7 +76,7 @@ for %%e in (%libraries%) do ( echo.Fetching external binaries... set binaries= -if NOT "%IncludeLibffi%"=="false" set binaries=%binaries% libffi-3.4.3 +if NOT "%IncludeLibffi%"=="false" set binaries=%binaries% libffi-3.4.4 if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.1t if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.13.0 if NOT "%IncludeSSLSrc%"=="false" set binaries=%binaries% nasm-2.11.06 diff --git a/PCbuild/python.props b/PCbuild/python.props index 28ee74d8594759..7994fbe7cd5e0b 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -71,7 +71,7 @@ $(ExternalsDir)sqlite-3.40.1.0\ $(ExternalsDir)bzip2-1.0.8\ $(ExternalsDir)xz-5.2.5\ - $(ExternalsDir)libffi-3.4.3\ + $(ExternalsDir)libffi-3.4.4\ $(libffiDir)$(ArchName)\ $(libffiOutDir)include $(ExternalsDir)openssl-1.1.1t\ From 2037ebf81bd4bbe5421421b822bd57cfd665a1e9 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Fri, 10 Feb 2023 18:54:04 +0100 Subject: [PATCH 075/247] Docs: use parameter list for sqlite3.Cursor.execute* (#101782) Co-authored-by: Alex Waygood --- Doc/library/sqlite3.rst | 45 ++++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index bbdc891c930cf4..8ffc0aad91995c 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -1418,15 +1418,22 @@ Cursor objects .. method:: execute(sql, parameters=(), /) - Execute SQL statement *sql*. - Bind values to the statement using :ref:`placeholders - ` that map to the :term:`sequence` or :class:`dict` - *parameters*. + Execute SQL a single SQL statement, + optionally binding Python values using + :ref:`placeholders `. - :meth:`execute` will only execute a single SQL statement. If you try to execute - more than one statement with it, it will raise a :exc:`ProgrammingError`. Use - :meth:`executescript` if you want to execute multiple SQL statements with one - call. + :param str sql: + A single SQL statement. + + :param parameters: + Python values to bind to placeholders in *sql*. + A :class:`!dict` if named placeholders are used. + A :term:`!sequence` if unnamed placeholders are used. + See :ref:`sqlite3-placeholders`. + :type parameters: :class:`dict` | :term:`sequence` + + :raises ProgrammingError: + If *sql* contains more than one SQL statement. If :attr:`~Connection.autocommit` is :data:`LEGACY_TRANSACTION_CONTROL`, @@ -1435,15 +1442,29 @@ Cursor objects and there is no open transaction, a transaction is implicitly opened before executing *sql*. + Use :meth:`executescript` to execute multiple SQL statements. .. method:: executemany(sql, parameters, /) - Execute :ref:`parameterized ` SQL statement *sql* - against all parameter sequences or mappings found in the sequence - *parameters*. It is also possible to use an - :term:`iterator` yielding parameters instead of a sequence. + For every item in *parameters*, + repeatedly execute the :ref:`parameterized ` + SQL statement *sql*. + Uses the same implicit transaction handling as :meth:`~Cursor.execute`. + :param str sql: + A single SQL :abbr:`DML (Data Manipulation Language)` statement. + + :param parameters: + An :term:`!iterable` of parameters to bind with + the placeholders in *sql*. + See :ref:`sqlite3-placeholders`. + :type parameters: :term:`iterable` + + :raises ProgrammingError: + If *sql* contains more than one SQL statement, + or is not a DML statment. + Example: .. testcode:: sqlite3.cursor From 61f2be08661949e2f6dfc94143436297e60d47de Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 10 Feb 2023 20:46:12 +0200 Subject: [PATCH 076/247] Docs: Fix getstatus() -> getcode() typos (#101296) --- Doc/library/http.client.rst | 2 +- Doc/library/urllib.request.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/http.client.rst b/Doc/library/http.client.rst index 48582219695b41..ad3416135e307b 100644 --- a/Doc/library/http.client.rst +++ b/Doc/library/http.client.rst @@ -532,7 +532,7 @@ statement. .. deprecated:: 3.9 Deprecated in favor of :attr:`~HTTPResponse.headers`. -.. method:: HTTPResponse.getstatus() +.. method:: HTTPResponse.getcode() .. deprecated:: 3.9 Deprecated in favor of :attr:`~HTTPResponse.status`. diff --git a/Doc/library/urllib.request.rst b/Doc/library/urllib.request.rst index 59e1f2da828a83..64cc9c388ec30d 100644 --- a/Doc/library/urllib.request.rst +++ b/Doc/library/urllib.request.rst @@ -1630,7 +1630,7 @@ The typical response object is a :class:`urllib.response.addinfourl` instance: .. deprecated:: 3.9 Deprecated in favor of :attr:`~addinfourl.status`. - .. method:: getstatus() + .. method:: getcode() .. deprecated:: 3.9 Deprecated in favor of :attr:`~addinfourl.status`. From 17143e2c30ae5e51945e04eeaec7ebb0e1f07fb5 Mon Sep 17 00:00:00 2001 From: busywhitespace Date: Sat, 11 Feb 2023 00:29:24 +0100 Subject: [PATCH 077/247] gh-101390: Fix docs for `imporlib.util.LazyLoader.factory` to properly call it a class method (GH-101391) --- 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 3fc1531c0cdf19..89efa64c6b5203 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -1387,7 +1387,7 @@ an :term:`importer`. .. classmethod:: factory(loader) - A static method which returns a callable that creates a lazy loader. This + A class method which returns a callable that creates a lazy loader. This is meant to be used in situations where the loader is passed by class instead of by instance. :: From b652d40f1c88fcd8595cd401513f6b7f8e499471 Mon Sep 17 00:00:00 2001 From: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> Date: Sat, 11 Feb 2023 14:07:39 +0530 Subject: [PATCH 078/247] GH-101797: allocate `PyExpat_CAPI` capsule on heap (#101798) --- Modules/pyexpat.c | 72 +++++++++++++++++++++++++++++------------------ 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/Modules/pyexpat.c b/Modules/pyexpat.c index 63a3392d5efe7d..0a744998b6c514 100644 --- a/Modules/pyexpat.c +++ b/Modules/pyexpat.c @@ -1878,6 +1878,18 @@ add_features(PyObject *mod) } #endif +static void +pyexpat_capsule_destructor(PyObject *capsule) +{ + void *p = PyCapsule_GetPointer(capsule, PyExpat_CAPSULE_NAME); + if (p == NULL) { + PyErr_WriteUnraisable(capsule); + return; + } + PyMem_Free(p); +} + + static int pyexpat_exec(PyObject *mod) { @@ -1965,40 +1977,46 @@ pyexpat_exec(PyObject *mod) MYCONST(XML_PARAM_ENTITY_PARSING_ALWAYS); #undef MYCONST - static struct PyExpat_CAPI capi; + struct PyExpat_CAPI *capi = PyMem_Malloc(sizeof(*capi)); + if (capi == NULL) { + PyErr_NoMemory(); + return -1; + } /* initialize pyexpat dispatch table */ - capi.size = sizeof(capi); - capi.magic = PyExpat_CAPI_MAGIC; - capi.MAJOR_VERSION = XML_MAJOR_VERSION; - capi.MINOR_VERSION = XML_MINOR_VERSION; - capi.MICRO_VERSION = XML_MICRO_VERSION; - capi.ErrorString = XML_ErrorString; - capi.GetErrorCode = XML_GetErrorCode; - capi.GetErrorColumnNumber = XML_GetErrorColumnNumber; - capi.GetErrorLineNumber = XML_GetErrorLineNumber; - capi.Parse = XML_Parse; - capi.ParserCreate_MM = XML_ParserCreate_MM; - capi.ParserFree = XML_ParserFree; - capi.SetCharacterDataHandler = XML_SetCharacterDataHandler; - capi.SetCommentHandler = XML_SetCommentHandler; - capi.SetDefaultHandlerExpand = XML_SetDefaultHandlerExpand; - capi.SetElementHandler = XML_SetElementHandler; - capi.SetNamespaceDeclHandler = XML_SetNamespaceDeclHandler; - capi.SetProcessingInstructionHandler = XML_SetProcessingInstructionHandler; - capi.SetUnknownEncodingHandler = XML_SetUnknownEncodingHandler; - capi.SetUserData = XML_SetUserData; - capi.SetStartDoctypeDeclHandler = XML_SetStartDoctypeDeclHandler; - capi.SetEncoding = XML_SetEncoding; - capi.DefaultUnknownEncodingHandler = PyUnknownEncodingHandler; + capi->size = sizeof(*capi); + capi->magic = PyExpat_CAPI_MAGIC; + capi->MAJOR_VERSION = XML_MAJOR_VERSION; + capi->MINOR_VERSION = XML_MINOR_VERSION; + capi->MICRO_VERSION = XML_MICRO_VERSION; + capi->ErrorString = XML_ErrorString; + capi->GetErrorCode = XML_GetErrorCode; + capi->GetErrorColumnNumber = XML_GetErrorColumnNumber; + capi->GetErrorLineNumber = XML_GetErrorLineNumber; + capi->Parse = XML_Parse; + capi->ParserCreate_MM = XML_ParserCreate_MM; + capi->ParserFree = XML_ParserFree; + capi->SetCharacterDataHandler = XML_SetCharacterDataHandler; + capi->SetCommentHandler = XML_SetCommentHandler; + capi->SetDefaultHandlerExpand = XML_SetDefaultHandlerExpand; + capi->SetElementHandler = XML_SetElementHandler; + capi->SetNamespaceDeclHandler = XML_SetNamespaceDeclHandler; + capi->SetProcessingInstructionHandler = XML_SetProcessingInstructionHandler; + capi->SetUnknownEncodingHandler = XML_SetUnknownEncodingHandler; + capi->SetUserData = XML_SetUserData; + capi->SetStartDoctypeDeclHandler = XML_SetStartDoctypeDeclHandler; + capi->SetEncoding = XML_SetEncoding; + capi->DefaultUnknownEncodingHandler = PyUnknownEncodingHandler; #if XML_COMBINED_VERSION >= 20100 - capi.SetHashSalt = XML_SetHashSalt; + capi->SetHashSalt = XML_SetHashSalt; #else - capi.SetHashSalt = NULL; + capi->SetHashSalt = NULL; #endif /* export using capsule */ - PyObject *capi_object = PyCapsule_New(&capi, PyExpat_CAPSULE_NAME, NULL); + PyObject *capi_object = PyCapsule_New(capi, PyExpat_CAPSULE_NAME, + pyexpat_capsule_destructor); if (capi_object == NULL) { + PyMem_Free(capi); return -1; } From 3eb12df8b526aa5a2ca6b43f21a1c5e7d38ee634 Mon Sep 17 00:00:00 2001 From: mjoerg Date: Sat, 11 Feb 2023 16:34:15 +0100 Subject: [PATCH 079/247] Fix typo in test_fstring.py (#101823) --- Lib/test/test_fstring.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py index a50056da116e32..b3f6ef41d77b8f 100644 --- a/Lib/test/test_fstring.py +++ b/Lib/test/test_fstring.py @@ -667,7 +667,7 @@ def test_missing_expression(self): "f'''{\t\f\r\n}'''", ]) - # Different error messages are raised when a specfier ('!', ':' or '=') is used after an empty expression + # Different error messages are raised when a specifier ('!', ':' or '=') is used after an empty expression self.assertAllRaise(SyntaxError, "f-string: expression required before '!'", ["f'{!r}'", "f'{ !r}'", From 1d194235e4d5981b5fea25c75318d61189103a58 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 11 Feb 2023 20:54:28 -0800 Subject: [PATCH 080/247] gh-89792: Prevent test_tools from copying 1000M of "source" in freeze test (#101837) Prevent test_tools from copying 1000M of "source" It doesn't need a git repo, just the checkout. We skip .git metadata, Doc/build, Doc/venv, and `__pycache__` subdirs, that developers often have in their clients to reduce the size of the source tree copy ten-fold. This should significantly reduce IO and presumably time on buildbots during this long test. --- .../2023-02-11-20-28-08.gh-issue-89792.S-Y5BZ.rst | 3 +++ Tools/freeze/test/freeze.py | 14 +++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Tests/2023-02-11-20-28-08.gh-issue-89792.S-Y5BZ.rst diff --git a/Misc/NEWS.d/next/Tests/2023-02-11-20-28-08.gh-issue-89792.S-Y5BZ.rst b/Misc/NEWS.d/next/Tests/2023-02-11-20-28-08.gh-issue-89792.S-Y5BZ.rst new file mode 100644 index 00000000000000..a3a3070d7f3790 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-02-11-20-28-08.gh-issue-89792.S-Y5BZ.rst @@ -0,0 +1,3 @@ +``test_tools`` now copies up to 10x less source data to a temporary +directory during the ``freeze`` test by ignoring git metadata and other +artifacts. diff --git a/Tools/freeze/test/freeze.py b/Tools/freeze/test/freeze.py index ddbfd7fc9c2f41..0ae983b15c98f0 100644 --- a/Tools/freeze/test/freeze.py +++ b/Tools/freeze/test/freeze.py @@ -80,7 +80,19 @@ def copy_source_tree(newroot, oldroot): if newroot == SRCDIR: raise Exception('this probably isn\'t what you wanted') shutil.rmtree(newroot) - shutil.copytree(oldroot, 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) if os.path.exists(os.path.join(newroot, 'Makefile')): _run_quiet([MAKE, 'clean'], newroot) From da2fb9264315dc30ac3012b4dbf5ba76d3f34433 Mon Sep 17 00:00:00 2001 From: Soumendra Ganguly <67527439+8vasu@users.noreply.github.com> Date: Sat, 11 Feb 2023 23:24:43 -0600 Subject: [PATCH 081/247] gh-85984: Utilize new "winsize" functions from termios in pty tests. (#101831) Utilize new functions termios.tcgetwinsize() and termios.tcsetwinsize in test_pty.py. Signed-off-by: Soumendra Ganguly Co-authored-by: Gregory P. Smith --- Lib/test/test_pty.py | 96 ++++++------------- ...3-02-11-22-36-10.gh-issue-85984.EVXjT9.rst | 1 + 2 files changed, 28 insertions(+), 69 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2023-02-11-22-36-10.gh-issue-85984.EVXjT9.rst diff --git a/Lib/test/test_pty.py b/Lib/test/test_pty.py index fa0dbcc16f3ce8..c723bb362c5d87 100644 --- a/Lib/test/test_pty.py +++ b/Lib/test/test_pty.py @@ -3,6 +3,8 @@ # Skip these tests if termios or fcntl are not available import_module('termios') +# fcntl is a proxy for not being one of the wasm32 platforms even though we +# don't use this module... a proper check for what crashes those is needed. import_module("fcntl") import errno @@ -15,20 +17,12 @@ import socket import io # readline import unittest - -import struct -import fcntl import warnings TEST_STRING_1 = b"I wish to buy a fish license.\n" TEST_STRING_2 = b"For my pet fish, Eric.\n" -try: - _TIOCGWINSZ = tty.TIOCGWINSZ - _TIOCSWINSZ = tty.TIOCSWINSZ - _HAVE_WINSZ = True -except AttributeError: - _HAVE_WINSZ = False +_HAVE_WINSZ = hasattr(tty, "TIOCGWINSZ") and hasattr(tty, "TIOCSWINSZ") if verbose: def debug(msg): @@ -82,14 +76,6 @@ def expectedFailureIfStdinIsTTY(fun): pass return fun -def _get_term_winsz(fd): - s = struct.pack("HHHH", 0, 0, 0, 0) - return fcntl.ioctl(fd, _TIOCGWINSZ, s) - -def _set_term_winsz(fd, winsz): - fcntl.ioctl(fd, _TIOCSWINSZ, winsz) - - # Marginal testing of pty suite. Cannot do extensive 'do or fail' testing # because pty code is not too portable. class PtyTest(unittest.TestCase): @@ -105,18 +91,14 @@ def setUp(self): self.addCleanup(signal.alarm, 0) signal.alarm(10) - # Save original stdin window size - self.stdin_rows = None - self.stdin_cols = None + # Save original stdin window size. + self.stdin_dim = None if _HAVE_WINSZ: try: - stdin_dim = os.get_terminal_size(pty.STDIN_FILENO) - self.stdin_rows = stdin_dim.lines - self.stdin_cols = stdin_dim.columns - old_stdin_winsz = struct.pack("HHHH", self.stdin_rows, - self.stdin_cols, 0, 0) - self.addCleanup(_set_term_winsz, pty.STDIN_FILENO, old_stdin_winsz) - except OSError: + self.stdin_dim = tty.tcgetwinsize(pty.STDIN_FILENO) + self.addCleanup(tty.tcsetwinsize, pty.STDIN_FILENO, + self.stdin_dim) + except tty.error: pass def handle_sig(self, sig, frame): @@ -131,41 +113,40 @@ def test_openpty(self): try: mode = tty.tcgetattr(pty.STDIN_FILENO) except tty.error: - # not a tty or bad/closed fd + # Not a tty or bad/closed fd. debug("tty.tcgetattr(pty.STDIN_FILENO) failed") mode = None - new_stdin_winsz = None - if self.stdin_rows is not None and self.stdin_cols is not None: + new_dim = None + if self.stdin_dim: try: # Modify pty.STDIN_FILENO window size; we need to # check if pty.openpty() is able to set pty slave # window size accordingly. - debug("Setting pty.STDIN_FILENO window size") - debug(f"original size: (rows={self.stdin_rows}, cols={self.stdin_cols})") - target_stdin_rows = self.stdin_rows + 1 - target_stdin_cols = self.stdin_cols + 1 - debug(f"target size: (rows={target_stdin_rows}, cols={target_stdin_cols})") - target_stdin_winsz = struct.pack("HHHH", target_stdin_rows, - target_stdin_cols, 0, 0) - _set_term_winsz(pty.STDIN_FILENO, target_stdin_winsz) + debug("Setting pty.STDIN_FILENO window size.") + debug(f"original size: (row, col) = {self.stdin_dim}") + target_dim = (self.stdin_dim[0] + 1, self.stdin_dim[1] + 1) + debug(f"target size: (row, col) = {target_dim}") + tty.tcsetwinsize(pty.STDIN_FILENO, target_dim) # Were we able to set the window size # of pty.STDIN_FILENO successfully? - new_stdin_winsz = _get_term_winsz(pty.STDIN_FILENO) - self.assertEqual(new_stdin_winsz, target_stdin_winsz, + new_dim = tty.tcgetwinsize(pty.STDIN_FILENO) + self.assertEqual(new_dim, target_dim, "pty.STDIN_FILENO window size unchanged") except OSError: - warnings.warn("Failed to set pty.STDIN_FILENO window size") + warnings.warn("Failed to set pty.STDIN_FILENO window size.") pass try: debug("Calling pty.openpty()") try: - master_fd, slave_fd = pty.openpty(mode, new_stdin_winsz) + master_fd, slave_fd, slave_name = pty.openpty(mode, new_dim, + True) except TypeError: master_fd, slave_fd = pty.openpty() - debug(f"Got master_fd '{master_fd}', slave_fd '{slave_fd}'") + slave_name = None + debug(f"Got {master_fd=}, {slave_fd=}, {slave_name=}") except OSError: # " An optional feature could not be imported " ... ? raise unittest.SkipTest("Pseudo-terminals (seemingly) not functional.") @@ -181,8 +162,8 @@ def test_openpty(self): if mode: self.assertEqual(tty.tcgetattr(slave_fd), mode, "openpty() failed to set slave termios") - if new_stdin_winsz: - self.assertEqual(_get_term_winsz(slave_fd), new_stdin_winsz, + if new_dim: + self.assertEqual(tty.tcgetwinsize(slave_fd), new_dim, "openpty() failed to set slave window size") # Ensure the fd is non-blocking in case there's nothing to read. @@ -367,9 +348,8 @@ def _socketpair(self): self.files.extend(socketpair) return socketpair - def _mock_select(self, rfds, wfds, xfds, timeout=0): + def _mock_select(self, rfds, wfds, xfds): # This will raise IndexError when no more expected calls exist. - # This ignores the timeout self.assertEqual(self.select_rfds_lengths.pop(0), len(rfds)) return self.select_rfds_results.pop(0), [], [] @@ -409,28 +389,6 @@ def test__copy_to_each(self): self.assertEqual(os.read(read_from_stdout_fd, 20), b'from master') self.assertEqual(os.read(masters[1], 20), b'from stdin') - def test__copy_eof_on_all(self): - """Test the empty read EOF case on both master_fd and stdin.""" - read_from_stdout_fd, mock_stdout_fd = self._pipe() - pty.STDOUT_FILENO = mock_stdout_fd - mock_stdin_fd, write_to_stdin_fd = self._pipe() - pty.STDIN_FILENO = mock_stdin_fd - socketpair = self._socketpair() - masters = [s.fileno() for s in socketpair] - - socketpair[1].close() - os.close(write_to_stdin_fd) - - pty.select = self._mock_select - self.select_rfds_lengths.append(2) - self.select_rfds_results.append([mock_stdin_fd, masters[0]]) - # We expect that both fds were removed from the fds list as they - # both encountered an EOF before the second select call. - self.select_rfds_lengths.append(0) - - # We expect the function to return without error. - self.assertEqual(pty._copy(masters[0]), None) - def test__restore_tty_mode_normal_return(self): """Test that spawn resets the tty mode no when _copy returns normally.""" diff --git a/Misc/NEWS.d/next/Tests/2023-02-11-22-36-10.gh-issue-85984.EVXjT9.rst b/Misc/NEWS.d/next/Tests/2023-02-11-22-36-10.gh-issue-85984.EVXjT9.rst new file mode 100644 index 00000000000000..402f99ea6c6ebf --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-02-11-22-36-10.gh-issue-85984.EVXjT9.rst @@ -0,0 +1 @@ +Utilize new "winsize" functions from termios in pty tests. From dfc2e065a2e71011017077e549cd2f9bf4944c54 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 11 Feb 2023 22:07:52 -0800 Subject: [PATCH 082/247] gh-89792: Limit test_tools freeze test build parallelism based on the number of cores (#101841) unhardcode freeze test build parallelism. base it on the number of cpus, don't use more than max(2, os.cpu_count()/3). --- .../2023-02-11-20-28-08.gh-issue-89792.S-Y5BZ.rst | 7 ++++--- Tools/freeze/test/freeze.py | 15 ++++++++++++--- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Misc/NEWS.d/next/Tests/2023-02-11-20-28-08.gh-issue-89792.S-Y5BZ.rst b/Misc/NEWS.d/next/Tests/2023-02-11-20-28-08.gh-issue-89792.S-Y5BZ.rst index a3a3070d7f3790..9de278919ef2f8 100644 --- a/Misc/NEWS.d/next/Tests/2023-02-11-20-28-08.gh-issue-89792.S-Y5BZ.rst +++ b/Misc/NEWS.d/next/Tests/2023-02-11-20-28-08.gh-issue-89792.S-Y5BZ.rst @@ -1,3 +1,4 @@ -``test_tools`` now copies up to 10x less source data to a temporary -directory during the ``freeze`` test by ignoring git metadata and other -artifacts. +``test_tools`` now copies up to 10x less source data to a temporary directory +during the ``freeze`` test by ignoring git metadata and other artifacts. It +also limits its python build parallelism based on os.cpu_count instead of hard +coding it as 8 cores. diff --git a/Tools/freeze/test/freeze.py b/Tools/freeze/test/freeze.py index 0ae983b15c98f0..b4c76ff36a873b 100644 --- a/Tools/freeze/test/freeze.py +++ b/Tools/freeze/test/freeze.py @@ -163,16 +163,25 @@ def prepare(script=None, outdir=None): if not MAKE: raise UnsupportedError('make') + cores = os.cpu_count() + if cores and cores >= 3: + # 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}' + else: + parallel = '-j2' + # Build python. - print(f'building python in {builddir}...') + 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, '-j8']) + _run_quiet([MAKE, '-C', builddir, parallel]) # Install the build. print(f'installing python into {prefix}...') - _run_quiet([MAKE, '-C', builddir, '-j8', 'install']) + _run_quiet([MAKE, '-C', builddir, 'install']) python = os.path.join(prefix, 'bin', 'python3') return outdir, scriptfile, python From 6ef6915d3530e844243893f91bf4bd702dfef570 Mon Sep 17 00:00:00 2001 From: Jean Abou-Samra Date: Sun, 12 Feb 2023 15:20:11 +0100 Subject: [PATCH 083/247] gh-101845: pyspecific: Fix i18n for availability directive (GH-101846) pyspecific: Fix i18n for availability directive If the directive has content, the previous code would nest paragraph nodes from that content inside a general paragraph node, which confuses Sphinx and leads it to drop the content when translating. Instead, use a container node for the body. Also use set_source_info so that any warnings have location info. --- Doc/tools/extensions/pyspecific.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/Doc/tools/extensions/pyspecific.py b/Doc/tools/extensions/pyspecific.py index db7bb3b44219d2..d659a4a54b9d11 100644 --- a/Doc/tools/extensions/pyspecific.py +++ b/Doc/tools/extensions/pyspecific.py @@ -28,6 +28,7 @@ from sphinx.environment import NoUri from sphinx.locale import _ as sphinx_gettext from sphinx.util import status_iterator, logging +from sphinx.util.docutils import SphinxDirective from sphinx.util.nodes import split_explicit_title from sphinx.writers.text import TextWriter, TextTranslator @@ -119,7 +120,7 @@ def run(self): # Support for documenting platform availability -class Availability(Directive): +class Availability(SphinxDirective): has_content = True required_arguments = 1 @@ -139,18 +140,19 @@ class Availability(Directive): def run(self): availability_ref = ':ref:`Availability `: ' + avail_nodes, avail_msgs = self.state.inline_text( + availability_ref + self.arguments[0], + self.lineno) pnode = nodes.paragraph(availability_ref + self.arguments[0], - classes=["availability"],) - n, m = self.state.inline_text(availability_ref, self.lineno) - pnode.extend(n + m) - n, m = self.state.inline_text(self.arguments[0], self.lineno) - pnode.extend(n + m) + '', *avail_nodes, *avail_msgs) + self.set_source_info(pnode) + cnode = nodes.container("", pnode, classes=["availability"]) + self.set_source_info(cnode) if self.content: - self.state.nested_parse(self.content, self.content_offset, pnode) - + self.state.nested_parse(self.content, self.content_offset, cnode) self.parse_platforms() - return [pnode] + return [cnode] def parse_platforms(self): """Parse platform information from arguments From a1f08f5f19753c7c9295f51b5ae1262c7a1c838f Mon Sep 17 00:00:00 2001 From: Steve Kowalik Date: Mon, 13 Feb 2023 20:11:43 +1100 Subject: [PATCH 084/247] Correct trivial grammar in reset_mock docs (#101861) --- Doc/library/unittest.mock.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/library/unittest.mock.rst b/Doc/library/unittest.mock.rst index e009f303fef317..d6d8e5e9557d5c 100644 --- a/Doc/library/unittest.mock.rst +++ b/Doc/library/unittest.mock.rst @@ -406,7 +406,7 @@ the *new_callable* argument to :func:`patch`. False .. versionchanged:: 3.6 - Added two keyword only argument to the reset_mock function. + Added two keyword-only arguments to the reset_mock function. This can be useful where you want to make a series of assertions that reuse the same object. Note that :meth:`reset_mock` *doesn't* clear the @@ -416,8 +416,8 @@ the *new_callable* argument to :func:`patch`. parameter as ``True``. Child mocks and the return value mock (if any) are reset as well. - .. note:: *return_value*, and :attr:`side_effect` are keyword only - argument. + .. note:: *return_value*, and :attr:`side_effect` are keyword-only + arguments. .. method:: mock_add_spec(spec, spec_set=False) From 160f2fe2b90ed5ec7838cb4141dd35768422891f Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 13 Feb 2023 11:24:55 +0000 Subject: [PATCH 085/247] GH-87849: Simplify stack effect of SEND and specialize it for generators and coroutines. (GH-101788) --- Include/internal/pycore_code.h | 7 ++ Include/internal/pycore_opcode.h | 5 +- Include/opcode.h | 1 + Lib/dis.py | 9 +- Lib/importlib/_bootstrap_external.py | 3 +- Lib/opcode.py | 8 +- Lib/test/test_dis.py | 25 +++--- ...3-02-10-15-54-57.gh-issue-87849.IUVvPz.rst | 3 + Objects/frameobject.c | 6 +- Python/bytecodes.c | 88 ++++++++++-------- Python/compile.c | 2 + Python/generated_cases.c.h | 89 +++++++++++-------- Python/opcode_metadata.h | 11 ++- Python/opcode_targets.h | 2 +- Python/specialize.c | 25 +++++- 15 files changed, 185 insertions(+), 99 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-10-15-54-57.gh-issue-87849.IUVvPz.rst diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index a287250acc1912..10f1e320a12ff4 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -92,6 +92,12 @@ typedef struct { #define INLINE_CACHE_ENTRIES_FOR_ITER CACHE_ENTRIES(_PyForIterCache) +typedef struct { + uint16_t counter; +} _PySendCache; + +#define INLINE_CACHE_ENTRIES_SEND CACHE_ENTRIES(_PySendCache) + // Borrowed references to common callables: struct callable_cache { PyObject *isinstance; @@ -233,6 +239,7 @@ extern void _Py_Specialize_CompareAndBranch(PyObject *lhs, PyObject *rhs, extern void _Py_Specialize_UnpackSequence(PyObject *seq, _Py_CODEUNIT *instr, int oparg); extern void _Py_Specialize_ForIter(PyObject *iter, _Py_CODEUNIT *instr, int oparg); +extern void _Py_Specialize_Send(PyObject *receiver, _Py_CODEUNIT *instr); /* Finalizer function for static codeobjects used in deepfreeze.py */ extern void _PyStaticCode_Fini(PyCodeObject *co); diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h index 47c84721335196..5e65adee9e00a5 100644 --- a/Include/internal/pycore_opcode.h +++ b/Include/internal/pycore_opcode.h @@ -50,6 +50,7 @@ const uint8_t _PyOpcode_Caches[256] = { [COMPARE_OP] = 1, [LOAD_GLOBAL] = 5, [BINARY_OP] = 1, + [SEND] = 1, [COMPARE_AND_BRANCH] = 1, [CALL] = 4, }; @@ -196,6 +197,7 @@ const uint8_t _PyOpcode_Deopt[256] = { [RETURN_GENERATOR] = RETURN_GENERATOR, [RETURN_VALUE] = RETURN_VALUE, [SEND] = SEND, + [SEND_GEN] = SEND, [SETUP_ANNOTATIONS] = SETUP_ANNOTATIONS, [SET_ADD] = SET_ADD, [SET_UPDATE] = SET_UPDATE, @@ -395,7 +397,7 @@ static const char *const _PyOpcode_OpName[263] = { [SET_UPDATE] = "SET_UPDATE", [DICT_MERGE] = "DICT_MERGE", [DICT_UPDATE] = "DICT_UPDATE", - [166] = "<166>", + [SEND_GEN] = "SEND_GEN", [167] = "<167>", [168] = "<168>", [169] = "<169>", @@ -496,7 +498,6 @@ static const char *const _PyOpcode_OpName[263] = { #endif #define EXTRA_CASES \ - case 166: \ case 167: \ case 168: \ case 169: \ diff --git a/Include/opcode.h b/Include/opcode.h index 77ad7c22440d72..d643741c3c3aa0 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -187,6 +187,7 @@ extern "C" { #define UNPACK_SEQUENCE_LIST 159 #define UNPACK_SEQUENCE_TUPLE 160 #define UNPACK_SEQUENCE_TWO_TUPLE 161 +#define SEND_GEN 166 #define DO_TRACING 255 #define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\ diff --git a/Lib/dis.py b/Lib/dis.py index a6921008d9d0e5..9edde6ae8258da 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -39,6 +39,7 @@ BINARY_OP = opmap['BINARY_OP'] JUMP_BACKWARD = opmap['JUMP_BACKWARD'] FOR_ITER = opmap['FOR_ITER'] +SEND = opmap['SEND'] LOAD_ATTR = opmap['LOAD_ATTR'] CACHE = opmap["CACHE"] @@ -453,6 +454,7 @@ def _get_instructions_bytes(code, varname_from_oparg=None, argrepr = '' positions = Positions(*next(co_positions, ())) deop = _deoptop(op) + caches = _inline_cache_entries[deop] if arg is not None: # Set argval to the dereferenced value of the argument when # available, and argrepr to the string representation of argval. @@ -478,8 +480,7 @@ def _get_instructions_bytes(code, varname_from_oparg=None, elif deop in hasjrel: signed_arg = -arg if _is_backward_jump(deop) else arg argval = offset + 2 + signed_arg*2 - if deop == FOR_ITER: - argval += 2 + argval += 2 * caches argrepr = "to " + repr(argval) elif deop in haslocal or deop in hasfree: argval, argrepr = _get_name_info(arg, varname_from_oparg) @@ -633,12 +634,12 @@ def findlabels(code): for offset, op, arg in _unpack_opargs(code): if arg is not None: deop = _deoptop(op) + caches = _inline_cache_entries[deop] if deop in hasjrel: if _is_backward_jump(deop): arg = -arg label = offset + 2 + arg*2 - if deop == FOR_ITER: - label += 2 + label += 2 * caches elif deop in hasjabs: label = arg*2 else: diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 933c8c7d7e0590..38d4a384c2cc95 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -432,6 +432,7 @@ def _write_atomic(path, data, mode=0o666): # Python 3.12a5 3516 (Add COMPARE_AND_BRANCH instruction) # Python 3.12a5 3517 (Change YIELD_VALUE oparg to exception block depth) # Python 3.12a5 3518 (Add RETURN_CONST instruction) +# Python 3.12a5 3519 (Modify SEND instruction) # Python 3.13 will start with 3550 @@ -444,7 +445,7 @@ def _write_atomic(path, data, mode=0o666): # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # in PC/launcher.c must also be updated. -MAGIC_NUMBER = (3518).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3519).to_bytes(2, 'little') + b'\r\n' _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c diff --git a/Lib/opcode.py b/Lib/opcode.py index 5f163d2ccb80df..b69cd1bbdd61ca 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -167,7 +167,7 @@ def pseudo_op(name, op, real_ops): def_op('RETURN_CONST', 121) hasconst.append(121) def_op('BINARY_OP', 122) -jrel_op('SEND', 123) # Number of bytes to skip +jrel_op('SEND', 123) # Number of words to skip def_op('LOAD_FAST', 124) # Local variable number, no null check haslocal.append(124) def_op('STORE_FAST', 125) # Local variable number @@ -370,6 +370,9 @@ def pseudo_op(name, op, real_ops): "UNPACK_SEQUENCE_TUPLE", "UNPACK_SEQUENCE_TWO_TUPLE", ], + "SEND": [ + "SEND_GEN", + ], } _specialized_instructions = [ opcode for family in _specializations.values() for opcode in family @@ -429,6 +432,9 @@ def pseudo_op(name, op, real_ops): "STORE_SUBSCR": { "counter": 1, }, + "SEND": { + "counter": 1, + }, } _inline_cache_entries = [ diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 1050b15e16eaaa..9086824dd6f40c 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -475,11 +475,13 @@ async def _asyncwith(c): BEFORE_ASYNC_WITH GET_AWAITABLE 1 LOAD_CONST 0 (None) - >> SEND 3 (to 22) + >> SEND 3 (to 24) YIELD_VALUE 2 RESUME 3 - JUMP_BACKWARD_NO_INTERRUPT 4 (to 14) - >> POP_TOP + JUMP_BACKWARD_NO_INTERRUPT 5 (to 14) + >> SWAP 2 + POP_TOP + POP_TOP %3d LOAD_CONST 1 (1) STORE_FAST 1 (x) @@ -490,30 +492,33 @@ async def _asyncwith(c): CALL 2 GET_AWAITABLE 2 LOAD_CONST 0 (None) - >> SEND 3 (to 56) + >> SEND 3 (to 64) YIELD_VALUE 2 RESUME 3 - JUMP_BACKWARD_NO_INTERRUPT 4 (to 48) + JUMP_BACKWARD_NO_INTERRUPT 5 (to 54) >> POP_TOP + POP_TOP %3d LOAD_CONST 2 (2) STORE_FAST 2 (y) RETURN_CONST 0 (None) %3d >> CLEANUP_THROW - JUMP_BACKWARD 23 (to 22) + JUMP_BACKWARD 27 (to 24) >> CLEANUP_THROW - JUMP_BACKWARD 8 (to 56) + JUMP_BACKWARD 9 (to 64) >> PUSH_EXC_INFO WITH_EXCEPT_START GET_AWAITABLE 2 LOAD_CONST 0 (None) - >> SEND 4 (to 90) + >> SEND 4 (to 102) YIELD_VALUE 3 RESUME 3 - JUMP_BACKWARD_NO_INTERRUPT 4 (to 80) + JUMP_BACKWARD_NO_INTERRUPT 5 (to 90) >> CLEANUP_THROW - >> POP_JUMP_IF_TRUE 1 (to 94) + >> SWAP 2 + POP_TOP + POP_JUMP_IF_TRUE 1 (to 110) RERAISE 2 >> POP_TOP POP_EXCEPT diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-10-15-54-57.gh-issue-87849.IUVvPz.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-10-15-54-57.gh-issue-87849.IUVvPz.rst new file mode 100644 index 00000000000000..da5f3ff79fd575 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-10-15-54-57.gh-issue-87849.IUVvPz.rst @@ -0,0 +1,3 @@ +Change the ``SEND`` instruction to leave the receiver on the stack. This +allows the specialized form of ``SEND`` to skip the chain of C calls and jump +directly to the ``RESUME`` in the generator or coroutine. diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 0e52a3e2399c06..581ed2d214c4d9 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -334,10 +334,10 @@ mark_stacks(PyCodeObject *code_obj, int len) break; } case SEND: - j = get_arg(code, i) + i + 1; + j = get_arg(code, i) + i + INLINE_CACHE_ENTRIES_SEND + 1; assert(j < len); - assert(stacks[j] == UNINITIALIZED || stacks[j] == pop_value(next_stack)); - stacks[j] = pop_value(next_stack); + assert(stacks[j] == UNINITIALIZED || stacks[j] == next_stack); + stacks[j] = next_stack; stacks[i+1] = next_stack; break; case JUMP_FORWARD: diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 2b9f12fefa14e9..429cd7fdafa168 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -680,51 +680,66 @@ dummy_func( PREDICT(LOAD_CONST); } - inst(SEND, (receiver, v -- receiver if (!jump), retval)) { + family(for_iter, INLINE_CACHE_ENTRIES_FOR_ITER) = { + SEND, + SEND_GEN, + }; + + inst(SEND, (unused/1, receiver, v -- receiver, retval)) { + #if ENABLE_SPECIALIZATION + _PySendCache *cache = (_PySendCache *)next_instr; + if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { + assert(cframe.use_tracing == 0); + next_instr--; + _Py_Specialize_Send(receiver, next_instr); + DISPATCH_SAME_OPARG(); + } + STAT_INC(SEND, deferred); + DECREMENT_ADAPTIVE_COUNTER(cache->counter); + #endif /* ENABLE_SPECIALIZATION */ assert(frame != &entry_frame); - bool jump = false; - PySendResult gen_status; - if (tstate->c_tracefunc == NULL) { - gen_status = PyIter_Send(receiver, v, &retval); - } else { - if (Py_IsNone(v) && PyIter_Check(receiver)) { - retval = Py_TYPE(receiver)->tp_iternext(receiver); - } - else { - retval = PyObject_CallMethodOneArg(receiver, &_Py_ID(send), v); - } - if (retval == NULL) { - if (tstate->c_tracefunc != NULL - && _PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) - call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, frame); - if (_PyGen_FetchStopIterationValue(&retval) == 0) { - gen_status = PYGEN_RETURN; - } - else { - gen_status = PYGEN_ERROR; - } + if (Py_IsNone(v) && PyIter_Check(receiver)) { + retval = Py_TYPE(receiver)->tp_iternext(receiver); + } + else { + retval = PyObject_CallMethodOneArg(receiver, &_Py_ID(send), v); + } + if (retval == NULL) { + if (tstate->c_tracefunc != NULL + && _PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) + call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, frame); + if (_PyGen_FetchStopIterationValue(&retval) == 0) { + assert(retval != NULL); + JUMPBY(oparg); } else { - gen_status = PYGEN_NEXT; + assert(retval == NULL); + goto error; } } - if (gen_status == PYGEN_ERROR) { - assert(retval == NULL); - goto error; - } - Py_DECREF(v); - if (gen_status == PYGEN_RETURN) { - assert(retval != NULL); - Py_DECREF(receiver); - JUMPBY(oparg); - jump = true; - } else { - assert(gen_status == PYGEN_NEXT); assert(retval != NULL); } } + inst(SEND_GEN, (unused/1, receiver, v -- receiver)) { + assert(cframe.use_tracing == 0); + PyGenObject *gen = (PyGenObject *)receiver; + DEOPT_IF(Py_TYPE(gen) != &PyGen_Type && + Py_TYPE(gen) != &PyCoro_Type, SEND); + DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, SEND); + STAT_INC(SEND, hit); + _PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe; + frame->yield_offset = oparg; + STACK_SHRINK(1); + _PyFrame_StackPush(gen_frame, v); + gen->gi_frame_state = FRAME_EXECUTING; + gen->gi_exc_state.previous_item = tstate->exc_info; + tstate->exc_info = &gen->gi_exc_state; + JUMPBY(INLINE_CACHE_ENTRIES_SEND + oparg); + DISPATCH_INLINED(gen_frame); + } + inst(YIELD_VALUE, (retval -- unused)) { // NOTE: It's important that YIELD_VALUE never raises an exception! // The compiler treats any exception raised here as a failed close() @@ -796,12 +811,13 @@ dummy_func( } } - inst(CLEANUP_THROW, (sub_iter, last_sent_val, exc_value -- value)) { + inst(CLEANUP_THROW, (sub_iter, last_sent_val, exc_value -- none, value)) { assert(throwflag); assert(exc_value && PyExceptionInstance_Check(exc_value)); if (PyErr_GivenExceptionMatches(exc_value, PyExc_StopIteration)) { value = Py_NewRef(((PyStopIterationObject *)exc_value)->value); DECREF_INPUTS(); + none = Py_NewRef(Py_None); } else { _PyErr_SetRaisedException(tstate, Py_NewRef(exc_value)); diff --git a/Python/compile.c b/Python/compile.c index a3c915c3c14a96..b49eda314eeef1 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -1789,6 +1789,8 @@ compiler_add_yield_from(struct compiler *c, location loc, int await) ADDOP(c, loc, CLEANUP_THROW); USE_LABEL(c, exit); + ADDOP_I(c, loc, SWAP, 2); + ADDOP(c, loc, POP_TOP); return SUCCESS; } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index a224d4eb892785..093ebff026b509 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -882,57 +882,69 @@ } TARGET(SEND) { + PREDICTED(SEND); PyObject *v = PEEK(1); PyObject *receiver = PEEK(2); PyObject *retval; + #if ENABLE_SPECIALIZATION + _PySendCache *cache = (_PySendCache *)next_instr; + if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { + assert(cframe.use_tracing == 0); + next_instr--; + _Py_Specialize_Send(receiver, next_instr); + DISPATCH_SAME_OPARG(); + } + STAT_INC(SEND, deferred); + DECREMENT_ADAPTIVE_COUNTER(cache->counter); + #endif /* ENABLE_SPECIALIZATION */ assert(frame != &entry_frame); - bool jump = false; - PySendResult gen_status; - if (tstate->c_tracefunc == NULL) { - gen_status = PyIter_Send(receiver, v, &retval); - } else { - if (Py_IsNone(v) && PyIter_Check(receiver)) { - retval = Py_TYPE(receiver)->tp_iternext(receiver); - } - else { - retval = PyObject_CallMethodOneArg(receiver, &_Py_ID(send), v); - } - if (retval == NULL) { - if (tstate->c_tracefunc != NULL - && _PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) - call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, frame); - if (_PyGen_FetchStopIterationValue(&retval) == 0) { - gen_status = PYGEN_RETURN; - } - else { - gen_status = PYGEN_ERROR; - } + if (Py_IsNone(v) && PyIter_Check(receiver)) { + retval = Py_TYPE(receiver)->tp_iternext(receiver); + } + else { + retval = PyObject_CallMethodOneArg(receiver, &_Py_ID(send), v); + } + if (retval == NULL) { + if (tstate->c_tracefunc != NULL + && _PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) + call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, frame); + if (_PyGen_FetchStopIterationValue(&retval) == 0) { + assert(retval != NULL); + JUMPBY(oparg); } else { - gen_status = PYGEN_NEXT; + assert(retval == NULL); + goto error; } } - if (gen_status == PYGEN_ERROR) { - assert(retval == NULL); - goto error; - } - Py_DECREF(v); - if (gen_status == PYGEN_RETURN) { - assert(retval != NULL); - Py_DECREF(receiver); - JUMPBY(oparg); - jump = true; - } else { - assert(gen_status == PYGEN_NEXT); assert(retval != NULL); } - STACK_SHRINK(1); - STACK_GROW(((!jump) ? 1 : 0)); POKE(1, retval); + JUMPBY(1); DISPATCH(); } + TARGET(SEND_GEN) { + PyObject *v = PEEK(1); + PyObject *receiver = PEEK(2); + assert(cframe.use_tracing == 0); + PyGenObject *gen = (PyGenObject *)receiver; + DEOPT_IF(Py_TYPE(gen) != &PyGen_Type && + Py_TYPE(gen) != &PyCoro_Type, SEND); + DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, SEND); + STAT_INC(SEND, hit); + _PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe; + frame->yield_offset = oparg; + STACK_SHRINK(1); + _PyFrame_StackPush(gen_frame, v); + gen->gi_frame_state = FRAME_EXECUTING; + gen->gi_exc_state.previous_item = tstate->exc_info; + tstate->exc_info = &gen->gi_exc_state; + JUMPBY(INLINE_CACHE_ENTRIES_SEND + oparg); + DISPATCH_INLINED(gen_frame); + } + TARGET(YIELD_VALUE) { PyObject *retval = PEEK(1); // NOTE: It's important that YIELD_VALUE never raises an exception! @@ -1026,6 +1038,7 @@ PyObject *exc_value = PEEK(1); PyObject *last_sent_val = PEEK(2); PyObject *sub_iter = PEEK(3); + PyObject *none; PyObject *value; assert(throwflag); assert(exc_value && PyExceptionInstance_Check(exc_value)); @@ -1034,13 +1047,15 @@ Py_DECREF(sub_iter); Py_DECREF(last_sent_val); Py_DECREF(exc_value); + none = Py_NewRef(Py_None); } else { _PyErr_SetRaisedException(tstate, Py_NewRef(exc_value)); goto exception_unwind; } - STACK_SHRINK(2); + STACK_SHRINK(1); POKE(1, value); + POKE(2, none); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index db1dfd37a90132..d622eb12c8cb2d 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -104,6 +104,8 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { return 1; case SEND: return 2; + case SEND_GEN: + return 2; case YIELD_VALUE: return 1; case POP_EXCEPT: @@ -453,7 +455,9 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { case GET_AWAITABLE: return 1; case SEND: - return ((!jump) ? 1 : 0) + 1; + return 2; + case SEND_GEN: + return 1; case YIELD_VALUE: return 1; case POP_EXCEPT: @@ -465,7 +469,7 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { case END_ASYNC_FOR: return 0; case CLEANUP_THROW: - return 1; + return 2; case LOAD_ASSERTION_ERROR: return 1; case LOAD_BUILD_CLASS: @@ -763,7 +767,8 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[256] = { [GET_AITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [GET_ANEXT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [GET_AWAITABLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [SEND] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [SEND] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, + [SEND_GEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, [YIELD_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [POP_EXCEPT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [RERAISE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index eceb246fac4909..301ec6e005dad6 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -165,7 +165,7 @@ static void *opcode_targets[256] = { &&TARGET_SET_UPDATE, &&TARGET_DICT_MERGE, &&TARGET_DICT_UPDATE, - &&_unknown_opcode, + &&TARGET_SEND_GEN, &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, diff --git a/Python/specialize.c b/Python/specialize.c index 908ad6dceb57f3..4ede3122d38046 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -128,6 +128,7 @@ print_spec_stats(FILE *out, OpcodeStats *stats) fprintf(out, "opcode[%d].specializable : 1\n", BINARY_SLICE); fprintf(out, "opcode[%d].specializable : 1\n", COMPARE_OP); fprintf(out, "opcode[%d].specializable : 1\n", STORE_SLICE); + fprintf(out, "opcode[%d].specializable : 1\n", SEND); for (int i = 0; i < 256; i++) { if (_PyOpcode_Caches[i]) { fprintf(out, "opcode[%d].specializable : 1\n", i); @@ -1084,7 +1085,7 @@ PyObject *descr, DescriptorClassification kind) if (dict) { SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_NOT_MANAGED_DICT); return 0; - } + } assert(owner_cls->tp_dictoffset > 0); assert(owner_cls->tp_dictoffset <= INT16_MAX); _py_set_opcode(instr, LOAD_ATTR_METHOD_LAZY_DICT); @@ -2183,3 +2184,25 @@ _Py_Specialize_ForIter(PyObject *iter, _Py_CODEUNIT *instr, int oparg) STAT_INC(FOR_ITER, success); cache->counter = adaptive_counter_cooldown(); } + +void +_Py_Specialize_Send(PyObject *receiver, _Py_CODEUNIT *instr) +{ + assert(ENABLE_SPECIALIZATION); + assert(_PyOpcode_Caches[SEND] == INLINE_CACHE_ENTRIES_SEND); + _PySendCache *cache = (_PySendCache *)(instr + 1); + PyTypeObject *tp = Py_TYPE(receiver); + if (tp == &PyGen_Type || tp == &PyCoro_Type) { + _py_set_opcode(instr, SEND_GEN); + goto success; + } + SPECIALIZATION_FAIL(SEND, + _PySpecialization_ClassifyIterator(receiver)); + STAT_INC(SEND, failure); + _py_set_opcode(instr, SEND); + cache->counter = adaptive_counter_backoff(cache->counter); + return; +success: + STAT_INC(SEND, success); + cache->counter = adaptive_counter_cooldown(); +} From d9199175c7386a95aaac91822a2197b9365eb0e8 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 13 Feb 2023 11:31:15 +0000 Subject: [PATCH 086/247] GH-100987: Refactor `_PyInterpreterFrame` a bit, to assist generator improvement. (GH-100988) Refactor _PyInterpreterFrame a bit, to assist generator improvement. --- Include/internal/pycore_frame.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index f12b225ebfccf2..81d16b219c305b 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -47,15 +47,13 @@ enum _frameowner { }; typedef struct _PyInterpreterFrame { - /* "Specials" section */ + PyCodeObject *f_code; /* Strong reference */ + struct _PyInterpreterFrame *previous; PyObject *f_funcobj; /* Strong reference. Only valid if not on C stack */ PyObject *f_globals; /* Borrowed reference. Only valid if not on C stack */ PyObject *f_builtins; /* Borrowed reference. Only valid if not on C stack */ PyObject *f_locals; /* Strong reference, may be NULL. Only valid if not on C stack */ - PyCodeObject *f_code; /* Strong reference */ PyFrameObject *frame_obj; /* Strong reference, may be NULL. Only valid if not on C stack */ - /* Linkage section */ - struct _PyInterpreterFrame *previous; // NOTE: This is not necessarily the last instruction started in the given // frame. Rather, it is the code unit *prior to* the *next* instruction. For // example, it may be an inline CACHE entry, an instruction we just jumped From 2db2c4b45501eebef5b3ff89118554bd5eb92ed4 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 13 Feb 2023 13:36:42 +0100 Subject: [PATCH 087/247] gh-92547: Purge sqlite3_enable_shared_cache() detection from configure (#101873) --- configure | 51 --------------------------------------------------- configure.ac | 1 - 2 files changed, 52 deletions(-) diff --git a/configure b/configure index 97694c602d1cc8..35088f9e5cafd6 100755 --- a/configure +++ b/configure @@ -13689,57 +13689,6 @@ fi - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sqlite3_enable_shared_cache in -lsqlite3" >&5 -$as_echo_n "checking for sqlite3_enable_shared_cache in -lsqlite3... " >&6; } -if ${ac_cv_lib_sqlite3_sqlite3_enable_shared_cache+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lsqlite3 $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char sqlite3_enable_shared_cache (); -int -main () -{ -return sqlite3_enable_shared_cache (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_sqlite3_sqlite3_enable_shared_cache=yes -else - ac_cv_lib_sqlite3_sqlite3_enable_shared_cache=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sqlite3_sqlite3_enable_shared_cache" >&5 -$as_echo "$ac_cv_lib_sqlite3_sqlite3_enable_shared_cache" >&6; } -if test "x$ac_cv_lib_sqlite3_sqlite3_enable_shared_cache" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_LIBSQLITE3 1 -_ACEOF - - LIBS="-lsqlite3 $LIBS" - -else - - have_supported_sqlite3=no - -fi - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sqlite3_progress_handler in -lsqlite3" >&5 $as_echo_n "checking for sqlite3_progress_handler in -lsqlite3... " >&6; } if ${ac_cv_lib_sqlite3_sqlite3_progress_handler+:} false; then : diff --git a/configure.ac b/configure.ac index 09369b985b33f6..1ab48e0d1c160a 100644 --- a/configure.ac +++ b/configure.ac @@ -3867,7 +3867,6 @@ dnl hence CPPFLAGS instead of CFLAGS. PY_CHECK_SQLITE_FUNC([sqlite3_column_decltype]) PY_CHECK_SQLITE_FUNC([sqlite3_column_double]) PY_CHECK_SQLITE_FUNC([sqlite3_complete]) - PY_CHECK_SQLITE_FUNC([sqlite3_enable_shared_cache]) PY_CHECK_SQLITE_FUNC([sqlite3_progress_handler]) PY_CHECK_SQLITE_FUNC([sqlite3_result_double]) PY_CHECK_SQLITE_FUNC([sqlite3_set_authorizer]) From 95cbb3d908175ccd855078b3fab7f99e7d0bca88 Mon Sep 17 00:00:00 2001 From: James Lee <49257044+juria90@users.noreply.github.com> Date: Mon, 13 Feb 2023 05:49:44 -0800 Subject: [PATCH 088/247] gh-101810: Remove duplicated st_ino calculation (GH-101811) --- Python/fileutils.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/Python/fileutils.c b/Python/fileutils.c index 244bd899b3bd24..22b2257a56d0ec 100644 --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -1162,8 +1162,6 @@ _Py_fstat_noraise(int fd, struct _Py_stat_struct *status) } _Py_attribute_data_to_stat(&info, 0, status); - /* specific to fstat() */ - status->st_ino = (((uint64_t)info.nFileIndexHigh) << 32) + info.nFileIndexLow; return 0; #else return fstat(fd, status); From 0c6fe81dce9d6bb1dce5e4503f1b42bc5355ba24 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Mon, 13 Feb 2023 20:33:48 +0000 Subject: [PATCH 089/247] gh-101849: Add upgrade codes for old versions of launcher that ended up with later version numbers (GH-101877) --- ...-02-13-16-32-50.gh-issue-101849.7lm_53.rst | 1 + Tools/msi/common.wxs | 2 +- Tools/msi/launcher/launcher.wxs | 21 +++++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Windows/2023-02-13-16-32-50.gh-issue-101849.7lm_53.rst diff --git a/Misc/NEWS.d/next/Windows/2023-02-13-16-32-50.gh-issue-101849.7lm_53.rst b/Misc/NEWS.d/next/Windows/2023-02-13-16-32-50.gh-issue-101849.7lm_53.rst new file mode 100644 index 00000000000000..861d4de9f9a650 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-02-13-16-32-50.gh-issue-101849.7lm_53.rst @@ -0,0 +1 @@ +Ensures installer will correctly upgrade existing ``py.exe`` launcher installs. diff --git a/Tools/msi/common.wxs b/Tools/msi/common.wxs index 55cb44860d02c0..54fa749ab17cdd 100644 --- a/Tools/msi/common.wxs +++ b/Tools/msi/common.wxs @@ -25,7 +25,6 @@ - @@ -42,6 +41,7 @@ UPGRADE + diff --git a/Tools/msi/launcher/launcher.wxs b/Tools/msi/launcher/launcher.wxs index b83058c63bf6d9..49f1f7b8c1762e 100644 --- a/Tools/msi/launcher/launcher.wxs +++ b/Tools/msi/launcher/launcher.wxs @@ -34,13 +34,34 @@ NOT Installed AND NOT ALLUSERS=1 NOT Installed AND ALLUSERS=1 + + UPGRADE or REMOVE_350_LAUNCHER or REMOVE_360A1_LAUNCHER or UPGRADE_3_11_0 or UPGRADE_3_11_1 + UPGRADE or REMOVE_350_LAUNCHER or REMOVE_360A1_LAUNCHER + + + Installed OR NOT DOWNGRADE OR UPGRADE_3_11_0 OR UPGRADE_3_11_1 + + Installed OR NOT DOWNGRADE + + + + + + + From 928752ce4c23f47d3175dd47ecacf08d86a99c9d Mon Sep 17 00:00:00 2001 From: Radek Smejkal Date: Tue, 14 Feb 2023 02:37:34 +0100 Subject: [PATCH 090/247] gh-74895: getaddrinfo no longer raises OverflowError (#2435) `socket.getaddrinfo()` no longer raises `OverflowError` based on the **port** argument. Error reporting (or not) for its value is left up to the underlying C library `getaddrinfo()` implementation. --- Lib/test/test_socket.py | 48 +++++++++++++++++++ Misc/ACKS | 1 + ...3-02-13-22-21-58.gh-issue-74895.esMNtq.rst | 5 ++ Modules/getaddrinfo.c | 6 ++- Modules/socketmodule.c | 14 ++++-- 5 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-13-22-21-58.gh-issue-74895.esMNtq.rst diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index f1b4018c265e18..a313da29b4a4fd 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -1600,6 +1600,54 @@ def testGetaddrinfo(self): except socket.gaierror: pass + def test_getaddrinfo_int_port_overflow(self): + # gh-74895: Test that getaddrinfo does not raise OverflowError on port. + # + # POSIX getaddrinfo() never specify the valid range for "service" + # decimal port number values. For IPv4 and IPv6 they are technically + # unsigned 16-bit values, but the API is protocol agnostic. Which values + # trigger an error from the C library function varies by platform as + # they do not all perform validation. + + # The key here is that we don't want to produce OverflowError as Python + # prior to 3.12 did for ints outside of a [LONG_MIN, LONG_MAX] range. + # Leave the error up to the underlying string based platform C API. + + from _testcapi import ULONG_MAX, LONG_MAX, LONG_MIN + try: + socket.getaddrinfo(None, ULONG_MAX + 1) + except OverflowError: + # Platforms differ as to what values consitute a getaddrinfo() error + # return. Some fail for LONG_MAX+1, others ULONG_MAX+1, and Windows + # silently accepts such huge "port" aka "service" numeric values. + self.fail("Either no error or socket.gaierror expected.") + except socket.gaierror: + pass + + try: + socket.getaddrinfo(None, LONG_MAX + 1) + except OverflowError: + self.fail("Either no error or socket.gaierror expected.") + except socket.gaierror: + pass + + try: + socket.getaddrinfo(None, LONG_MAX - 0xffff + 1) + except OverflowError: + self.fail("Either no error or socket.gaierror expected.") + except socket.gaierror: + pass + + try: + socket.getaddrinfo(None, LONG_MIN - 1) + except OverflowError: + self.fail("Either no error or socket.gaierror expected.") + except socket.gaierror: + pass + + socket.getaddrinfo(None, 0) # No error expected. + socket.getaddrinfo(None, 0xffff) # No error expected. + def test_getnameinfo(self): # only IP addresses are allowed self.assertRaises(OSError, socket.getnameinfo, ('mail.python.org',0), 0) diff --git a/Misc/ACKS b/Misc/ACKS index e12cbea0ebd6ed..ca92608868f23f 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1688,6 +1688,7 @@ Roman Skurikhin Ville Skyttä Michael Sloan Nick Sloan +Radek Smejkal Václav Šmilauer Casper W. Smet Allen W. Smith diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-13-22-21-58.gh-issue-74895.esMNtq.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-13-22-21-58.gh-issue-74895.esMNtq.rst new file mode 100644 index 00000000000000..adbbb601634a60 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-13-22-21-58.gh-issue-74895.esMNtq.rst @@ -0,0 +1,5 @@ +:mod:`socket.getaddrinfo` no longer raises :class:`OverflowError` for +:class:`int` **port** values outside of the C long range. Out of range values +are left up to the underlying string based C library API to report. A +:class:`socket.gaierror` ``SAI_SERVICE`` may occur instead, or no error at all +as not all platform C libraries generate an error. diff --git a/Modules/getaddrinfo.c b/Modules/getaddrinfo.c index 0b4620ed683de9..f1c28d7d9312ac 100644 --- a/Modules/getaddrinfo.c +++ b/Modules/getaddrinfo.c @@ -342,7 +342,11 @@ getaddrinfo(const char*hostname, const char*servname, pai->ai_socktype = SOCK_DGRAM; pai->ai_protocol = IPPROTO_UDP; } - port = htons((u_short)atoi(servname)); + long maybe_port = strtol(servname, NULL, 10); + if (maybe_port < 0 || maybe_port > 0xffff) { + ERR(EAI_SERVICE); + } + port = htons((u_short)maybe_port); } else { struct servent *sp; const char *proto; diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 0a9e46512b157b..2d300f19436b1a 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -6650,7 +6650,7 @@ socket_getaddrinfo(PyObject *self, PyObject *args, PyObject* kwargs) struct addrinfo *res0 = NULL; PyObject *hobj = NULL; PyObject *pobj = (PyObject *)NULL; - char pbuf[30]; + PyObject *pstr = NULL; const char *hptr, *pptr; int family, socktype, protocol, flags; int error; @@ -6680,11 +6680,13 @@ socket_getaddrinfo(PyObject *self, PyObject *args, PyObject* kwargs) return NULL; } if (PyLong_CheckExact(pobj)) { - long value = PyLong_AsLong(pobj); - if (value == -1 && PyErr_Occurred()) + pstr = PyObject_Str(pobj); + if (pstr == NULL) + goto err; + assert(PyUnicode_Check(pstr)); + pptr = PyUnicode_AsUTF8(pstr); + if (pptr == NULL) goto err; - PyOS_snprintf(pbuf, sizeof(pbuf), "%ld", value); - pptr = pbuf; } else if (PyUnicode_Check(pobj)) { pptr = PyUnicode_AsUTF8(pobj); if (pptr == NULL) @@ -6750,12 +6752,14 @@ socket_getaddrinfo(PyObject *self, PyObject *args, PyObject* kwargs) Py_DECREF(single); } Py_XDECREF(idna); + Py_XDECREF(pstr); if (res0) freeaddrinfo(res0); return all; err: Py_XDECREF(all); Py_XDECREF(idna); + Py_XDECREF(pstr); if (res0) freeaddrinfo(res0); return (PyObject *)NULL; From 8be8101bca34b60481ec3d7ecaea4a3379fb7dbb Mon Sep 17 00:00:00 2001 From: Sam James Date: Tue, 14 Feb 2023 07:21:58 +0000 Subject: [PATCH 091/247] gh-101857: Allow xattr detection on musl libc (#101858) Previously, we checked exclusively for `__GLIBC__` (AND'd with some other conditions). Checking for `__linux__` instead should be fine. This fixes using e.g. `os.listxattr()` on systems using musl libc. Bug: https://bugs.gentoo.org/894130 Co-authored-by: Gregory P. Smith --- .../2023-02-12-22-40-22.gh-issue-101857._bribG.rst | 1 + Modules/posixmodule.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-12-22-40-22.gh-issue-101857._bribG.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-12-22-40-22.gh-issue-101857._bribG.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-12-22-40-22.gh-issue-101857._bribG.rst new file mode 100644 index 00000000000000..832cc300fa9433 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-12-22-40-22.gh-issue-101857._bribG.rst @@ -0,0 +1 @@ +Fix xattr support detection on Linux systems by widening the check to linux, not just glibc. This fixes support for musl. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index cba6cea48b77e1..d9e93473aeadaa 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -274,8 +274,9 @@ corresponding Unix manual entries for more information on calls."); # undef HAVE_SCHED_SETAFFINITY #endif -#if defined(HAVE_SYS_XATTR_H) && defined(__GLIBC__) && !defined(__FreeBSD_kernel__) && !defined(__GNU__) +#if defined(HAVE_SYS_XATTR_H) && defined(__linux__) && !defined(__FreeBSD_kernel__) && !defined(__GNU__) # define USE_XATTRS +# include // Needed for XATTR_SIZE_MAX on musl libc. #endif #ifdef USE_XATTRS From e5da9ab2c82c6b4e4f8ffa699a9a609ea1bea255 Mon Sep 17 00:00:00 2001 From: Jonathan Protzenko Date: Tue, 14 Feb 2023 01:25:16 -0800 Subject: [PATCH 092/247] gh-99108: Import SHA2-384/512 from HACL* (#101707) Replace the builtin hashlib implementations of SHA2-384 and SHA2-512 originally from LibTomCrypt with formally verified, side-channel resistant code from the [HACL*](https://github.com/hacl-star/hacl-star/) project. The builtins remain a fallback only used when OpenSSL does not provide them. --- Makefile.pre.in | 2 +- ...3-02-08-12-57-35.gh-issue-99108.6tnmhA.rst | 4 + Modules/Setup.stdlib.in | 2 +- Modules/_hacl/Hacl_Streaming_SHA2.c | 654 ++++++++++++++++++ Modules/_hacl/Hacl_Streaming_SHA2.h | 85 ++- .../include/krml/FStar_UInt128_Verified.h | 347 ++++++++++ .../include/krml/FStar_UInt_8_16_32_64.h | 2 +- .../krml/fstar_uint128_struct_endianness.h | 68 ++ Modules/_hacl/include/krml/types.h | 14 + .../_hacl/include/python_hacl_namespaces.h | 17 + Modules/_hacl/internal/Hacl_SHA2_Generic.h | 5 +- Modules/_hacl/refresh.sh | 45 +- Modules/sha256module.c | 1 + Modules/sha512module.c | 441 ++---------- configure | 2 +- configure.ac | 4 +- 16 files changed, 1259 insertions(+), 434 deletions(-) create mode 100644 Misc/NEWS.d/next/Security/2023-02-08-12-57-35.gh-issue-99108.6tnmhA.rst create mode 100644 Modules/_hacl/include/krml/FStar_UInt128_Verified.h create mode 100644 Modules/_hacl/include/krml/fstar_uint128_struct_endianness.h create mode 100644 Modules/_hacl/include/krml/types.h diff --git a/Makefile.pre.in b/Makefile.pre.in index 7a84b953d97962..d42d4d8a3c1c9f 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -2608,7 +2608,7 @@ MODULE__MD5_DEPS=$(srcdir)/Modules/hashlib.h MODULE__SHA1_DEPS=$(srcdir)/Modules/hashlib.h MODULE__SHA256_DEPS=$(srcdir)/Modules/hashlib.h $(srcdir)/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h $(srcdir)/Modules/_hacl/include/krml/lowstar_endianness.h $(srcdir)/Modules/_hacl/include/krml/internal/target.h $(srcdir)/Modules/_hacl/Hacl_Streaming_SHA2.h MODULE__SHA3_DEPS=$(srcdir)/Modules/_sha3/sha3.c $(srcdir)/Modules/_sha3/sha3.h $(srcdir)/Modules/hashlib.h -MODULE__SHA512_DEPS=$(srcdir)/Modules/hashlib.h +MODULE__SHA512_DEPS=$(srcdir)/Modules/hashlib.h $(srcdir)/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h $(srcdir)/Modules/_hacl/include/krml/lowstar_endianness.h $(srcdir)/Modules/_hacl/include/krml/internal/target.h $(srcdir)/Modules/_hacl/Hacl_Streaming_SHA2.h MODULE__SOCKET_DEPS=$(srcdir)/Modules/socketmodule.h $(srcdir)/Modules/addrinfo.h $(srcdir)/Modules/getaddrinfo.c $(srcdir)/Modules/getnameinfo.c MODULE__SSL_DEPS=$(srcdir)/Modules/_ssl.h $(srcdir)/Modules/_ssl/cert.c $(srcdir)/Modules/_ssl/debughelpers.c $(srcdir)/Modules/_ssl/misc.c $(srcdir)/Modules/_ssl_data.h $(srcdir)/Modules/_ssl_data_111.h $(srcdir)/Modules/_ssl_data_300.h $(srcdir)/Modules/socketmodule.h MODULE__TESTCAPI_DEPS=$(srcdir)/Modules/_testcapi/testcapi_long.h $(srcdir)/Modules/_testcapi/parts.h diff --git a/Misc/NEWS.d/next/Security/2023-02-08-12-57-35.gh-issue-99108.6tnmhA.rst b/Misc/NEWS.d/next/Security/2023-02-08-12-57-35.gh-issue-99108.6tnmhA.rst new file mode 100644 index 00000000000000..6a7a309dad5d8f --- /dev/null +++ b/Misc/NEWS.d/next/Security/2023-02-08-12-57-35.gh-issue-99108.6tnmhA.rst @@ -0,0 +1,4 @@ +Replace the builtin :mod:`hashlib` implementations of SHA2-384 and SHA2-512 +originally from LibTomCrypt with formally verified, side-channel resistant +code from the `HACL* `_ project. +The builtins remain a fallback only used when OpenSSL does not provide them. diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index f72783810f9415..b6d13e04d3fa87 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -80,7 +80,7 @@ @MODULE__MD5_TRUE@_md5 md5module.c @MODULE__SHA1_TRUE@_sha1 sha1module.c @MODULE__SHA256_TRUE@_sha256 sha256module.c _hacl/Hacl_Streaming_SHA2.c -@MODULE__SHA512_TRUE@_sha512 sha512module.c +@MODULE__SHA512_TRUE@_sha512 sha512module.c _hacl/Hacl_Streaming_SHA2.c @MODULE__SHA3_TRUE@_sha3 _sha3/sha3module.c @MODULE__BLAKE2_TRUE@_blake2 _blake2/blake2module.c _blake2/blake2b_impl.c _blake2/blake2s_impl.c diff --git a/Modules/_hacl/Hacl_Streaming_SHA2.c b/Modules/_hacl/Hacl_Streaming_SHA2.c index 84566571792a3c..8169c7a356731e 100644 --- a/Modules/_hacl/Hacl_Streaming_SHA2.c +++ b/Modules/_hacl/Hacl_Streaming_SHA2.c @@ -250,6 +250,229 @@ static inline void sha224_finish(uint32_t *st, uint8_t *h) memcpy(h, hbuf, (uint32_t)28U * sizeof (uint8_t)); } +void Hacl_SHA2_Scalar32_sha512_init(uint64_t *hash) +{ + KRML_MAYBE_FOR8(i, + (uint32_t)0U, + (uint32_t)8U, + (uint32_t)1U, + uint64_t *os = hash; + uint64_t x = Hacl_Impl_SHA2_Generic_h512[i]; + os[i] = x;); +} + +static inline void sha512_update(uint8_t *b, uint64_t *hash) +{ + uint64_t hash_old[8U] = { 0U }; + uint64_t ws[16U] = { 0U }; + memcpy(hash_old, hash, (uint32_t)8U * sizeof (uint64_t)); + uint8_t *b10 = b; + uint64_t u = load64_be(b10); + ws[0U] = u; + uint64_t u0 = load64_be(b10 + (uint32_t)8U); + ws[1U] = u0; + uint64_t u1 = load64_be(b10 + (uint32_t)16U); + ws[2U] = u1; + uint64_t u2 = load64_be(b10 + (uint32_t)24U); + ws[3U] = u2; + uint64_t u3 = load64_be(b10 + (uint32_t)32U); + ws[4U] = u3; + uint64_t u4 = load64_be(b10 + (uint32_t)40U); + ws[5U] = u4; + uint64_t u5 = load64_be(b10 + (uint32_t)48U); + ws[6U] = u5; + uint64_t u6 = load64_be(b10 + (uint32_t)56U); + ws[7U] = u6; + uint64_t u7 = load64_be(b10 + (uint32_t)64U); + ws[8U] = u7; + uint64_t u8 = load64_be(b10 + (uint32_t)72U); + ws[9U] = u8; + uint64_t u9 = load64_be(b10 + (uint32_t)80U); + ws[10U] = u9; + uint64_t u10 = load64_be(b10 + (uint32_t)88U); + ws[11U] = u10; + uint64_t u11 = load64_be(b10 + (uint32_t)96U); + ws[12U] = u11; + uint64_t u12 = load64_be(b10 + (uint32_t)104U); + ws[13U] = u12; + uint64_t u13 = load64_be(b10 + (uint32_t)112U); + ws[14U] = u13; + uint64_t u14 = load64_be(b10 + (uint32_t)120U); + ws[15U] = u14; + KRML_MAYBE_FOR5(i0, + (uint32_t)0U, + (uint32_t)5U, + (uint32_t)1U, + KRML_MAYBE_FOR16(i, + (uint32_t)0U, + (uint32_t)16U, + (uint32_t)1U, + uint64_t k_t = Hacl_Impl_SHA2_Generic_k384_512[(uint32_t)16U * i0 + i]; + uint64_t ws_t = ws[i]; + uint64_t a0 = hash[0U]; + uint64_t b0 = hash[1U]; + uint64_t c0 = hash[2U]; + uint64_t d0 = hash[3U]; + uint64_t e0 = hash[4U]; + uint64_t f0 = hash[5U]; + uint64_t g0 = hash[6U]; + uint64_t h02 = hash[7U]; + uint64_t k_e_t = k_t; + uint64_t + t1 = + h02 + + + ((e0 << (uint32_t)50U | e0 >> (uint32_t)14U) + ^ + ((e0 << (uint32_t)46U | e0 >> (uint32_t)18U) + ^ (e0 << (uint32_t)23U | e0 >> (uint32_t)41U))) + + ((e0 & f0) ^ (~e0 & g0)) + + k_e_t + + ws_t; + uint64_t + t2 = + ((a0 << (uint32_t)36U | a0 >> (uint32_t)28U) + ^ + ((a0 << (uint32_t)30U | a0 >> (uint32_t)34U) + ^ (a0 << (uint32_t)25U | a0 >> (uint32_t)39U))) + + ((a0 & b0) ^ ((a0 & c0) ^ (b0 & c0))); + uint64_t a1 = t1 + t2; + uint64_t b1 = a0; + uint64_t c1 = b0; + uint64_t d1 = c0; + uint64_t e1 = d0 + t1; + uint64_t f1 = e0; + uint64_t g1 = f0; + uint64_t h12 = g0; + hash[0U] = a1; + hash[1U] = b1; + hash[2U] = c1; + hash[3U] = d1; + hash[4U] = e1; + hash[5U] = f1; + hash[6U] = g1; + hash[7U] = h12;); + if (i0 < (uint32_t)4U) + { + KRML_MAYBE_FOR16(i, + (uint32_t)0U, + (uint32_t)16U, + (uint32_t)1U, + uint64_t t16 = ws[i]; + uint64_t t15 = ws[(i + (uint32_t)1U) % (uint32_t)16U]; + uint64_t t7 = ws[(i + (uint32_t)9U) % (uint32_t)16U]; + uint64_t t2 = ws[(i + (uint32_t)14U) % (uint32_t)16U]; + uint64_t + s1 = + (t2 << (uint32_t)45U | t2 >> (uint32_t)19U) + ^ ((t2 << (uint32_t)3U | t2 >> (uint32_t)61U) ^ t2 >> (uint32_t)6U); + uint64_t + s0 = + (t15 << (uint32_t)63U | t15 >> (uint32_t)1U) + ^ ((t15 << (uint32_t)56U | t15 >> (uint32_t)8U) ^ t15 >> (uint32_t)7U); + ws[i] = s1 + t7 + s0 + t16;); + }); + KRML_MAYBE_FOR8(i, + (uint32_t)0U, + (uint32_t)8U, + (uint32_t)1U, + uint64_t *os = hash; + uint64_t x = hash[i] + hash_old[i]; + os[i] = x;); +} + +static inline void sha512_update_nblocks(uint32_t len, uint8_t *b, uint64_t *st) +{ + uint32_t blocks = len / (uint32_t)128U; + for (uint32_t i = (uint32_t)0U; i < blocks; i++) + { + uint8_t *b0 = b; + uint8_t *mb = b0 + i * (uint32_t)128U; + sha512_update(mb, st); + } +} + +static inline void +sha512_update_last(FStar_UInt128_uint128 totlen, uint32_t len, uint8_t *b, uint64_t *hash) +{ + uint32_t blocks; + if (len + (uint32_t)16U + (uint32_t)1U <= (uint32_t)128U) + { + blocks = (uint32_t)1U; + } + else + { + blocks = (uint32_t)2U; + } + uint32_t fin = blocks * (uint32_t)128U; + uint8_t last[256U] = { 0U }; + uint8_t totlen_buf[16U] = { 0U }; + FStar_UInt128_uint128 total_len_bits = FStar_UInt128_shift_left(totlen, (uint32_t)3U); + store128_be(totlen_buf, total_len_bits); + uint8_t *b0 = b; + memcpy(last, b0, len * sizeof (uint8_t)); + last[len] = (uint8_t)0x80U; + memcpy(last + fin - (uint32_t)16U, totlen_buf, (uint32_t)16U * sizeof (uint8_t)); + uint8_t *last00 = last; + uint8_t *last10 = last + (uint32_t)128U; + uint8_t *l0 = last00; + uint8_t *l1 = last10; + uint8_t *lb0 = l0; + uint8_t *lb1 = l1; + uint8_t *last0 = lb0; + uint8_t *last1 = lb1; + sha512_update(last0, hash); + if (blocks > (uint32_t)1U) + { + sha512_update(last1, hash); + return; + } +} + +static inline void sha512_finish(uint64_t *st, uint8_t *h) +{ + uint8_t hbuf[64U] = { 0U }; + KRML_MAYBE_FOR8(i, + (uint32_t)0U, + (uint32_t)8U, + (uint32_t)1U, + store64_be(hbuf + i * (uint32_t)8U, st[i]);); + memcpy(h, hbuf, (uint32_t)64U * sizeof (uint8_t)); +} + +static inline void sha384_init(uint64_t *hash) +{ + KRML_MAYBE_FOR8(i, + (uint32_t)0U, + (uint32_t)8U, + (uint32_t)1U, + uint64_t *os = hash; + uint64_t x = Hacl_Impl_SHA2_Generic_h384[i]; + os[i] = x;); +} + +static inline void sha384_update_nblocks(uint32_t len, uint8_t *b, uint64_t *st) +{ + sha512_update_nblocks(len, b, st); +} + +static void +sha384_update_last(FStar_UInt128_uint128 totlen, uint32_t len, uint8_t *b, uint64_t *st) +{ + sha512_update_last(totlen, len, b, st); +} + +static inline void sha384_finish(uint64_t *st, uint8_t *h) +{ + uint8_t hbuf[64U] = { 0U }; + KRML_MAYBE_FOR8(i, + (uint32_t)0U, + (uint32_t)8U, + (uint32_t)1U, + store64_be(hbuf + i * (uint32_t)8U, st[i]);); + memcpy(h, hbuf, (uint32_t)48U * sizeof (uint8_t)); +} + /** Allocate initial state for the SHA2_256 hash. The state is to be freed by calling `free_256`. @@ -680,3 +903,434 @@ void Hacl_Streaming_SHA2_sha224(uint8_t *input, uint32_t input_len, uint8_t *dst sha224_finish(st, rb); } +Hacl_Streaming_SHA2_state_sha2_384 *Hacl_Streaming_SHA2_create_in_512(void) +{ + uint8_t *buf = (uint8_t *)KRML_HOST_CALLOC((uint32_t)128U, sizeof (uint8_t)); + uint64_t *block_state = (uint64_t *)KRML_HOST_CALLOC((uint32_t)8U, sizeof (uint64_t)); + Hacl_Streaming_SHA2_state_sha2_384 + s = { .block_state = block_state, .buf = buf, .total_len = (uint64_t)(uint32_t)0U }; + Hacl_Streaming_SHA2_state_sha2_384 + *p = + (Hacl_Streaming_SHA2_state_sha2_384 *)KRML_HOST_MALLOC(sizeof ( + Hacl_Streaming_SHA2_state_sha2_384 + )); + p[0U] = s; + Hacl_SHA2_Scalar32_sha512_init(block_state); + return p; +} + +/** +Copies the state passed as argument into a newly allocated state (deep copy). +The state is to be freed by calling `free_512`. Cloning the state this way is +useful, for instance, if your control-flow diverges and you need to feed +more (different) data into the hash in each branch. +*/ +Hacl_Streaming_SHA2_state_sha2_384 +*Hacl_Streaming_SHA2_copy_512(Hacl_Streaming_SHA2_state_sha2_384 *s0) +{ + Hacl_Streaming_SHA2_state_sha2_384 scrut = *s0; + uint64_t *block_state0 = scrut.block_state; + uint8_t *buf0 = scrut.buf; + uint64_t total_len0 = scrut.total_len; + uint8_t *buf = (uint8_t *)KRML_HOST_CALLOC((uint32_t)128U, sizeof (uint8_t)); + memcpy(buf, buf0, (uint32_t)128U * sizeof (uint8_t)); + uint64_t *block_state = (uint64_t *)KRML_HOST_CALLOC((uint32_t)8U, sizeof (uint64_t)); + memcpy(block_state, block_state0, (uint32_t)8U * sizeof (uint64_t)); + Hacl_Streaming_SHA2_state_sha2_384 + s = { .block_state = block_state, .buf = buf, .total_len = total_len0 }; + Hacl_Streaming_SHA2_state_sha2_384 + *p = + (Hacl_Streaming_SHA2_state_sha2_384 *)KRML_HOST_MALLOC(sizeof ( + Hacl_Streaming_SHA2_state_sha2_384 + )); + p[0U] = s; + return p; +} + +void Hacl_Streaming_SHA2_init_512(Hacl_Streaming_SHA2_state_sha2_384 *s) +{ + Hacl_Streaming_SHA2_state_sha2_384 scrut = *s; + uint8_t *buf = scrut.buf; + uint64_t *block_state = scrut.block_state; + Hacl_SHA2_Scalar32_sha512_init(block_state); + Hacl_Streaming_SHA2_state_sha2_384 + tmp = { .block_state = block_state, .buf = buf, .total_len = (uint64_t)(uint32_t)0U }; + s[0U] = tmp; +} + +static inline uint32_t +update_384_512(Hacl_Streaming_SHA2_state_sha2_384 *p, uint8_t *data, uint32_t len) +{ + Hacl_Streaming_SHA2_state_sha2_384 s = *p; + uint64_t total_len = s.total_len; + if ((uint64_t)len > (uint64_t)18446744073709551615U - total_len) + { + return (uint32_t)1U; + } + uint32_t sz; + if (total_len % (uint64_t)(uint32_t)128U == (uint64_t)0U && total_len > (uint64_t)0U) + { + sz = (uint32_t)128U; + } + else + { + sz = (uint32_t)(total_len % (uint64_t)(uint32_t)128U); + } + if (len <= (uint32_t)128U - sz) + { + Hacl_Streaming_SHA2_state_sha2_384 s1 = *p; + uint64_t *block_state1 = s1.block_state; + uint8_t *buf = s1.buf; + uint64_t total_len1 = s1.total_len; + uint32_t sz1; + if (total_len1 % (uint64_t)(uint32_t)128U == (uint64_t)0U && total_len1 > (uint64_t)0U) + { + sz1 = (uint32_t)128U; + } + else + { + sz1 = (uint32_t)(total_len1 % (uint64_t)(uint32_t)128U); + } + uint8_t *buf2 = buf + sz1; + memcpy(buf2, data, len * sizeof (uint8_t)); + uint64_t total_len2 = total_len1 + (uint64_t)len; + *p + = + ( + (Hacl_Streaming_SHA2_state_sha2_384){ + .block_state = block_state1, + .buf = buf, + .total_len = total_len2 + } + ); + } + else if (sz == (uint32_t)0U) + { + Hacl_Streaming_SHA2_state_sha2_384 s1 = *p; + uint64_t *block_state1 = s1.block_state; + uint8_t *buf = s1.buf; + uint64_t total_len1 = s1.total_len; + uint32_t sz1; + if (total_len1 % (uint64_t)(uint32_t)128U == (uint64_t)0U && total_len1 > (uint64_t)0U) + { + sz1 = (uint32_t)128U; + } + else + { + sz1 = (uint32_t)(total_len1 % (uint64_t)(uint32_t)128U); + } + if (!(sz1 == (uint32_t)0U)) + { + sha512_update_nblocks((uint32_t)128U, buf, block_state1); + } + uint32_t ite; + if ((uint64_t)len % (uint64_t)(uint32_t)128U == (uint64_t)0U && (uint64_t)len > (uint64_t)0U) + { + ite = (uint32_t)128U; + } + else + { + ite = (uint32_t)((uint64_t)len % (uint64_t)(uint32_t)128U); + } + uint32_t n_blocks = (len - ite) / (uint32_t)128U; + uint32_t data1_len = n_blocks * (uint32_t)128U; + uint32_t data2_len = len - data1_len; + uint8_t *data1 = data; + uint8_t *data2 = data + data1_len; + sha512_update_nblocks(data1_len, data1, block_state1); + uint8_t *dst = buf; + memcpy(dst, data2, data2_len * sizeof (uint8_t)); + *p + = + ( + (Hacl_Streaming_SHA2_state_sha2_384){ + .block_state = block_state1, + .buf = buf, + .total_len = total_len1 + (uint64_t)len + } + ); + } + else + { + uint32_t diff = (uint32_t)128U - sz; + uint8_t *data1 = data; + uint8_t *data2 = data + diff; + Hacl_Streaming_SHA2_state_sha2_384 s1 = *p; + uint64_t *block_state10 = s1.block_state; + uint8_t *buf0 = s1.buf; + uint64_t total_len10 = s1.total_len; + uint32_t sz10; + if (total_len10 % (uint64_t)(uint32_t)128U == (uint64_t)0U && total_len10 > (uint64_t)0U) + { + sz10 = (uint32_t)128U; + } + else + { + sz10 = (uint32_t)(total_len10 % (uint64_t)(uint32_t)128U); + } + uint8_t *buf2 = buf0 + sz10; + memcpy(buf2, data1, diff * sizeof (uint8_t)); + uint64_t total_len2 = total_len10 + (uint64_t)diff; + *p + = + ( + (Hacl_Streaming_SHA2_state_sha2_384){ + .block_state = block_state10, + .buf = buf0, + .total_len = total_len2 + } + ); + Hacl_Streaming_SHA2_state_sha2_384 s10 = *p; + uint64_t *block_state1 = s10.block_state; + uint8_t *buf = s10.buf; + uint64_t total_len1 = s10.total_len; + uint32_t sz1; + if (total_len1 % (uint64_t)(uint32_t)128U == (uint64_t)0U && total_len1 > (uint64_t)0U) + { + sz1 = (uint32_t)128U; + } + else + { + sz1 = (uint32_t)(total_len1 % (uint64_t)(uint32_t)128U); + } + if (!(sz1 == (uint32_t)0U)) + { + sha512_update_nblocks((uint32_t)128U, buf, block_state1); + } + uint32_t ite; + if + ( + (uint64_t)(len - diff) + % (uint64_t)(uint32_t)128U + == (uint64_t)0U + && (uint64_t)(len - diff) > (uint64_t)0U + ) + { + ite = (uint32_t)128U; + } + else + { + ite = (uint32_t)((uint64_t)(len - diff) % (uint64_t)(uint32_t)128U); + } + uint32_t n_blocks = (len - diff - ite) / (uint32_t)128U; + uint32_t data1_len = n_blocks * (uint32_t)128U; + uint32_t data2_len = len - diff - data1_len; + uint8_t *data11 = data2; + uint8_t *data21 = data2 + data1_len; + sha512_update_nblocks(data1_len, data11, block_state1); + uint8_t *dst = buf; + memcpy(dst, data21, data2_len * sizeof (uint8_t)); + *p + = + ( + (Hacl_Streaming_SHA2_state_sha2_384){ + .block_state = block_state1, + .buf = buf, + .total_len = total_len1 + (uint64_t)(len - diff) + } + ); + } + return (uint32_t)0U; +} + +/** +Feed an arbitrary amount of data into the hash. This function returns 0 for +success, or 1 if the combined length of all of the data passed to `update_512` +(since the last call to `init_512`) exceeds 2^125-1 bytes. + +This function is identical to the update function for SHA2_384. +*/ +uint32_t +Hacl_Streaming_SHA2_update_512( + Hacl_Streaming_SHA2_state_sha2_384 *p, + uint8_t *input, + uint32_t input_len +) +{ + return update_384_512(p, input, input_len); +} + +/** +Write the resulting hash into `dst`, an array of 64 bytes. The state remains +valid after a call to `finish_512`, meaning the user may feed more data into +the hash via `update_512`. (The finish_512 function operates on an internal copy of +the state and therefore does not invalidate the client-held state `p`.) +*/ +void Hacl_Streaming_SHA2_finish_512(Hacl_Streaming_SHA2_state_sha2_384 *p, uint8_t *dst) +{ + Hacl_Streaming_SHA2_state_sha2_384 scrut = *p; + uint64_t *block_state = scrut.block_state; + uint8_t *buf_ = scrut.buf; + uint64_t total_len = scrut.total_len; + uint32_t r; + if (total_len % (uint64_t)(uint32_t)128U == (uint64_t)0U && total_len > (uint64_t)0U) + { + r = (uint32_t)128U; + } + else + { + r = (uint32_t)(total_len % (uint64_t)(uint32_t)128U); + } + uint8_t *buf_1 = buf_; + uint64_t tmp_block_state[8U] = { 0U }; + memcpy(tmp_block_state, block_state, (uint32_t)8U * sizeof (uint64_t)); + uint32_t ite; + if (r % (uint32_t)128U == (uint32_t)0U && r > (uint32_t)0U) + { + ite = (uint32_t)128U; + } + else + { + ite = r % (uint32_t)128U; + } + uint8_t *buf_last = buf_1 + r - ite; + uint8_t *buf_multi = buf_1; + sha512_update_nblocks((uint32_t)0U, buf_multi, tmp_block_state); + uint64_t prev_len_last = total_len - (uint64_t)r; + sha512_update_last(FStar_UInt128_add(FStar_UInt128_uint64_to_uint128(prev_len_last), + FStar_UInt128_uint64_to_uint128((uint64_t)r)), + r, + buf_last, + tmp_block_state); + sha512_finish(tmp_block_state, dst); +} + +/** +Free a state allocated with `create_in_512`. + +This function is identical to the free function for SHA2_384. +*/ +void Hacl_Streaming_SHA2_free_512(Hacl_Streaming_SHA2_state_sha2_384 *s) +{ + Hacl_Streaming_SHA2_state_sha2_384 scrut = *s; + uint8_t *buf = scrut.buf; + uint64_t *block_state = scrut.block_state; + KRML_HOST_FREE(block_state); + KRML_HOST_FREE(buf); + KRML_HOST_FREE(s); +} + +/** +Hash `input`, of len `input_len`, into `dst`, an array of 64 bytes. +*/ +void Hacl_Streaming_SHA2_sha512(uint8_t *input, uint32_t input_len, uint8_t *dst) +{ + uint8_t *ib = input; + uint8_t *rb = dst; + uint64_t st[8U] = { 0U }; + Hacl_SHA2_Scalar32_sha512_init(st); + uint32_t rem = input_len % (uint32_t)128U; + FStar_UInt128_uint128 len_ = FStar_UInt128_uint64_to_uint128((uint64_t)input_len); + sha512_update_nblocks(input_len, ib, st); + uint32_t rem1 = input_len % (uint32_t)128U; + uint8_t *b0 = ib; + uint8_t *lb = b0 + input_len - rem1; + sha512_update_last(len_, rem, lb, st); + sha512_finish(st, rb); +} + +Hacl_Streaming_SHA2_state_sha2_384 *Hacl_Streaming_SHA2_create_in_384(void) +{ + uint8_t *buf = (uint8_t *)KRML_HOST_CALLOC((uint32_t)128U, sizeof (uint8_t)); + uint64_t *block_state = (uint64_t *)KRML_HOST_CALLOC((uint32_t)8U, sizeof (uint64_t)); + Hacl_Streaming_SHA2_state_sha2_384 + s = { .block_state = block_state, .buf = buf, .total_len = (uint64_t)(uint32_t)0U }; + Hacl_Streaming_SHA2_state_sha2_384 + *p = + (Hacl_Streaming_SHA2_state_sha2_384 *)KRML_HOST_MALLOC(sizeof ( + Hacl_Streaming_SHA2_state_sha2_384 + )); + p[0U] = s; + sha384_init(block_state); + return p; +} + +void Hacl_Streaming_SHA2_init_384(Hacl_Streaming_SHA2_state_sha2_384 *s) +{ + Hacl_Streaming_SHA2_state_sha2_384 scrut = *s; + uint8_t *buf = scrut.buf; + uint64_t *block_state = scrut.block_state; + sha384_init(block_state); + Hacl_Streaming_SHA2_state_sha2_384 + tmp = { .block_state = block_state, .buf = buf, .total_len = (uint64_t)(uint32_t)0U }; + s[0U] = tmp; +} + +uint32_t +Hacl_Streaming_SHA2_update_384( + Hacl_Streaming_SHA2_state_sha2_384 *p, + uint8_t *input, + uint32_t input_len +) +{ + return update_384_512(p, input, input_len); +} + +/** +Write the resulting hash into `dst`, an array of 48 bytes. The state remains +valid after a call to `finish_384`, meaning the user may feed more data into +the hash via `update_384`. +*/ +void Hacl_Streaming_SHA2_finish_384(Hacl_Streaming_SHA2_state_sha2_384 *p, uint8_t *dst) +{ + Hacl_Streaming_SHA2_state_sha2_384 scrut = *p; + uint64_t *block_state = scrut.block_state; + uint8_t *buf_ = scrut.buf; + uint64_t total_len = scrut.total_len; + uint32_t r; + if (total_len % (uint64_t)(uint32_t)128U == (uint64_t)0U && total_len > (uint64_t)0U) + { + r = (uint32_t)128U; + } + else + { + r = (uint32_t)(total_len % (uint64_t)(uint32_t)128U); + } + uint8_t *buf_1 = buf_; + uint64_t tmp_block_state[8U] = { 0U }; + memcpy(tmp_block_state, block_state, (uint32_t)8U * sizeof (uint64_t)); + uint32_t ite; + if (r % (uint32_t)128U == (uint32_t)0U && r > (uint32_t)0U) + { + ite = (uint32_t)128U; + } + else + { + ite = r % (uint32_t)128U; + } + uint8_t *buf_last = buf_1 + r - ite; + uint8_t *buf_multi = buf_1; + sha384_update_nblocks((uint32_t)0U, buf_multi, tmp_block_state); + uint64_t prev_len_last = total_len - (uint64_t)r; + sha384_update_last(FStar_UInt128_add(FStar_UInt128_uint64_to_uint128(prev_len_last), + FStar_UInt128_uint64_to_uint128((uint64_t)r)), + r, + buf_last, + tmp_block_state); + sha384_finish(tmp_block_state, dst); +} + +void Hacl_Streaming_SHA2_free_384(Hacl_Streaming_SHA2_state_sha2_384 *p) +{ + Hacl_Streaming_SHA2_free_512(p); +} + +/** +Hash `input`, of len `input_len`, into `dst`, an array of 48 bytes. +*/ +void Hacl_Streaming_SHA2_sha384(uint8_t *input, uint32_t input_len, uint8_t *dst) +{ + uint8_t *ib = input; + uint8_t *rb = dst; + uint64_t st[8U] = { 0U }; + sha384_init(st); + uint32_t rem = input_len % (uint32_t)128U; + FStar_UInt128_uint128 len_ = FStar_UInt128_uint64_to_uint128((uint64_t)input_len); + sha384_update_nblocks(input_len, ib, st); + uint32_t rem1 = input_len % (uint32_t)128U; + uint8_t *b0 = ib; + uint8_t *lb = b0 + input_len - rem1; + sha384_update_last(len_, rem, lb, st); + sha384_finish(st, rb); +} + diff --git a/Modules/_hacl/Hacl_Streaming_SHA2.h b/Modules/_hacl/Hacl_Streaming_SHA2.h index c83a835afe70fd..2c905854f336fd 100644 --- a/Modules/_hacl/Hacl_Streaming_SHA2.h +++ b/Modules/_hacl/Hacl_Streaming_SHA2.h @@ -32,13 +32,12 @@ extern "C" { #include #include "python_hacl_namespaces.h" -#include "krml/FStar_UInt_8_16_32_64.h" +#include "krml/types.h" #include "krml/lowstar_endianness.h" #include "krml/internal/target.h" - typedef struct Hacl_Streaming_SHA2_state_sha2_224_s { uint32_t *block_state; @@ -49,6 +48,16 @@ Hacl_Streaming_SHA2_state_sha2_224; typedef Hacl_Streaming_SHA2_state_sha2_224 Hacl_Streaming_SHA2_state_sha2_256; +typedef struct Hacl_Streaming_SHA2_state_sha2_384_s +{ + uint64_t *block_state; + uint8_t *buf; + uint64_t total_len; +} +Hacl_Streaming_SHA2_state_sha2_384; + +typedef Hacl_Streaming_SHA2_state_sha2_384 Hacl_Streaming_SHA2_state_sha2_512; + /** Allocate initial state for the SHA2_256 hash. The state is to be freed by calling `free_256`. @@ -128,6 +137,78 @@ Hash `input`, of len `input_len`, into `dst`, an array of 28 bytes. */ void Hacl_Streaming_SHA2_sha224(uint8_t *input, uint32_t input_len, uint8_t *dst); +Hacl_Streaming_SHA2_state_sha2_384 *Hacl_Streaming_SHA2_create_in_512(void); + +/** +Copies the state passed as argument into a newly allocated state (deep copy). +The state is to be freed by calling `free_512`. Cloning the state this way is +useful, for instance, if your control-flow diverges and you need to feed +more (different) data into the hash in each branch. +*/ +Hacl_Streaming_SHA2_state_sha2_384 +*Hacl_Streaming_SHA2_copy_512(Hacl_Streaming_SHA2_state_sha2_384 *s0); + +void Hacl_Streaming_SHA2_init_512(Hacl_Streaming_SHA2_state_sha2_384 *s); + +/** +Feed an arbitrary amount of data into the hash. This function returns 0 for +success, or 1 if the combined length of all of the data passed to `update_512` +(since the last call to `init_512`) exceeds 2^125-1 bytes. + +This function is identical to the update function for SHA2_384. +*/ +uint32_t +Hacl_Streaming_SHA2_update_512( + Hacl_Streaming_SHA2_state_sha2_384 *p, + uint8_t *input, + uint32_t input_len +); + +/** +Write the resulting hash into `dst`, an array of 64 bytes. The state remains +valid after a call to `finish_512`, meaning the user may feed more data into +the hash via `update_512`. (The finish_512 function operates on an internal copy of +the state and therefore does not invalidate the client-held state `p`.) +*/ +void Hacl_Streaming_SHA2_finish_512(Hacl_Streaming_SHA2_state_sha2_384 *p, uint8_t *dst); + +/** +Free a state allocated with `create_in_512`. + +This function is identical to the free function for SHA2_384. +*/ +void Hacl_Streaming_SHA2_free_512(Hacl_Streaming_SHA2_state_sha2_384 *s); + +/** +Hash `input`, of len `input_len`, into `dst`, an array of 64 bytes. +*/ +void Hacl_Streaming_SHA2_sha512(uint8_t *input, uint32_t input_len, uint8_t *dst); + +Hacl_Streaming_SHA2_state_sha2_384 *Hacl_Streaming_SHA2_create_in_384(void); + +void Hacl_Streaming_SHA2_init_384(Hacl_Streaming_SHA2_state_sha2_384 *s); + +uint32_t +Hacl_Streaming_SHA2_update_384( + Hacl_Streaming_SHA2_state_sha2_384 *p, + uint8_t *input, + uint32_t input_len +); + +/** +Write the resulting hash into `dst`, an array of 48 bytes. The state remains +valid after a call to `finish_384`, meaning the user may feed more data into +the hash via `update_384`. +*/ +void Hacl_Streaming_SHA2_finish_384(Hacl_Streaming_SHA2_state_sha2_384 *p, uint8_t *dst); + +void Hacl_Streaming_SHA2_free_384(Hacl_Streaming_SHA2_state_sha2_384 *p); + +/** +Hash `input`, of len `input_len`, into `dst`, an array of 48 bytes. +*/ +void Hacl_Streaming_SHA2_sha384(uint8_t *input, uint32_t input_len, uint8_t *dst); + #if defined(__cplusplus) } #endif diff --git a/Modules/_hacl/include/krml/FStar_UInt128_Verified.h b/Modules/_hacl/include/krml/FStar_UInt128_Verified.h new file mode 100644 index 00000000000000..ee160193539e28 --- /dev/null +++ b/Modules/_hacl/include/krml/FStar_UInt128_Verified.h @@ -0,0 +1,347 @@ +/* + Copyright (c) INRIA and Microsoft Corporation. All rights reserved. + Licensed under the Apache 2.0 License. +*/ + + +#ifndef __FStar_UInt128_Verified_H +#define __FStar_UInt128_Verified_H + + + +#include "FStar_UInt_8_16_32_64.h" +#include +#include +#include "krml/types.h" +#include "krml/internal/target.h" +static inline uint64_t FStar_UInt128_constant_time_carry(uint64_t a, uint64_t b) +{ + return (a ^ ((a ^ b) | ((a - b) ^ b))) >> (uint32_t)63U; +} + +static inline uint64_t FStar_UInt128_carry(uint64_t a, uint64_t b) +{ + return FStar_UInt128_constant_time_carry(a, b); +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_add(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 lit; + lit.low = a.low + b.low; + lit.high = a.high + b.high + FStar_UInt128_carry(a.low + b.low, b.low); + return lit; +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_add_underspec(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 lit; + lit.low = a.low + b.low; + lit.high = a.high + b.high + FStar_UInt128_carry(a.low + b.low, b.low); + return lit; +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_add_mod(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 lit; + lit.low = a.low + b.low; + lit.high = a.high + b.high + FStar_UInt128_carry(a.low + b.low, b.low); + return lit; +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_sub(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 lit; + lit.low = a.low - b.low; + lit.high = a.high - b.high - FStar_UInt128_carry(a.low, a.low - b.low); + return lit; +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_sub_underspec(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 lit; + lit.low = a.low - b.low; + lit.high = a.high - b.high - FStar_UInt128_carry(a.low, a.low - b.low); + return lit; +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_sub_mod_impl(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 lit; + lit.low = a.low - b.low; + lit.high = a.high - b.high - FStar_UInt128_carry(a.low, a.low - b.low); + return lit; +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_sub_mod(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + return FStar_UInt128_sub_mod_impl(a, b); +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_logand(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 lit; + lit.low = a.low & b.low; + lit.high = a.high & b.high; + return lit; +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_logxor(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 lit; + lit.low = a.low ^ b.low; + lit.high = a.high ^ b.high; + return lit; +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_logor(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 lit; + lit.low = a.low | b.low; + lit.high = a.high | b.high; + return lit; +} + +static inline FStar_UInt128_uint128 FStar_UInt128_lognot(FStar_UInt128_uint128 a) +{ + FStar_UInt128_uint128 lit; + lit.low = ~a.low; + lit.high = ~a.high; + return lit; +} + +static uint32_t FStar_UInt128_u32_64 = (uint32_t)64U; + +static inline uint64_t FStar_UInt128_add_u64_shift_left(uint64_t hi, uint64_t lo, uint32_t s) +{ + return (hi << s) + (lo >> (FStar_UInt128_u32_64 - s)); +} + +static inline uint64_t +FStar_UInt128_add_u64_shift_left_respec(uint64_t hi, uint64_t lo, uint32_t s) +{ + return FStar_UInt128_add_u64_shift_left(hi, lo, s); +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_shift_left_small(FStar_UInt128_uint128 a, uint32_t s) +{ + if (s == (uint32_t)0U) + { + return a; + } + else + { + FStar_UInt128_uint128 lit; + lit.low = a.low << s; + lit.high = FStar_UInt128_add_u64_shift_left_respec(a.high, a.low, s); + return lit; + } +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_shift_left_large(FStar_UInt128_uint128 a, uint32_t s) +{ + FStar_UInt128_uint128 lit; + lit.low = (uint64_t)0U; + lit.high = a.low << (s - FStar_UInt128_u32_64); + return lit; +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_shift_left(FStar_UInt128_uint128 a, uint32_t s) +{ + if (s < FStar_UInt128_u32_64) + { + return FStar_UInt128_shift_left_small(a, s); + } + else + { + return FStar_UInt128_shift_left_large(a, s); + } +} + +static inline uint64_t FStar_UInt128_add_u64_shift_right(uint64_t hi, uint64_t lo, uint32_t s) +{ + return (lo >> s) + (hi << (FStar_UInt128_u32_64 - s)); +} + +static inline uint64_t +FStar_UInt128_add_u64_shift_right_respec(uint64_t hi, uint64_t lo, uint32_t s) +{ + return FStar_UInt128_add_u64_shift_right(hi, lo, s); +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_shift_right_small(FStar_UInt128_uint128 a, uint32_t s) +{ + if (s == (uint32_t)0U) + { + return a; + } + else + { + FStar_UInt128_uint128 lit; + lit.low = FStar_UInt128_add_u64_shift_right_respec(a.high, a.low, s); + lit.high = a.high >> s; + return lit; + } +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_shift_right_large(FStar_UInt128_uint128 a, uint32_t s) +{ + FStar_UInt128_uint128 lit; + lit.low = a.high >> (s - FStar_UInt128_u32_64); + lit.high = (uint64_t)0U; + return lit; +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_shift_right(FStar_UInt128_uint128 a, uint32_t s) +{ + if (s < FStar_UInt128_u32_64) + { + return FStar_UInt128_shift_right_small(a, s); + } + else + { + return FStar_UInt128_shift_right_large(a, s); + } +} + +static inline bool FStar_UInt128_eq(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + return a.low == b.low && a.high == b.high; +} + +static inline bool FStar_UInt128_gt(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + return a.high > b.high || (a.high == b.high && a.low > b.low); +} + +static inline bool FStar_UInt128_lt(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + return a.high < b.high || (a.high == b.high && a.low < b.low); +} + +static inline bool FStar_UInt128_gte(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + return a.high > b.high || (a.high == b.high && a.low >= b.low); +} + +static inline bool FStar_UInt128_lte(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + return a.high < b.high || (a.high == b.high && a.low <= b.low); +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_eq_mask(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 lit; + lit.low = FStar_UInt64_eq_mask(a.low, b.low) & FStar_UInt64_eq_mask(a.high, b.high); + lit.high = FStar_UInt64_eq_mask(a.low, b.low) & FStar_UInt64_eq_mask(a.high, b.high); + return lit; +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_gte_mask(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 lit; + lit.low = + (FStar_UInt64_gte_mask(a.high, b.high) & ~FStar_UInt64_eq_mask(a.high, b.high)) + | (FStar_UInt64_eq_mask(a.high, b.high) & FStar_UInt64_gte_mask(a.low, b.low)); + lit.high = + (FStar_UInt64_gte_mask(a.high, b.high) & ~FStar_UInt64_eq_mask(a.high, b.high)) + | (FStar_UInt64_eq_mask(a.high, b.high) & FStar_UInt64_gte_mask(a.low, b.low)); + return lit; +} + +static inline FStar_UInt128_uint128 FStar_UInt128_uint64_to_uint128(uint64_t a) +{ + FStar_UInt128_uint128 lit; + lit.low = a; + lit.high = (uint64_t)0U; + return lit; +} + +static inline uint64_t FStar_UInt128_uint128_to_uint64(FStar_UInt128_uint128 a) +{ + return a.low; +} + +static inline uint64_t FStar_UInt128_u64_mod_32(uint64_t a) +{ + return a & (uint64_t)0xffffffffU; +} + +static uint32_t FStar_UInt128_u32_32 = (uint32_t)32U; + +static inline uint64_t FStar_UInt128_u32_combine(uint64_t hi, uint64_t lo) +{ + return lo + (hi << FStar_UInt128_u32_32); +} + +static inline FStar_UInt128_uint128 FStar_UInt128_mul32(uint64_t x, uint32_t y) +{ + FStar_UInt128_uint128 lit; + lit.low = + FStar_UInt128_u32_combine((x >> FStar_UInt128_u32_32) + * (uint64_t)y + + (FStar_UInt128_u64_mod_32(x) * (uint64_t)y >> FStar_UInt128_u32_32), + FStar_UInt128_u64_mod_32(FStar_UInt128_u64_mod_32(x) * (uint64_t)y)); + lit.high = + ((x >> FStar_UInt128_u32_32) + * (uint64_t)y + + (FStar_UInt128_u64_mod_32(x) * (uint64_t)y >> FStar_UInt128_u32_32)) + >> FStar_UInt128_u32_32; + return lit; +} + +static inline uint64_t FStar_UInt128_u32_combine_(uint64_t hi, uint64_t lo) +{ + return lo + (hi << FStar_UInt128_u32_32); +} + +static inline FStar_UInt128_uint128 FStar_UInt128_mul_wide(uint64_t x, uint64_t y) +{ + FStar_UInt128_uint128 lit; + lit.low = + FStar_UInt128_u32_combine_(FStar_UInt128_u64_mod_32(x) + * (y >> FStar_UInt128_u32_32) + + + FStar_UInt128_u64_mod_32((x >> FStar_UInt128_u32_32) + * FStar_UInt128_u64_mod_32(y) + + (FStar_UInt128_u64_mod_32(x) * FStar_UInt128_u64_mod_32(y) >> FStar_UInt128_u32_32)), + FStar_UInt128_u64_mod_32(FStar_UInt128_u64_mod_32(x) * FStar_UInt128_u64_mod_32(y))); + lit.high = + (x >> FStar_UInt128_u32_32) + * (y >> FStar_UInt128_u32_32) + + + (((x >> FStar_UInt128_u32_32) + * FStar_UInt128_u64_mod_32(y) + + (FStar_UInt128_u64_mod_32(x) * FStar_UInt128_u64_mod_32(y) >> FStar_UInt128_u32_32)) + >> FStar_UInt128_u32_32) + + + ((FStar_UInt128_u64_mod_32(x) + * (y >> FStar_UInt128_u32_32) + + + FStar_UInt128_u64_mod_32((x >> FStar_UInt128_u32_32) + * FStar_UInt128_u64_mod_32(y) + + (FStar_UInt128_u64_mod_32(x) * FStar_UInt128_u64_mod_32(y) >> FStar_UInt128_u32_32))) + >> FStar_UInt128_u32_32); + return lit; +} + + +#define __FStar_UInt128_Verified_H_DEFINED +#endif diff --git a/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h b/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h index 3e2e4b32b22f96..965afc836fd12b 100644 --- a/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h +++ b/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h @@ -14,7 +14,7 @@ #include #include "krml/lowstar_endianness.h" -#include "krml/FStar_UInt_8_16_32_64.h" +#include "krml/types.h" #include "krml/internal/target.h" static inline uint64_t FStar_UInt64_eq_mask(uint64_t a, uint64_t b) { diff --git a/Modules/_hacl/include/krml/fstar_uint128_struct_endianness.h b/Modules/_hacl/include/krml/fstar_uint128_struct_endianness.h new file mode 100644 index 00000000000000..e2b6d62859a5f1 --- /dev/null +++ b/Modules/_hacl/include/krml/fstar_uint128_struct_endianness.h @@ -0,0 +1,68 @@ +/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. + Licensed under the Apache 2.0 License. */ + +#ifndef FSTAR_UINT128_STRUCT_ENDIANNESS_H +#define FSTAR_UINT128_STRUCT_ENDIANNESS_H + +/* Hand-written implementation of endianness-related uint128 functions + * for the extracted uint128 implementation */ + +/* Access 64-bit fields within the int128. */ +#define HIGH64_OF(x) ((x)->high) +#define LOW64_OF(x) ((x)->low) + +/* A series of definitions written using pointers. */ + +inline static void load128_le_(uint8_t *b, uint128_t *r) { + LOW64_OF(r) = load64_le(b); + HIGH64_OF(r) = load64_le(b + 8); +} + +inline static void store128_le_(uint8_t *b, uint128_t *n) { + store64_le(b, LOW64_OF(n)); + store64_le(b + 8, HIGH64_OF(n)); +} + +inline static void load128_be_(uint8_t *b, uint128_t *r) { + HIGH64_OF(r) = load64_be(b); + LOW64_OF(r) = load64_be(b + 8); +} + +inline static void store128_be_(uint8_t *b, uint128_t *n) { + store64_be(b, HIGH64_OF(n)); + store64_be(b + 8, LOW64_OF(n)); +} + +#ifndef KRML_NOSTRUCT_PASSING + +inline static uint128_t load128_le(uint8_t *b) { + uint128_t r; + load128_le_(b, &r); + return r; +} + +inline static void store128_le(uint8_t *b, uint128_t n) { + store128_le_(b, &n); +} + +inline static uint128_t load128_be(uint8_t *b) { + uint128_t r; + load128_be_(b, &r); + return r; +} + +inline static void store128_be(uint8_t *b, uint128_t n) { + store128_be_(b, &n); +} + +#else /* !defined(KRML_STRUCT_PASSING) */ + +# define print128 print128_ +# define load128_le load128_le_ +# define store128_le store128_le_ +# define load128_be load128_be_ +# define store128_be store128_be_ + +#endif /* KRML_STRUCT_PASSING */ + +#endif diff --git a/Modules/_hacl/include/krml/types.h b/Modules/_hacl/include/krml/types.h new file mode 100644 index 00000000000000..509f555536e4c6 --- /dev/null +++ b/Modules/_hacl/include/krml/types.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct FStar_UInt128_uint128_s { + uint64_t low; + uint64_t high; +} FStar_UInt128_uint128, uint128_t; + +#define KRML_VERIFIED_UINT128 + +#include "krml/lowstar_endianness.h" +#include "krml/fstar_uint128_struct_endianness.h" +#include "krml/FStar_UInt128_Verified.h" diff --git a/Modules/_hacl/include/python_hacl_namespaces.h b/Modules/_hacl/include/python_hacl_namespaces.h index af390459311fe8..65608d1fd283c4 100644 --- a/Modules/_hacl/include/python_hacl_namespaces.h +++ b/Modules/_hacl/include/python_hacl_namespaces.h @@ -10,19 +10,36 @@ #define Hacl_Streaming_SHA2_state_sha2_224_s python_hashlib_Hacl_Streaming_SHA2_state_sha2_224_s #define Hacl_Streaming_SHA2_state_sha2_224 python_hashlib_Hacl_Streaming_SHA2_state_sha2_224 #define Hacl_Streaming_SHA2_state_sha2_256 python_hashlib_Hacl_Streaming_SHA2_state_sha2_256 +#define Hacl_Streaming_SHA2_state_sha2_384_s python_hashlib_Hacl_Streaming_SHA2_state_sha2_384_s +#define Hacl_Streaming_SHA2_state_sha2_384 python_hashlib_Hacl_Streaming_SHA2_state_sha2_384 +#define Hacl_Streaming_SHA2_state_sha2_512 python_hashlib_Hacl_Streaming_SHA2_state_sha2_512 #define Hacl_Streaming_SHA2_create_in_256 python_hashlib_Hacl_Streaming_SHA2_create_in_256 #define Hacl_Streaming_SHA2_create_in_224 python_hashlib_Hacl_Streaming_SHA2_create_in_224 +#define Hacl_Streaming_SHA2_create_in_512 python_hashlib_Hacl_Streaming_SHA2_create_in_512 +#define Hacl_Streaming_SHA2_create_in_384 python_hashlib_Hacl_Streaming_SHA2_create_in_384 #define Hacl_Streaming_SHA2_copy_256 python_hashlib_Hacl_Streaming_SHA2_copy_256 #define Hacl_Streaming_SHA2_copy_224 python_hashlib_Hacl_Streaming_SHA2_copy_224 +#define Hacl_Streaming_SHA2_copy_512 python_hashlib_Hacl_Streaming_SHA2_copy_512 +#define Hacl_Streaming_SHA2_copy_384 python_hashlib_Hacl_Streaming_SHA2_copy_384 #define Hacl_Streaming_SHA2_init_256 python_hashlib_Hacl_Streaming_SHA2_init_256 #define Hacl_Streaming_SHA2_init_224 python_hashlib_Hacl_Streaming_SHA2_init_224 +#define Hacl_Streaming_SHA2_init_512 python_hashlib_Hacl_Streaming_SHA2_init_512 +#define Hacl_Streaming_SHA2_init_384 python_hashlib_Hacl_Streaming_SHA2_init_384 #define Hacl_Streaming_SHA2_update_256 python_hashlib_Hacl_Streaming_SHA2_update_256 #define Hacl_Streaming_SHA2_update_224 python_hashlib_Hacl_Streaming_SHA2_update_224 +#define Hacl_Streaming_SHA2_update_512 python_hashlib_Hacl_Streaming_SHA2_update_512 +#define Hacl_Streaming_SHA2_update_384 python_hashlib_Hacl_Streaming_SHA2_update_384 #define Hacl_Streaming_SHA2_finish_256 python_hashlib_Hacl_Streaming_SHA2_finish_256 #define Hacl_Streaming_SHA2_finish_224 python_hashlib_Hacl_Streaming_SHA2_finish_224 +#define Hacl_Streaming_SHA2_finish_512 python_hashlib_Hacl_Streaming_SHA2_finish_512 +#define Hacl_Streaming_SHA2_finish_384 python_hashlib_Hacl_Streaming_SHA2_finish_384 #define Hacl_Streaming_SHA2_free_256 python_hashlib_Hacl_Streaming_SHA2_free_256 #define Hacl_Streaming_SHA2_free_224 python_hashlib_Hacl_Streaming_SHA2_free_224 +#define Hacl_Streaming_SHA2_free_512 python_hashlib_Hacl_Streaming_SHA2_free_512 +#define Hacl_Streaming_SHA2_free_384 python_hashlib_Hacl_Streaming_SHA2_free_384 #define Hacl_Streaming_SHA2_sha256 python_hashlib_Hacl_Streaming_SHA2_sha256 #define Hacl_Streaming_SHA2_sha224 python_hashlib_Hacl_Streaming_SHA2_sha224 +#define Hacl_Streaming_SHA2_sha512 python_hashlib_Hacl_Streaming_SHA2_sha512 +#define Hacl_Streaming_SHA2_sha384 python_hashlib_Hacl_Streaming_SHA2_sha384 #endif // _PYTHON_HACL_NAMESPACES_H diff --git a/Modules/_hacl/internal/Hacl_SHA2_Generic.h b/Modules/_hacl/internal/Hacl_SHA2_Generic.h index 23f7cea1eb3884..6ac47f3cf7ed36 100644 --- a/Modules/_hacl/internal/Hacl_SHA2_Generic.h +++ b/Modules/_hacl/internal/Hacl_SHA2_Generic.h @@ -31,13 +31,10 @@ extern "C" { #endif #include -#include "krml/FStar_UInt_8_16_32_64.h" +#include "krml/types.h" #include "krml/lowstar_endianness.h" #include "krml/internal/target.h" - - - static const uint32_t Hacl_Impl_SHA2_Generic_h224[8U] = diff --git a/Modules/_hacl/refresh.sh b/Modules/_hacl/refresh.sh index 594873862a2db0..dba8cb3972ea17 100755 --- a/Modules/_hacl/refresh.sh +++ b/Modules/_hacl/refresh.sh @@ -22,7 +22,7 @@ fi # Update this when updating to a new version after verifying that the changes # the update brings in are good. -expected_hacl_star_rev=94aabbb4cf71347d3779a8db486c761403c6d036 +expected_hacl_star_rev=4751fc2b11639f651718abf8522fcc36902ca67c hacl_dir="$(realpath "$1")" cd "$(dirname "$0")" @@ -54,6 +54,8 @@ include_files=( declare -a lib_files lib_files=( krmllib/dist/minimal/FStar_UInt_8_16_32_64.h + krmllib/dist/minimal/fstar_uint128_struct_endianness.h + krmllib/dist/minimal/FStar_UInt128_Verified.h ) # C files for the algorithms themselves: current directory @@ -82,10 +84,27 @@ fi readarray -t all_files < <(find . -name '*.h' -or -name '*.c') -# types.h is a simple wrapper that defines the uint128 type then proceeds to -# include FStar_UInt_8_16_32_64.h; we jump the types.h step since our current -# selection of algorithms does not necessitate the use of uint128 -$sed -i 's!#include.*types.h"!#include "krml/FStar_UInt_8_16_32_64.h"!g' "${all_files[@]}" +# types.h originally contains a complex series of if-defs and auxiliary type +# definitions; here, we just need a proper uint128 type in scope +# is a simple wrapper that defines the uint128 type +cat > include/krml/types.h < + +typedef struct FStar_UInt128_uint128_s { + uint64_t low; + uint64_t high; +} FStar_UInt128_uint128, uint128_t; + +#define KRML_VERIFIED_UINT128 + +#include "krml/lowstar_endianness.h" +#include "krml/fstar_uint128_struct_endianness.h" +#include "krml/FStar_UInt128_Verified.h" +EOF +# Adjust the include path to reflect the local directory structure +$sed -i 's!#include.*types.h"!#include "krml/types.h"!g' "${all_files[@]}" $sed -i 's!#include.*compat.h"!!g' "${all_files[@]}" # FStar_UInt_8_16_32_64 contains definitions useful in the general case, but not @@ -103,22 +122,6 @@ $sed -i 's!#include.*Hacl_Krmllib.h"!!g' "${all_files[@]}" # included in $dist_files). $sed -i 's!#include.*internal/Hacl_Streaming_SHA2.h"!#include "Hacl_Streaming_SHA2.h"!g' "${all_files[@]}" -# The SHA2 file contains all variants of SHA2. We strip 384 and 512 for the time -# being, to be included later. -# This regexp matches a separator (two new lines), followed by: -# -# * -# ... 384 or 512 ... { -# * -# } -# -# The first non-empty lines are the comment block. The second ... may spill over -# the next following lines if the arguments are printed in one-per-line mode. -$sed -i -z 's/\n\n\([^\n]\+\n\)*[^\n]*\(384\|512\)[^{]*{\n\?\( [^\n]*\n\)*}//g' Hacl_Streaming_SHA2.c - -# Same thing with function prototypes -$sed -i -z 's/\n\n\([^\n]\+\n\)*[^\n]*\(384\|512\)[^;]*;//g' Hacl_Streaming_SHA2.h - # Use globally unique names for the Hacl_ C APIs to avoid linkage conflicts. $sed -i -z 's!#include \n!#include \n#include "python_hacl_namespaces.h"\n!' Hacl_Streaming_SHA2.h diff --git a/Modules/sha256module.c b/Modules/sha256module.c index 630e4bf03bbe96..301c9837bb6720 100644 --- a/Modules/sha256module.c +++ b/Modules/sha256module.c @@ -8,6 +8,7 @@ Andrew Kuchling (amk@amk.ca) Greg Stein (gstein@lyra.org) Trevor Perrin (trevp@trevp.net) + Jonathan Protzenko (jonathan@protzenko.fr) Copyright (C) 2005-2007 Gregory P. Smith (greg@krypto.org) Licensed to PSF under a Contributor Agreement. diff --git a/Modules/sha512module.c b/Modules/sha512module.c index bf4408b455f2c4..d7dfed4e5db03a 100644 --- a/Modules/sha512module.c +++ b/Modules/sha512module.c @@ -8,6 +8,7 @@ Andrew Kuchling (amk@amk.ca) Greg Stein (gstein@lyra.org) Trevor Perrin (trevp@trevp.net) + Jonathan Protzenko (jonathan@protzenko.fr) Copyright (C) 2005-2007 Gregory P. Smith (greg@krypto.org) Licensed to PSF under a Contributor Agreement. @@ -31,400 +32,32 @@ class SHA512Type "SHAobject *" "&PyType_Type" [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=81a3ccde92bcfe8d]*/ -/* Some useful types */ - -typedef unsigned char SHA_BYTE; -typedef uint32_t SHA_INT32; /* 32-bit integer */ -typedef uint64_t SHA_INT64; /* 64-bit integer */ /* The SHA block size and message digest sizes, in bytes */ #define SHA_BLOCKSIZE 128 #define SHA_DIGESTSIZE 64 -/* The structure for storing SHA info */ +/* The SHA2-384 and SHA2-512 implementations defer to the HACL* verified + * library. */ + +#include "_hacl/Hacl_Streaming_SHA2.h" typedef struct { PyObject_HEAD - SHA_INT64 digest[8]; /* Message digest */ - SHA_INT32 count_lo, count_hi; /* 64-bit bit count */ - SHA_BYTE data[SHA_BLOCKSIZE]; /* SHA data buffer */ - int local; /* unprocessed amount in data */ int digestsize; + Hacl_Streaming_SHA2_state_sha2_512 *state; } SHAobject; #include "clinic/sha512module.c.h" -/* When run on a little-endian CPU we need to perform byte reversal on an - array of longwords. */ - -#if PY_LITTLE_ENDIAN -static void longReverse(SHA_INT64 *buffer, int byteCount) -{ - byteCount /= sizeof(*buffer); - for (; byteCount--; buffer++) { - *buffer = _Py_bswap64(*buffer); - } -} -#endif static void SHAcopy(SHAobject *src, SHAobject *dest) { - dest->local = src->local; dest->digestsize = src->digestsize; - dest->count_lo = src->count_lo; - dest->count_hi = src->count_hi; - memcpy(dest->digest, src->digest, sizeof(src->digest)); - memcpy(dest->data, src->data, sizeof(src->data)); -} - - -/* ------------------------------------------------------------------------ - * - * This code for the SHA-512 algorithm was noted as public domain. The - * original headers are pasted below. - * - * Several changes have been made to make it more compatible with the - * Python environment and desired interface. - * - */ - -/* LibTomCrypt, modular cryptographic library -- Tom St Denis - * - * LibTomCrypt is a library that provides various cryptographic - * algorithms in a highly modular and flexible manner. - * - * The library is free for all purposes without any express - * guarantee it works. - * - * Tom St Denis, tomstdenis@iahu.ca, https://www.libtom.net - */ - - -/* SHA512 by Tom St Denis */ - -/* Various logical functions */ -#define ROR64(x, y) \ - ( ((((x) & 0xFFFFFFFFFFFFFFFFULL)>>((unsigned long long)(y) & 63)) | \ - ((x)<<((unsigned long long)(64-((y) & 63))))) & 0xFFFFFFFFFFFFFFFFULL) -#define Ch(x,y,z) (z ^ (x & (y ^ z))) -#define Maj(x,y,z) (((x | y) & z) | (x & y)) -#define S(x, n) ROR64((x),(n)) -#define R(x, n) (((x) & 0xFFFFFFFFFFFFFFFFULL) >> ((unsigned long long)n)) -#define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39)) -#define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41)) -#define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7)) -#define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6)) - - -static void -sha512_transform(SHAobject *sha_info) -{ - int i; - SHA_INT64 S[8], W[80], t0, t1; - - memcpy(W, sha_info->data, sizeof(sha_info->data)); -#if PY_LITTLE_ENDIAN - longReverse(W, (int)sizeof(sha_info->data)); -#endif - - for (i = 16; i < 80; ++i) { - W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; - } - for (i = 0; i < 8; ++i) { - S[i] = sha_info->digest[i]; - } - - /* Compress */ -#define RND(a,b,c,d,e,f,g,h,i,ki) \ - t0 = h + Sigma1(e) + Ch(e, f, g) + ki + W[i]; \ - t1 = Sigma0(a) + Maj(a, b, c); \ - d += t0; \ - h = t0 + t1; - - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],0,0x428a2f98d728ae22ULL); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],1,0x7137449123ef65cdULL); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],2,0xb5c0fbcfec4d3b2fULL); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],3,0xe9b5dba58189dbbcULL); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],4,0x3956c25bf348b538ULL); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],5,0x59f111f1b605d019ULL); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],6,0x923f82a4af194f9bULL); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],7,0xab1c5ed5da6d8118ULL); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],8,0xd807aa98a3030242ULL); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],9,0x12835b0145706fbeULL); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],10,0x243185be4ee4b28cULL); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],11,0x550c7dc3d5ffb4e2ULL); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],12,0x72be5d74f27b896fULL); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],13,0x80deb1fe3b1696b1ULL); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],14,0x9bdc06a725c71235ULL); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],15,0xc19bf174cf692694ULL); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],16,0xe49b69c19ef14ad2ULL); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],17,0xefbe4786384f25e3ULL); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],18,0x0fc19dc68b8cd5b5ULL); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],19,0x240ca1cc77ac9c65ULL); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],20,0x2de92c6f592b0275ULL); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],21,0x4a7484aa6ea6e483ULL); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],22,0x5cb0a9dcbd41fbd4ULL); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],23,0x76f988da831153b5ULL); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],24,0x983e5152ee66dfabULL); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],25,0xa831c66d2db43210ULL); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],26,0xb00327c898fb213fULL); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],27,0xbf597fc7beef0ee4ULL); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],28,0xc6e00bf33da88fc2ULL); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],29,0xd5a79147930aa725ULL); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],30,0x06ca6351e003826fULL); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],31,0x142929670a0e6e70ULL); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],32,0x27b70a8546d22ffcULL); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],33,0x2e1b21385c26c926ULL); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],34,0x4d2c6dfc5ac42aedULL); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],35,0x53380d139d95b3dfULL); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],36,0x650a73548baf63deULL); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],37,0x766a0abb3c77b2a8ULL); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],38,0x81c2c92e47edaee6ULL); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],39,0x92722c851482353bULL); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],40,0xa2bfe8a14cf10364ULL); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],41,0xa81a664bbc423001ULL); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],42,0xc24b8b70d0f89791ULL); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],43,0xc76c51a30654be30ULL); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],44,0xd192e819d6ef5218ULL); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],45,0xd69906245565a910ULL); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],46,0xf40e35855771202aULL); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],47,0x106aa07032bbd1b8ULL); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],48,0x19a4c116b8d2d0c8ULL); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],49,0x1e376c085141ab53ULL); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],50,0x2748774cdf8eeb99ULL); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],51,0x34b0bcb5e19b48a8ULL); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],52,0x391c0cb3c5c95a63ULL); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],53,0x4ed8aa4ae3418acbULL); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],54,0x5b9cca4f7763e373ULL); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],55,0x682e6ff3d6b2b8a3ULL); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],56,0x748f82ee5defb2fcULL); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],57,0x78a5636f43172f60ULL); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],58,0x84c87814a1f0ab72ULL); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],59,0x8cc702081a6439ecULL); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],60,0x90befffa23631e28ULL); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],61,0xa4506cebde82bde9ULL); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],62,0xbef9a3f7b2c67915ULL); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],63,0xc67178f2e372532bULL); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],64,0xca273eceea26619cULL); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],65,0xd186b8c721c0c207ULL); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],66,0xeada7dd6cde0eb1eULL); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],67,0xf57d4f7fee6ed178ULL); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],68,0x06f067aa72176fbaULL); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],69,0x0a637dc5a2c898a6ULL); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],70,0x113f9804bef90daeULL); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],71,0x1b710b35131c471bULL); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],72,0x28db77f523047d84ULL); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],73,0x32caab7b40c72493ULL); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],74,0x3c9ebe0a15c9bebcULL); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],75,0x431d67c49c100d4cULL); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],76,0x4cc5d4becb3e42b6ULL); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],77,0x597f299cfc657e2aULL); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],78,0x5fcb6fab3ad6faecULL); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],79,0x6c44198c4a475817ULL); - -#undef RND - - /* feedback */ - for (i = 0; i < 8; i++) { - sha_info->digest[i] = sha_info->digest[i] + S[i]; - } - -} - - - -/* initialize the SHA digest */ - -static void -sha512_init(SHAobject *sha_info) -{ - sha_info->digest[0] = Py_ULL(0x6a09e667f3bcc908); - sha_info->digest[1] = Py_ULL(0xbb67ae8584caa73b); - sha_info->digest[2] = Py_ULL(0x3c6ef372fe94f82b); - sha_info->digest[3] = Py_ULL(0xa54ff53a5f1d36f1); - sha_info->digest[4] = Py_ULL(0x510e527fade682d1); - sha_info->digest[5] = Py_ULL(0x9b05688c2b3e6c1f); - sha_info->digest[6] = Py_ULL(0x1f83d9abfb41bd6b); - sha_info->digest[7] = Py_ULL(0x5be0cd19137e2179); - sha_info->count_lo = 0L; - sha_info->count_hi = 0L; - sha_info->local = 0; - sha_info->digestsize = 64; -} - -static void -sha384_init(SHAobject *sha_info) -{ - sha_info->digest[0] = Py_ULL(0xcbbb9d5dc1059ed8); - sha_info->digest[1] = Py_ULL(0x629a292a367cd507); - sha_info->digest[2] = Py_ULL(0x9159015a3070dd17); - sha_info->digest[3] = Py_ULL(0x152fecd8f70e5939); - sha_info->digest[4] = Py_ULL(0x67332667ffc00b31); - sha_info->digest[5] = Py_ULL(0x8eb44a8768581511); - sha_info->digest[6] = Py_ULL(0xdb0c2e0d64f98fa7); - sha_info->digest[7] = Py_ULL(0x47b5481dbefa4fa4); - sha_info->count_lo = 0L; - sha_info->count_hi = 0L; - sha_info->local = 0; - sha_info->digestsize = 48; -} - - -/* update the SHA digest */ - -static void -sha512_update(SHAobject *sha_info, SHA_BYTE *buffer, Py_ssize_t count) -{ - Py_ssize_t i; - SHA_INT32 clo; - - clo = sha_info->count_lo + ((SHA_INT32) count << 3); - if (clo < sha_info->count_lo) { - ++sha_info->count_hi; - } - sha_info->count_lo = clo; - sha_info->count_hi += (SHA_INT32) count >> 29; - if (sha_info->local) { - i = SHA_BLOCKSIZE - sha_info->local; - if (i > count) { - i = count; - } - memcpy(((SHA_BYTE *) sha_info->data) + sha_info->local, buffer, i); - count -= i; - buffer += i; - sha_info->local += (int)i; - if (sha_info->local == SHA_BLOCKSIZE) { - sha512_transform(sha_info); - } - else { - return; - } - } - while (count >= SHA_BLOCKSIZE) { - memcpy(sha_info->data, buffer, SHA_BLOCKSIZE); - buffer += SHA_BLOCKSIZE; - count -= SHA_BLOCKSIZE; - sha512_transform(sha_info); - } - memcpy(sha_info->data, buffer, count); - sha_info->local = (int)count; + dest->state = Hacl_Streaming_SHA2_copy_512(src->state); } -/* finish computing the SHA digest */ - -static void -sha512_final(unsigned char digest[SHA_DIGESTSIZE], SHAobject *sha_info) -{ - int count; - SHA_INT32 lo_bit_count, hi_bit_count; - - lo_bit_count = sha_info->count_lo; - hi_bit_count = sha_info->count_hi; - count = (int) ((lo_bit_count >> 3) & 0x7f); - ((SHA_BYTE *) sha_info->data)[count++] = 0x80; - if (count > SHA_BLOCKSIZE - 16) { - memset(((SHA_BYTE *) sha_info->data) + count, 0, - SHA_BLOCKSIZE - count); - sha512_transform(sha_info); - memset((SHA_BYTE *) sha_info->data, 0, SHA_BLOCKSIZE - 16); - } - else { - memset(((SHA_BYTE *) sha_info->data) + count, 0, - SHA_BLOCKSIZE - 16 - count); - } - - /* GJS: note that we add the hi/lo in big-endian. sha512_transform will - swap these values into host-order. */ - sha_info->data[112] = 0; - sha_info->data[113] = 0; - sha_info->data[114] = 0; - sha_info->data[115] = 0; - sha_info->data[116] = 0; - sha_info->data[117] = 0; - sha_info->data[118] = 0; - sha_info->data[119] = 0; - sha_info->data[120] = (hi_bit_count >> 24) & 0xff; - sha_info->data[121] = (hi_bit_count >> 16) & 0xff; - sha_info->data[122] = (hi_bit_count >> 8) & 0xff; - sha_info->data[123] = (hi_bit_count >> 0) & 0xff; - sha_info->data[124] = (lo_bit_count >> 24) & 0xff; - sha_info->data[125] = (lo_bit_count >> 16) & 0xff; - sha_info->data[126] = (lo_bit_count >> 8) & 0xff; - sha_info->data[127] = (lo_bit_count >> 0) & 0xff; - sha512_transform(sha_info); - digest[ 0] = (unsigned char) ((sha_info->digest[0] >> 56) & 0xff); - digest[ 1] = (unsigned char) ((sha_info->digest[0] >> 48) & 0xff); - digest[ 2] = (unsigned char) ((sha_info->digest[0] >> 40) & 0xff); - digest[ 3] = (unsigned char) ((sha_info->digest[0] >> 32) & 0xff); - digest[ 4] = (unsigned char) ((sha_info->digest[0] >> 24) & 0xff); - digest[ 5] = (unsigned char) ((sha_info->digest[0] >> 16) & 0xff); - digest[ 6] = (unsigned char) ((sha_info->digest[0] >> 8) & 0xff); - digest[ 7] = (unsigned char) ((sha_info->digest[0] ) & 0xff); - digest[ 8] = (unsigned char) ((sha_info->digest[1] >> 56) & 0xff); - digest[ 9] = (unsigned char) ((sha_info->digest[1] >> 48) & 0xff); - digest[10] = (unsigned char) ((sha_info->digest[1] >> 40) & 0xff); - digest[11] = (unsigned char) ((sha_info->digest[1] >> 32) & 0xff); - digest[12] = (unsigned char) ((sha_info->digest[1] >> 24) & 0xff); - digest[13] = (unsigned char) ((sha_info->digest[1] >> 16) & 0xff); - digest[14] = (unsigned char) ((sha_info->digest[1] >> 8) & 0xff); - digest[15] = (unsigned char) ((sha_info->digest[1] ) & 0xff); - digest[16] = (unsigned char) ((sha_info->digest[2] >> 56) & 0xff); - digest[17] = (unsigned char) ((sha_info->digest[2] >> 48) & 0xff); - digest[18] = (unsigned char) ((sha_info->digest[2] >> 40) & 0xff); - digest[19] = (unsigned char) ((sha_info->digest[2] >> 32) & 0xff); - digest[20] = (unsigned char) ((sha_info->digest[2] >> 24) & 0xff); - digest[21] = (unsigned char) ((sha_info->digest[2] >> 16) & 0xff); - digest[22] = (unsigned char) ((sha_info->digest[2] >> 8) & 0xff); - digest[23] = (unsigned char) ((sha_info->digest[2] ) & 0xff); - digest[24] = (unsigned char) ((sha_info->digest[3] >> 56) & 0xff); - digest[25] = (unsigned char) ((sha_info->digest[3] >> 48) & 0xff); - digest[26] = (unsigned char) ((sha_info->digest[3] >> 40) & 0xff); - digest[27] = (unsigned char) ((sha_info->digest[3] >> 32) & 0xff); - digest[28] = (unsigned char) ((sha_info->digest[3] >> 24) & 0xff); - digest[29] = (unsigned char) ((sha_info->digest[3] >> 16) & 0xff); - digest[30] = (unsigned char) ((sha_info->digest[3] >> 8) & 0xff); - digest[31] = (unsigned char) ((sha_info->digest[3] ) & 0xff); - digest[32] = (unsigned char) ((sha_info->digest[4] >> 56) & 0xff); - digest[33] = (unsigned char) ((sha_info->digest[4] >> 48) & 0xff); - digest[34] = (unsigned char) ((sha_info->digest[4] >> 40) & 0xff); - digest[35] = (unsigned char) ((sha_info->digest[4] >> 32) & 0xff); - digest[36] = (unsigned char) ((sha_info->digest[4] >> 24) & 0xff); - digest[37] = (unsigned char) ((sha_info->digest[4] >> 16) & 0xff); - digest[38] = (unsigned char) ((sha_info->digest[4] >> 8) & 0xff); - digest[39] = (unsigned char) ((sha_info->digest[4] ) & 0xff); - digest[40] = (unsigned char) ((sha_info->digest[5] >> 56) & 0xff); - digest[41] = (unsigned char) ((sha_info->digest[5] >> 48) & 0xff); - digest[42] = (unsigned char) ((sha_info->digest[5] >> 40) & 0xff); - digest[43] = (unsigned char) ((sha_info->digest[5] >> 32) & 0xff); - digest[44] = (unsigned char) ((sha_info->digest[5] >> 24) & 0xff); - digest[45] = (unsigned char) ((sha_info->digest[5] >> 16) & 0xff); - digest[46] = (unsigned char) ((sha_info->digest[5] >> 8) & 0xff); - digest[47] = (unsigned char) ((sha_info->digest[5] ) & 0xff); - digest[48] = (unsigned char) ((sha_info->digest[6] >> 56) & 0xff); - digest[49] = (unsigned char) ((sha_info->digest[6] >> 48) & 0xff); - digest[50] = (unsigned char) ((sha_info->digest[6] >> 40) & 0xff); - digest[51] = (unsigned char) ((sha_info->digest[6] >> 32) & 0xff); - digest[52] = (unsigned char) ((sha_info->digest[6] >> 24) & 0xff); - digest[53] = (unsigned char) ((sha_info->digest[6] >> 16) & 0xff); - digest[54] = (unsigned char) ((sha_info->digest[6] >> 8) & 0xff); - digest[55] = (unsigned char) ((sha_info->digest[6] ) & 0xff); - digest[56] = (unsigned char) ((sha_info->digest[7] >> 56) & 0xff); - digest[57] = (unsigned char) ((sha_info->digest[7] >> 48) & 0xff); - digest[58] = (unsigned char) ((sha_info->digest[7] >> 40) & 0xff); - digest[59] = (unsigned char) ((sha_info->digest[7] >> 32) & 0xff); - digest[60] = (unsigned char) ((sha_info->digest[7] >> 24) & 0xff); - digest[61] = (unsigned char) ((sha_info->digest[7] >> 16) & 0xff); - digest[62] = (unsigned char) ((sha_info->digest[7] >> 8) & 0xff); - digest[63] = (unsigned char) ((sha_info->digest[7] ) & 0xff); -} - -/* - * End of copied SHA code. - * - * ------------------------------------------------------------------------ - */ - typedef struct { PyTypeObject* sha384_type; PyTypeObject* sha512_type; @@ -463,14 +96,31 @@ SHA_traverse(PyObject *ptr, visitproc visit, void *arg) } static void -SHA512_dealloc(PyObject *ptr) +SHA512_dealloc(SHAobject *ptr) { + Hacl_Streaming_SHA2_free_512(ptr->state); PyTypeObject *tp = Py_TYPE(ptr); PyObject_GC_UnTrack(ptr); PyObject_GC_Del(ptr); Py_DECREF(tp); } +/* HACL* takes a uint32_t for the length of its parameter, but Py_ssize_t can be + * 64 bits. */ +static void update_512(Hacl_Streaming_SHA2_state_sha2_512 *state, uint8_t *buf, Py_ssize_t len) { + /* Note: we explicitly ignore the error code on the basis that it would take > + * 1 billion years to overflow the maximum admissible length for this API + * (namely, 2^64-1 bytes). */ + while (len > UINT32_MAX) { + Hacl_Streaming_SHA2_update_512(state, buf, UINT32_MAX); + len -= UINT32_MAX; + buf += UINT32_MAX; + } + /* Cast to uint32_t is safe: upon exiting the loop, len <= UINT32_MAX, and + * therefore fits in a uint32_t */ + Hacl_Streaming_SHA2_update_512(state, buf, (uint32_t) len); +} + /* External methods for a hash object */ @@ -514,11 +164,10 @@ static PyObject * SHA512Type_digest_impl(SHAobject *self) /*[clinic end generated code: output=1080bbeeef7dde1b input=f6470dd359071f4b]*/ { - unsigned char digest[SHA_DIGESTSIZE]; - SHAobject temp; - - SHAcopy(self, &temp); - sha512_final(digest, &temp); + uint8_t digest[SHA_DIGESTSIZE]; + // HACL performs copies under the hood so that self->state remains valid + // after this call. + Hacl_Streaming_SHA2_finish_512(self->state, digest); return PyBytes_FromStringAndSize((const char *)digest, self->digestsize); } @@ -532,13 +181,8 @@ static PyObject * SHA512Type_hexdigest_impl(SHAobject *self) /*[clinic end generated code: output=7373305b8601e18b input=498b877b25cbe0a2]*/ { - unsigned char digest[SHA_DIGESTSIZE]; - SHAobject temp; - - /* Get the raw (binary) digest value */ - SHAcopy(self, &temp); - sha512_final(digest, &temp); - + uint8_t digest[SHA_DIGESTSIZE]; + Hacl_Streaming_SHA2_finish_512(self->state, digest); return _Py_strhex((const char *)digest, self->digestsize); } @@ -559,7 +203,7 @@ SHA512Type_update(SHAobject *self, PyObject *obj) GET_BUFFER_VIEW_OR_ERROUT(obj, &buf); - sha512_update(self, buf.buf, buf.len); + update_512(self->state, buf.buf, buf.len); PyBuffer_Release(&buf); Py_RETURN_NONE; @@ -622,15 +266,6 @@ static PyType_Spec sha512_sha384_type_spec = { .slots = sha512_sha384_type_slots }; -static PyType_Slot sha512_sha512_type_slots[] = { - {Py_tp_dealloc, SHA512_dealloc}, - {Py_tp_methods, SHA_methods}, - {Py_tp_members, SHA_members}, - {Py_tp_getset, SHA_getseters}, - {Py_tp_traverse, SHA_traverse}, - {0,0} -}; - // Using PyType_GetModuleState() on this type is safe since // it cannot be subclassed: it does not have the Py_TPFLAGS_BASETYPE flag. static PyType_Spec sha512_sha512_type_spec = { @@ -638,7 +273,7 @@ static PyType_Spec sha512_sha512_type_spec = { .basicsize = sizeof(SHAobject), .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), - .slots = sha512_sha512_type_slots + .slots = sha512_sha384_type_slots }; /* The single module-level function: new() */ @@ -671,7 +306,8 @@ _sha512_sha512_impl(PyObject *module, PyObject *string, int usedforsecurity) return NULL; } - sha512_init(new); + new->state = Hacl_Streaming_SHA2_create_in_512(); + new->digestsize = 64; if (PyErr_Occurred()) { Py_DECREF(new); @@ -680,7 +316,7 @@ _sha512_sha512_impl(PyObject *module, PyObject *string, int usedforsecurity) return NULL; } if (string) { - sha512_update(new, buf.buf, buf.len); + update_512(new->state, buf.buf, buf.len); PyBuffer_Release(&buf); } @@ -715,7 +351,8 @@ _sha512_sha384_impl(PyObject *module, PyObject *string, int usedforsecurity) return NULL; } - sha384_init(new); + new->state = Hacl_Streaming_SHA2_create_in_384(); + new->digestsize = 48; if (PyErr_Occurred()) { Py_DECREF(new); @@ -724,7 +361,7 @@ _sha512_sha384_impl(PyObject *module, PyObject *string, int usedforsecurity) return NULL; } if (string) { - sha512_update(new, buf.buf, buf.len); + update_512(new->state, buf.buf, buf.len); PyBuffer_Release(&buf); } diff --git a/configure b/configure index 35088f9e5cafd6..c00a1e1d2ec986 100755 --- a/configure +++ b/configure @@ -26950,7 +26950,7 @@ fi as_fn_append MODULE_BLOCK "MODULE__SHA512_STATE=$py_cv_module__sha512$as_nl" if test "x$py_cv_module__sha512" = xyes; then : - + as_fn_append MODULE_BLOCK "MODULE__SHA512_CFLAGS=-I\$(srcdir)/Modules/_hacl/include -I\$(srcdir)/Modules/_hacl/internal -D_BSD_SOURCE -D_DEFAULT_SOURCE$as_nl" fi diff --git a/configure.ac b/configure.ac index 1ab48e0d1c160a..92a05c011026f2 100644 --- a/configure.ac +++ b/configure.ac @@ -7200,7 +7200,9 @@ PY_STDLIB_MOD([_sha1], [test "$with_builtin_sha1" = yes]) PY_STDLIB_MOD([_sha256], [test "$with_builtin_sha256" = yes], [], [-I\$(srcdir)/Modules/_hacl/include -I\$(srcdir)/Modules/_hacl/internal -D_BSD_SOURCE -D_DEFAULT_SOURCE]) -PY_STDLIB_MOD([_sha512], [test "$with_builtin_sha512" = yes]) +PY_STDLIB_MOD([_sha512], + [test "$with_builtin_sha512" = yes], [], + [-I\$(srcdir)/Modules/_hacl/include -I\$(srcdir)/Modules/_hacl/internal -D_BSD_SOURCE -D_DEFAULT_SOURCE]) PY_STDLIB_MOD([_sha3], [test "$with_builtin_sha3" = yes]) PY_STDLIB_MOD([_blake2], [test "$with_builtin_blake2" = yes], [], From 3690688149dca11589af59b7704541336613199a Mon Sep 17 00:00:00 2001 From: Furkan Onder Date: Tue, 14 Feb 2023 10:20:11 +0000 Subject: [PATCH 093/247] GH-101898: Fix missing term references for hashable definition (#101899) Fix missing term references for hashable definition --- Doc/c-api/dict.rst | 2 +- Doc/c-api/object.rst | 2 +- Doc/faq/programming.rst | 2 +- Doc/library/abc.rst | 2 +- Doc/library/collections.abc.rst | 4 ++-- Doc/library/collections.rst | 4 ++-- Doc/library/datetime.rst | 2 +- Doc/library/fractions.rst | 2 +- Doc/library/functools.rst | 2 +- Doc/library/graphlib.rst | 4 ++-- Doc/library/inspect.rst | 4 ++-- Doc/library/operator.rst | 2 +- Doc/library/pathlib.rst | 2 +- Doc/library/stdtypes.rst | 6 +++--- Doc/library/typing.rst | 2 +- Doc/reference/datamodel.rst | 2 +- 16 files changed, 22 insertions(+), 22 deletions(-) diff --git a/Doc/c-api/dict.rst b/Doc/c-api/dict.rst index e5f28b59a701e0..34106ee6b1f30d 100644 --- a/Doc/c-api/dict.rst +++ b/Doc/c-api/dict.rst @@ -80,7 +80,7 @@ Dictionary Objects .. c:function:: int PyDict_DelItem(PyObject *p, PyObject *key) - Remove the entry in dictionary *p* with key *key*. *key* must be hashable; + Remove the entry in dictionary *p* with key *key*. *key* must be :term:`hashable`; if it isn't, :exc:`TypeError` is raised. If *key* is not in the dictionary, :exc:`KeyError` is raised. Return ``0`` on success or ``-1`` on failure. diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index 5a25a2b6c9d3db..84c72e7e108b64 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -281,7 +281,7 @@ Object Protocol .. c:function:: Py_hash_t PyObject_HashNotImplemented(PyObject *o) - Set a :exc:`TypeError` indicating that ``type(o)`` is not hashable and return ``-1``. + Set a :exc:`TypeError` indicating that ``type(o)`` is not :term:`hashable` and return ``-1``. This function receives special treatment when stored in a ``tp_hash`` slot, allowing a type to explicitly indicate to the interpreter that it is not hashable. diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst index ba42289f3466c2..38f9b171618b26 100644 --- a/Doc/faq/programming.rst +++ b/Doc/faq/programming.rst @@ -1979,7 +1979,7 @@ method result will be released right away. The disadvantage is that if instances accumulate, so too will the accumulated method results. They can grow without bound. -The *lru_cache* approach works with methods that have hashable +The *lru_cache* approach works with methods that have :term:`hashable` arguments. It creates a reference to the instance unless special efforts are made to pass in weak references. diff --git a/Doc/library/abc.rst b/Doc/library/abc.rst index 3b74622e7ff46c..274b2d69f0ab5c 100644 --- a/Doc/library/abc.rst +++ b/Doc/library/abc.rst @@ -21,7 +21,7 @@ The :mod:`collections` module has some concrete classes that derive from ABCs; these can, of course, be further derived. In addition, the :mod:`collections.abc` submodule has some ABCs that can be used to test whether a class or instance provides a particular interface, for example, if it is -hashable or if it is a mapping. +:term:`hashable` or if it is a mapping. This module provides the metaclass :class:`ABCMeta` for defining ABCs and diff --git a/Doc/library/collections.abc.rst b/Doc/library/collections.abc.rst index 132b0ce7192ac1..1ada0d352a0cc6 100644 --- a/Doc/library/collections.abc.rst +++ b/Doc/library/collections.abc.rst @@ -22,7 +22,7 @@ This module provides :term:`abstract base classes ` that can be used to test whether a class provides a particular interface; for -example, whether it is hashable or whether it is a mapping. +example, whether it is :term:`hashable` or whether it is a mapping. An :func:`issubclass` or :func:`isinstance` test for an interface works in one of three ways. @@ -406,7 +406,7 @@ Notes on using :class:`Set` and :class:`MutableSet` as a mixin: (3) The :class:`Set` mixin provides a :meth:`_hash` method to compute a hash value for the set; however, :meth:`__hash__` is not defined because not all sets - are hashable or immutable. To add set hashability using mixins, + are :term:`hashable` or immutable. To add set hashability using mixins, inherit from both :meth:`Set` and :meth:`Hashable`, then define ``__hash__ = Set._hash``. diff --git a/Doc/library/collections.rst b/Doc/library/collections.rst index 2cffc2300a2298..bb46782c06e1c8 100644 --- a/Doc/library/collections.rst +++ b/Doc/library/collections.rst @@ -25,7 +25,7 @@ Python's general purpose built-in containers, :class:`dict`, :class:`list`, :func:`namedtuple` factory function for creating tuple subclasses with named fields :class:`deque` list-like container with fast appends and pops on either end :class:`ChainMap` dict-like class for creating a single view of multiple mappings -:class:`Counter` dict subclass for counting hashable objects +:class:`Counter` dict subclass for counting :term:`hashable` objects :class:`OrderedDict` dict subclass that remembers the order entries were added :class:`defaultdict` dict subclass that calls a factory function to supply missing values :class:`UserDict` wrapper around dictionary objects for easier dict subclassing @@ -242,7 +242,7 @@ For example:: .. class:: Counter([iterable-or-mapping]) - A :class:`Counter` is a :class:`dict` subclass for counting hashable objects. + A :class:`Counter` is a :class:`dict` subclass for counting :term:`hashable` objects. It is a collection where elements are stored as dictionary keys and their counts are stored as dictionary values. Counts are allowed to be any integer value including zero or negative counts. The :class:`Counter` diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index 2f1ab7c3dd4b51..50827b27ebea04 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -160,7 +160,7 @@ The :class:`date`, :class:`.datetime`, :class:`.time`, and :class:`timezone` typ share these common features: - Objects of these types are immutable. -- Objects of these types are hashable, meaning that they can be used as +- Objects of these types are :term:`hashable`, meaning that they can be used as dictionary keys. - Objects of these types support efficient pickling via the :mod:`pickle` module. diff --git a/Doc/library/fractions.rst b/Doc/library/fractions.rst index 06b0e038c89bd0..c61bbac892e0e4 100644 --- a/Doc/library/fractions.rst +++ b/Doc/library/fractions.rst @@ -77,7 +77,7 @@ another rational number, or from a string. The :class:`Fraction` class inherits from the abstract base class :class:`numbers.Rational`, and implements all of the methods and - operations from that class. :class:`Fraction` instances are hashable, + operations from that class. :class:`Fraction` instances are :term:`hashable`, and should be treated as immutable. In addition, :class:`Fraction` has the following properties and methods: diff --git a/Doc/library/functools.rst b/Doc/library/functools.rst index 2f0a9bd8be8815..80a405e87d8d56 100644 --- a/Doc/library/functools.rst +++ b/Doc/library/functools.rst @@ -147,7 +147,7 @@ The :mod:`functools` module defines the following functions: threads. Since a dictionary is used to cache results, the positional and keyword - arguments to the function must be hashable. + arguments to the function must be :term:`hashable`. Distinct argument patterns may be considered to be distinct calls with separate cache entries. For example, ``f(a=1, b=2)`` and ``f(b=2, a=1)`` diff --git a/Doc/library/graphlib.rst b/Doc/library/graphlib.rst index 2bc80da4ead2a2..fe7932e7a61cb5 100644 --- a/Doc/library/graphlib.rst +++ b/Doc/library/graphlib.rst @@ -17,7 +17,7 @@ .. class:: TopologicalSorter(graph=None) - Provides functionality to topologically sort a graph of hashable nodes. + Provides functionality to topologically sort a graph of :term:`hashable` nodes. A topological order is a linear ordering of the vertices in a graph such that for every directed edge u -> v from vertex u to vertex v, vertex u comes @@ -85,7 +85,7 @@ .. method:: add(node, *predecessors) Add a new node and its predecessors to the graph. Both the *node* and all - elements in *predecessors* must be hashable. + elements in *predecessors* must be :term:`hashable`. If called multiple times with the same node argument, the set of dependencies will be the union of all dependencies passed in. diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index 58b84a35a890e3..9c3be5a250a67e 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -689,7 +689,7 @@ function. modified copy. .. versionchanged:: 3.5 - Signature objects are picklable and hashable. + Signature objects are picklable and :term:`hashable`. .. attribute:: Signature.empty @@ -768,7 +768,7 @@ function. you can use :meth:`Parameter.replace` to create a modified copy. .. versionchanged:: 3.5 - Parameter objects are picklable and hashable. + Parameter objects are picklable and :term:`hashable`. .. attribute:: Parameter.empty diff --git a/Doc/library/operator.rst b/Doc/library/operator.rst index 35e9b49ea8bcca..6d90473f2367b5 100644 --- a/Doc/library/operator.rst +++ b/Doc/library/operator.rst @@ -327,7 +327,7 @@ expect a function argument. return g The items can be any type accepted by the operand's :meth:`__getitem__` - method. Dictionaries accept any hashable value. Lists, tuples, and + method. Dictionaries accept any :term:`hashable` value. Lists, tuples, and strings accept an index or a slice: >>> itemgetter(1)('ABCDEFG') diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index f222745a2c56bc..c8a734ecad8e7b 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -186,7 +186,7 @@ these classes, since they don't provide any operation that does system calls. General properties ^^^^^^^^^^^^^^^^^^ -Paths are immutable and hashable. Paths of a same flavour are comparable +Paths are immutable and :term:`hashable`. Paths of a same flavour are comparable and orderable. These properties respect the flavour's case-folding semantics:: diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 0ef03035a572e5..98bda6ded30f79 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -3775,7 +3775,7 @@ copying. >>> data bytearray(b'z1spam') - One-dimensional memoryviews of hashable (read-only) types with formats + One-dimensional memoryviews of :term:`hashable` (read-only) types with formats 'B', 'b' or 'c' are also hashable. The hash is defined as ``hash(m) == hash(m.tobytes())``:: @@ -3789,7 +3789,7 @@ copying. .. versionchanged:: 3.3 One-dimensional memoryviews can now be sliced. - One-dimensional memoryviews with formats 'B', 'b' or 'c' are now hashable. + One-dimensional memoryviews with formats 'B', 'b' or 'c' are now :term:`hashable`. .. versionchanged:: 3.4 memoryview is now registered automatically with @@ -4710,7 +4710,7 @@ support membership tests: .. versionadded:: 3.10 -Keys views are set-like since their entries are unique and hashable. If all +Keys views are set-like since their entries are unique and :term:`hashable`. If all values are hashable, so that ``(key, value)`` pairs are unique and hashable, then the items view is also set-like. (Values views are not treated as set-like since the entries are generally not unique.) For set-like views, all of the diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 356f919a1897b2..169f7196a74ec6 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -439,7 +439,7 @@ are intended primarily for static type checking. A user-defined generic class can have ABCs as base classes without a metaclass conflict. Generic metaclasses are not supported. The outcome of parameterizing -generics is cached, and most types in the typing module are hashable and +generics is cached, and most types in the typing module are :term:`hashable` and comparable for equality. diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 1d2ddf3507aee1..e4e471e50079ae 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -1525,7 +1525,7 @@ Basic customization :meth:`__hash__`, its instances will not be usable as items in hashable collections. If a class defines mutable objects and implements an :meth:`__eq__` method, it should not implement :meth:`__hash__`, since the - implementation of hashable collections requires that a key's hash value is + implementation of :term:`hashable` collections requires that a key's hash value is immutable (if the object's hash value changes, it will be in the wrong hash bucket). From 81e3aa835c32363f4547b6566edf1125386f1f6d Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Tue, 14 Feb 2023 11:54:13 +0000 Subject: [PATCH 094/247] gh-101799: implement PREP_RERAISE_STAR as an intrinsic function (#101800) --- Doc/library/dis.rst | 26 +++++++++------- Include/internal/pycore_intrinsics.h | 13 ++++++++ Include/internal/pycore_opcode.h | 18 +++++------ Include/opcode.h | 20 ++++++------- Lib/importlib/_bootstrap_external.py | 3 +- Lib/opcode.py | 2 +- ...-02-13-18-21-14.gh-issue-101799.wpHbCn.rst | 2 ++ Python/bytecodes.c | 20 ++++++------- Python/compile.c | 4 +-- Python/generated_cases.c.h | 30 +++++++++---------- Python/intrinsics.c | 18 +++++++++++ Python/opcode_metadata.h | 10 +++---- Python/opcode_targets.h | 14 ++++----- 13 files changed, 107 insertions(+), 73 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-13-18-21-14.gh-issue-101799.wpHbCn.rst diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index b1e61d7e77b2f5..a5bc5e7fb6ea71 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -768,16 +768,6 @@ iterations of the loop. .. versionadded:: 3.11 -.. opcode:: PREP_RERAISE_STAR - - Combines the raised and reraised exceptions list from ``STACK[-1]``, into an - exception group to propagate from a try-except* block. Uses the original exception - group from ``STACK[-2]`` to reconstruct the structure of reraised exceptions. Pops - two items from the stack and pushes the exception to reraise or ``None`` - if there isn't one. - - .. versionadded:: 3.11 - .. opcode:: WITH_EXCEPT_START Calls the function in position 4 on the stack with arguments (type, val, tb) @@ -1515,7 +1505,8 @@ iterations of the loop. .. opcode:: CALL_INTRINSIC_1 Calls an intrinsic function with one argument. Passes ``STACK[-1]`` as the - argument and sets ``STACK[-1]`` to the result. Used to implement functionality that is necessary but not performance critical. + argument and sets ``STACK[-1]`` to the result. Used to implement + functionality that is necessary but not performance critical. The operand determines which intrinsic function is called: @@ -1529,6 +1520,19 @@ iterations of the loop. .. versionadded:: 3.12 +.. opcode:: CALL_INTRINSIC_2 + + Calls an intrinsic function with two arguments. Passes ``STACK[-2]``, ``STACK[-1]`` as the + arguments and sets ``STACK[-1]`` to the result. Used to implement functionality that is + necessary but not performance critical. + + The operand determines which intrinsic function is called: + + * ``0`` Not valid + * ``1`` Calculates the :exc:`ExceptionGroup` to raise from a ``try-except*``. + + .. versionadded:: 3.12 + **Pseudo-instructions** diff --git a/Include/internal/pycore_intrinsics.h b/Include/internal/pycore_intrinsics.h index 1da618f2b4a548..deac145fff7627 100644 --- a/Include/internal/pycore_intrinsics.h +++ b/Include/internal/pycore_intrinsics.h @@ -1,4 +1,6 @@ +/* Unary Functions: */ + #define INTRINSIC_PRINT 1 #define INTRINSIC_IMPORT_STAR 2 #define INTRINSIC_STOPITERATION_ERROR 3 @@ -8,6 +10,17 @@ #define MAX_INTRINSIC_1 6 + +/* Binary Functions: */ + +#define INTRINSIC_PREP_RERAISE_STAR 1 + +#define MAX_INTRINSIC_2 1 + + typedef PyObject *(*instrinsic_func1)(PyThreadState* tstate, PyObject *value); +typedef PyObject *(*instrinsic_func2)(PyThreadState* tstate, PyObject *value1, PyObject *value2); extern instrinsic_func1 _PyIntrinsics_UnaryFunctions[]; +extern instrinsic_func2 _PyIntrinsics_BinaryFunctions[]; + diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h index 5e65adee9e00a5..f9ab95ca4bb9d3 100644 --- a/Include/internal/pycore_opcode.h +++ b/Include/internal/pycore_opcode.h @@ -87,6 +87,7 @@ const uint8_t _PyOpcode_Deopt[256] = { [CALL_BUILTIN_FAST_WITH_KEYWORDS] = CALL, [CALL_FUNCTION_EX] = CALL_FUNCTION_EX, [CALL_INTRINSIC_1] = CALL_INTRINSIC_1, + [CALL_INTRINSIC_2] = CALL_INTRINSIC_2, [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = CALL, [CALL_NO_KW_BUILTIN_FAST] = CALL, [CALL_NO_KW_BUILTIN_O] = CALL, @@ -187,7 +188,6 @@ const uint8_t _PyOpcode_Deopt[256] = { [POP_JUMP_IF_NOT_NONE] = POP_JUMP_IF_NOT_NONE, [POP_JUMP_IF_TRUE] = POP_JUMP_IF_TRUE, [POP_TOP] = POP_TOP, - [PREP_RERAISE_STAR] = PREP_RERAISE_STAR, [PUSH_EXC_INFO] = PUSH_EXC_INFO, [PUSH_NULL] = PUSH_NULL, [RAISE_VARARGS] = RAISE_VARARGS, @@ -319,7 +319,7 @@ static const char *const _PyOpcode_OpName[263] = { [SETUP_ANNOTATIONS] = "SETUP_ANNOTATIONS", [STORE_ATTR_INSTANCE_VALUE] = "STORE_ATTR_INSTANCE_VALUE", [STORE_ATTR_SLOT] = "STORE_ATTR_SLOT", - [PREP_RERAISE_STAR] = "PREP_RERAISE_STAR", + [STORE_ATTR_WITH_HINT] = "STORE_ATTR_WITH_HINT", [POP_EXCEPT] = "POP_EXCEPT", [STORE_NAME] = "STORE_NAME", [DELETE_NAME] = "DELETE_NAME", @@ -344,7 +344,7 @@ static const char *const _PyOpcode_OpName[263] = { [JUMP_FORWARD] = "JUMP_FORWARD", [JUMP_IF_FALSE_OR_POP] = "JUMP_IF_FALSE_OR_POP", [JUMP_IF_TRUE_OR_POP] = "JUMP_IF_TRUE_OR_POP", - [STORE_ATTR_WITH_HINT] = "STORE_ATTR_WITH_HINT", + [STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST", [POP_JUMP_IF_FALSE] = "POP_JUMP_IF_FALSE", [POP_JUMP_IF_TRUE] = "POP_JUMP_IF_TRUE", [LOAD_GLOBAL] = "LOAD_GLOBAL", @@ -374,7 +374,7 @@ static const char *const _PyOpcode_OpName[263] = { [JUMP_BACKWARD] = "JUMP_BACKWARD", [COMPARE_AND_BRANCH] = "COMPARE_AND_BRANCH", [CALL_FUNCTION_EX] = "CALL_FUNCTION_EX", - [STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST", + [STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST", [EXTENDED_ARG] = "EXTENDED_ARG", [LIST_APPEND] = "LIST_APPEND", [SET_ADD] = "SET_ADD", @@ -384,20 +384,20 @@ static const char *const _PyOpcode_OpName[263] = { [YIELD_VALUE] = "YIELD_VALUE", [RESUME] = "RESUME", [MATCH_CLASS] = "MATCH_CLASS", - [STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST", [STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT", + [STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT", [FORMAT_VALUE] = "FORMAT_VALUE", [BUILD_CONST_KEY_MAP] = "BUILD_CONST_KEY_MAP", [BUILD_STRING] = "BUILD_STRING", - [STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT", [UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST", [UNPACK_SEQUENCE_TUPLE] = "UNPACK_SEQUENCE_TUPLE", [UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE", + [SEND_GEN] = "SEND_GEN", [LIST_EXTEND] = "LIST_EXTEND", [SET_UPDATE] = "SET_UPDATE", [DICT_MERGE] = "DICT_MERGE", [DICT_UPDATE] = "DICT_UPDATE", - [SEND_GEN] = "SEND_GEN", + [166] = "<166>", [167] = "<167>", [168] = "<168>", [169] = "<169>", @@ -405,7 +405,7 @@ static const char *const _PyOpcode_OpName[263] = { [CALL] = "CALL", [KW_NAMES] = "KW_NAMES", [CALL_INTRINSIC_1] = "CALL_INTRINSIC_1", - [174] = "<174>", + [CALL_INTRINSIC_2] = "CALL_INTRINSIC_2", [175] = "<175>", [176] = "<176>", [177] = "<177>", @@ -498,11 +498,11 @@ static const char *const _PyOpcode_OpName[263] = { #endif #define EXTRA_CASES \ + case 166: \ case 167: \ case 168: \ case 169: \ case 170: \ - case 174: \ case 175: \ case 176: \ case 177: \ diff --git a/Include/opcode.h b/Include/opcode.h index d643741c3c3aa0..760ff945f31f9e 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -43,7 +43,6 @@ extern "C" { #define RETURN_GENERATOR 75 #define RETURN_VALUE 83 #define SETUP_ANNOTATIONS 85 -#define PREP_RERAISE_STAR 88 #define POP_EXCEPT 89 #define HAVE_ARGUMENT 90 #define STORE_NAME 90 @@ -117,6 +116,7 @@ extern "C" { #define CALL 171 #define KW_NAMES 172 #define CALL_INTRINSIC_1 173 +#define CALL_INTRINSIC_2 174 #define MIN_PSEUDO_OPCODE 256 #define SETUP_FINALLY 256 #define SETUP_CLEANUP 257 @@ -179,15 +179,15 @@ extern "C" { #define LOAD_GLOBAL_MODULE 84 #define STORE_ATTR_INSTANCE_VALUE 86 #define STORE_ATTR_SLOT 87 -#define STORE_ATTR_WITH_HINT 113 -#define STORE_FAST__LOAD_FAST 143 -#define STORE_FAST__STORE_FAST 153 -#define STORE_SUBSCR_DICT 154 -#define STORE_SUBSCR_LIST_INT 158 -#define UNPACK_SEQUENCE_LIST 159 -#define UNPACK_SEQUENCE_TUPLE 160 -#define UNPACK_SEQUENCE_TWO_TUPLE 161 -#define SEND_GEN 166 +#define STORE_ATTR_WITH_HINT 88 +#define STORE_FAST__LOAD_FAST 113 +#define STORE_FAST__STORE_FAST 143 +#define STORE_SUBSCR_DICT 153 +#define STORE_SUBSCR_LIST_INT 154 +#define UNPACK_SEQUENCE_LIST 158 +#define UNPACK_SEQUENCE_TUPLE 159 +#define UNPACK_SEQUENCE_TWO_TUPLE 160 +#define SEND_GEN 161 #define DO_TRACING 255 #define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\ diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 38d4a384c2cc95..954401cfa85ed3 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -433,6 +433,7 @@ def _write_atomic(path, data, mode=0o666): # Python 3.12a5 3517 (Change YIELD_VALUE oparg to exception block depth) # Python 3.12a5 3518 (Add RETURN_CONST instruction) # Python 3.12a5 3519 (Modify SEND instruction) +# Python 3.12a5 3520 (Remove PREP_RERAISE_STAR, add CALL_INTRINSIC_2) # Python 3.13 will start with 3550 @@ -445,7 +446,7 @@ def _write_atomic(path, data, mode=0o666): # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # in PC/launcher.c must also be updated. -MAGIC_NUMBER = (3519).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3520).to_bytes(2, 'little') + b'\r\n' _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c diff --git a/Lib/opcode.py b/Lib/opcode.py index b69cd1bbdd61ca..809d24e51676bd 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -127,7 +127,6 @@ def pseudo_op(name, op, real_ops): def_op('SETUP_ANNOTATIONS', 85) -def_op('PREP_RERAISE_STAR', 88) def_op('POP_EXCEPT', 89) HAVE_ARGUMENT = 90 # real opcodes from here have an argument: @@ -224,6 +223,7 @@ def pseudo_op(name, op, real_ops): def_op('KW_NAMES', 172) hasconst.append(172) def_op('CALL_INTRINSIC_1', 173) +def_op('CALL_INTRINSIC_2', 174) hasarg.extend([op for op in opmap.values() if op >= HAVE_ARGUMENT]) diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-13-18-21-14.gh-issue-101799.wpHbCn.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-13-18-21-14.gh-issue-101799.wpHbCn.rst new file mode 100644 index 00000000000000..3233a573be7acd --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-13-18-21-14.gh-issue-101799.wpHbCn.rst @@ -0,0 +1,2 @@ +Add :opcode:`CALL_INTRINSIC_2` and use it instead of +:opcode:`PREP_RERAISE_STAR`. diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 429cd7fdafa168..be54e5f6f589eb 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -501,7 +501,14 @@ dummy_func( inst(CALL_INTRINSIC_1, (value -- res)) { assert(oparg <= MAX_INTRINSIC_1); res = _PyIntrinsics_UnaryFunctions[oparg](tstate, value); - Py_DECREF(value); + DECREF_INPUTS(); + ERROR_IF(res == NULL, error); + } + + inst(CALL_INTRINSIC_2, (value2, value1 -- res)) { + assert(oparg <= MAX_INTRINSIC_2); + res = _PyIntrinsics_BinaryFunctions[oparg](tstate, value2, value1); + DECREF_INPUTS(); ERROR_IF(res == NULL, error); } @@ -788,15 +795,6 @@ dummy_func( goto exception_unwind; } - inst(PREP_RERAISE_STAR, (orig, excs -- val)) { - assert(PyList_Check(excs)); - - val = _PyExc_PrepReraiseStar(orig, excs); - DECREF_INPUTS(); - - ERROR_IF(val == NULL, error); - } - inst(END_ASYNC_FOR, (awaitable, exc -- )) { assert(exc && PyExceptionInstance_Check(exc)); if (PyErr_GivenExceptionMatches(exc, PyExc_StopAsyncIteration)) { @@ -2383,7 +2381,7 @@ dummy_func( } // Cache layout: counter/1, func_version/2, min_args/1 - // Neither CALL_INTRINSIC_1 nor CALL_FUNCTION_EX are members! + // Neither CALL_INTRINSIC_1/2 nor CALL_FUNCTION_EX are members! family(call, INLINE_CACHE_ENTRIES_CALL) = { CALL, CALL_BOUND_METHOD_EXACT_ARGS, diff --git a/Python/compile.c b/Python/compile.c index b49eda314eeef1..0534b536e3d12e 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -3431,7 +3431,7 @@ compiler_try_except(struct compiler *c, stmt_ty s) [orig, res, rest] Ln+1: LIST_APPEND 1 ) add unhandled exc to res (could be None) - [orig, res] PREP_RERAISE_STAR + [orig, res] CALL_INTRINSIC_2 PREP_RERAISE_STAR [exc] COPY 1 [exc, exc] POP_JUMP_IF_NOT_NONE RER [exc] POP_TOP @@ -3580,7 +3580,7 @@ compiler_try_star_except(struct compiler *c, stmt_ty s) NEW_JUMP_TARGET_LABEL(c, reraise); USE_LABEL(c, reraise_star); - ADDOP(c, NO_LOCATION, PREP_RERAISE_STAR); + ADDOP_I(c, NO_LOCATION, CALL_INTRINSIC_2, INTRINSIC_PREP_RERAISE_STAR); ADDOP_I(c, NO_LOCATION, COPY, 1); ADDOP_JUMP(c, NO_LOCATION, POP_JUMP_IF_NOT_NONE, reraise); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 093ebff026b509..beb797cbd233d7 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -689,6 +689,20 @@ DISPATCH(); } + TARGET(CALL_INTRINSIC_2) { + PyObject *value1 = PEEK(1); + PyObject *value2 = PEEK(2); + PyObject *res; + assert(oparg <= MAX_INTRINSIC_2); + res = _PyIntrinsics_BinaryFunctions[oparg](tstate, value2, value1); + Py_DECREF(value2); + Py_DECREF(value1); + if (res == NULL) goto pop_2_error; + STACK_SHRINK(1); + POKE(1, res); + DISPATCH(); + } + TARGET(RAISE_VARARGS) { PyObject **args = &PEEK(oparg); PyObject *cause = NULL, *exc = NULL; @@ -999,22 +1013,6 @@ goto exception_unwind; } - TARGET(PREP_RERAISE_STAR) { - PyObject *excs = PEEK(1); - PyObject *orig = PEEK(2); - PyObject *val; - assert(PyList_Check(excs)); - - val = _PyExc_PrepReraiseStar(orig, excs); - Py_DECREF(orig); - Py_DECREF(excs); - - if (val == NULL) goto pop_2_error; - STACK_SHRINK(1); - POKE(1, val); - DISPATCH(); - } - TARGET(END_ASYNC_FOR) { PyObject *exc = PEEK(1); PyObject *awaitable = PEEK(2); diff --git a/Python/intrinsics.c b/Python/intrinsics.c index ae1775862d945d..9e90ef32130f1d 100644 --- a/Python/intrinsics.c +++ b/Python/intrinsics.c @@ -9,6 +9,7 @@ #include "pycore_pyerrors.h" +/******** Unary functions ********/ static PyObject * no_intrinsic(PyThreadState* tstate, PyObject *unused) @@ -208,3 +209,20 @@ _PyIntrinsics_UnaryFunctions[] = { [INTRINSIC_UNARY_POSITIVE] = unary_pos, [INTRINSIC_LIST_TO_TUPLE] = list_to_tuple, }; + + +/******** Binary functions ********/ + + +static PyObject * +prep_reraise_star(PyThreadState* unused, PyObject *orig, PyObject *excs) +{ + assert(PyList_Check(excs)); + return _PyExc_PrepReraiseStar(orig, excs); +} + +instrinsic_func2 +_PyIntrinsics_BinaryFunctions[] = { + [INTRINSIC_PREP_RERAISE_STAR] = prep_reraise_star, +}; + diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index d622eb12c8cb2d..f27906a3e349eb 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -88,6 +88,8 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { return 2; case CALL_INTRINSIC_1: return 1; + case CALL_INTRINSIC_2: + return 2; case RAISE_VARARGS: return oparg; case INTERPRETER_EXIT: @@ -112,8 +114,6 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { return 1; case RERAISE: return oparg + 1; - case PREP_RERAISE_STAR: - return 2; case END_ASYNC_FOR: return 2; case CLEANUP_THROW: @@ -440,6 +440,8 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { return 0; case CALL_INTRINSIC_1: return 1; + case CALL_INTRINSIC_2: + return 1; case RAISE_VARARGS: return 0; case INTERPRETER_EXIT: @@ -464,8 +466,6 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { return 0; case RERAISE: return oparg; - case PREP_RERAISE_STAR: - return 1; case END_ASYNC_FOR: return 0; case CLEANUP_THROW: @@ -760,6 +760,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[256] = { [STORE_SUBSCR_DICT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, [DELETE_SUBSCR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [CALL_INTRINSIC_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_INTRINSIC_2] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [RAISE_VARARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [INTERPRETER_EXIT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [RETURN_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, @@ -772,7 +773,6 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[256] = { [YIELD_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [POP_EXCEPT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [RERAISE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [PREP_RERAISE_STAR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [END_ASYNC_FOR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [CLEANUP_THROW] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [LOAD_ASSERTION_ERROR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index 301ec6e005dad6..bc64bd582fd572 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -87,7 +87,7 @@ static void *opcode_targets[256] = { &&TARGET_SETUP_ANNOTATIONS, &&TARGET_STORE_ATTR_INSTANCE_VALUE, &&TARGET_STORE_ATTR_SLOT, - &&TARGET_PREP_RERAISE_STAR, + &&TARGET_STORE_ATTR_WITH_HINT, &&TARGET_POP_EXCEPT, &&TARGET_STORE_NAME, &&TARGET_DELETE_NAME, @@ -112,7 +112,7 @@ static void *opcode_targets[256] = { &&TARGET_JUMP_FORWARD, &&TARGET_JUMP_IF_FALSE_OR_POP, &&TARGET_JUMP_IF_TRUE_OR_POP, - &&TARGET_STORE_ATTR_WITH_HINT, + &&TARGET_STORE_FAST__LOAD_FAST, &&TARGET_POP_JUMP_IF_FALSE, &&TARGET_POP_JUMP_IF_TRUE, &&TARGET_LOAD_GLOBAL, @@ -142,7 +142,7 @@ static void *opcode_targets[256] = { &&TARGET_JUMP_BACKWARD, &&TARGET_COMPARE_AND_BRANCH, &&TARGET_CALL_FUNCTION_EX, - &&TARGET_STORE_FAST__LOAD_FAST, + &&TARGET_STORE_FAST__STORE_FAST, &&TARGET_EXTENDED_ARG, &&TARGET_LIST_APPEND, &&TARGET_SET_ADD, @@ -152,20 +152,20 @@ static void *opcode_targets[256] = { &&TARGET_YIELD_VALUE, &&TARGET_RESUME, &&TARGET_MATCH_CLASS, - &&TARGET_STORE_FAST__STORE_FAST, &&TARGET_STORE_SUBSCR_DICT, + &&TARGET_STORE_SUBSCR_LIST_INT, &&TARGET_FORMAT_VALUE, &&TARGET_BUILD_CONST_KEY_MAP, &&TARGET_BUILD_STRING, - &&TARGET_STORE_SUBSCR_LIST_INT, &&TARGET_UNPACK_SEQUENCE_LIST, &&TARGET_UNPACK_SEQUENCE_TUPLE, &&TARGET_UNPACK_SEQUENCE_TWO_TUPLE, + &&TARGET_SEND_GEN, &&TARGET_LIST_EXTEND, &&TARGET_SET_UPDATE, &&TARGET_DICT_MERGE, &&TARGET_DICT_UPDATE, - &&TARGET_SEND_GEN, + &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, @@ -173,7 +173,7 @@ static void *opcode_targets[256] = { &&TARGET_CALL, &&TARGET_KW_NAMES, &&TARGET_CALL_INTRINSIC_1, - &&_unknown_opcode, + &&TARGET_CALL_INTRINSIC_2, &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, From 096d0097a09e439a4564531b297a998e5d74c9b5 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 14 Feb 2023 14:26:03 -0700 Subject: [PATCH 095/247] gh-101758: Add a Test For Single-Phase Init Module Variants (gh-101891) The new test exercises the most important variants for single-phase init extension modules. We also add some explanation about those variants to import.c. https://github.com/python/cpython/issues/101758 --- Lib/test/test_imp.py | 199 +++++++++++++++++++ Modules/_testsinglephase.c | 394 ++++++++++++++++++++++++++++++++++--- Python/import.c | 105 +++++++++- 3 files changed, 660 insertions(+), 38 deletions(-) diff --git a/Lib/test/test_imp.py b/Lib/test/test_imp.py index 80abc720c3251a..31dce21587e2ca 100644 --- a/Lib/test/test_imp.py +++ b/Lib/test/test_imp.py @@ -251,6 +251,205 @@ def test_issue16421_multiple_modules_in_one_dll(self): with self.assertRaises(ImportError): imp.load_dynamic('nonexistent', pathname) + @requires_load_dynamic + def test_singlephase_variants(self): + '''Exercise the most meaningful variants described in Python/import.c.''' + self.maxDiff = None + + basename = '_testsinglephase' + fileobj, pathname, _ = imp.find_module(basename) + fileobj.close() + + modules = {} + def load(name): + assert name not in modules + module = imp.load_dynamic(name, pathname) + self.assertNotIn(module, modules.values()) + modules[name] = module + return module + + def re_load(name, module): + assert sys.modules[name] is module + before = type(module)(module.__name__) + before.__dict__.update(vars(module)) + + reloaded = imp.load_dynamic(name, pathname) + + return before, reloaded + + def check_common(name, module): + summed = module.sum(1, 2) + lookedup = module.look_up_self() + initialized = module.initialized() + cached = sys.modules[name] + + # module.__name__ might not match, but the spec will. + self.assertEqual(module.__spec__.name, name) + if initialized is not None: + self.assertIsInstance(initialized, float) + self.assertGreater(initialized, 0) + self.assertEqual(summed, 3) + self.assertTrue(issubclass(module.error, Exception)) + self.assertEqual(module.int_const, 1969) + self.assertEqual(module.str_const, 'something different') + self.assertIs(cached, module) + + return lookedup, initialized, cached + + def check_direct(name, module, lookedup): + # The module has its own PyModuleDef, with a matching name. + self.assertEqual(module.__name__, name) + self.assertIs(lookedup, module) + + def check_indirect(name, module, lookedup, orig): + # The module re-uses another's PyModuleDef, with a different name. + assert orig is not module + assert orig.__name__ != name + self.assertNotEqual(module.__name__, name) + self.assertIs(lookedup, module) + + def check_basic(module, initialized): + init_count = module.initialized_count() + + self.assertIsNot(initialized, None) + self.assertIsInstance(init_count, int) + self.assertGreater(init_count, 0) + + return init_count + + def check_common_reloaded(name, module, cached, before, reloaded): + recached = sys.modules[name] + + self.assertEqual(reloaded.__spec__.name, name) + self.assertEqual(reloaded.__name__, before.__name__) + self.assertEqual(before.__dict__, module.__dict__) + self.assertIs(recached, reloaded) + + def check_basic_reloaded(module, lookedup, initialized, init_count, + before, reloaded): + relookedup = reloaded.look_up_self() + reinitialized = reloaded.initialized() + reinit_count = reloaded.initialized_count() + + self.assertIs(reloaded, module) + self.assertIs(reloaded.__dict__, module.__dict__) + # It only happens to be the same but that's good enough here. + # We really just want to verify that the re-loaded attrs + # didn't change. + self.assertIs(relookedup, lookedup) + self.assertEqual(reinitialized, initialized) + self.assertEqual(reinit_count, init_count) + + def check_with_reinit_reloaded(module, lookedup, initialized, + before, reloaded): + relookedup = reloaded.look_up_self() + reinitialized = reloaded.initialized() + + self.assertIsNot(reloaded, module) + self.assertIsNot(reloaded, module) + self.assertNotEqual(reloaded.__dict__, module.__dict__) + self.assertIs(relookedup, reloaded) + if initialized is None: + self.assertIs(reinitialized, None) + else: + self.assertGreater(reinitialized, initialized) + + # Check the "basic" module. + + name = basename + expected_init_count = 1 + with self.subTest(name): + mod = load(name) + lookedup, initialized, cached = check_common(name, mod) + check_direct(name, mod, lookedup) + init_count = check_basic(mod, initialized) + self.assertEqual(init_count, expected_init_count) + + before, reloaded = re_load(name, mod) + check_common_reloaded(name, mod, cached, before, reloaded) + check_basic_reloaded(mod, lookedup, initialized, init_count, + before, reloaded) + basic = mod + + # Check its indirect variants. + + name = f'{basename}_basic_wrapper' + expected_init_count += 1 + with self.subTest(name): + mod = load(name) + lookedup, initialized, cached = check_common(name, mod) + check_indirect(name, mod, lookedup, basic) + init_count = check_basic(mod, initialized) + self.assertEqual(init_count, expected_init_count) + + before, reloaded = re_load(name, mod) + check_common_reloaded(name, mod, cached, before, reloaded) + check_basic_reloaded(mod, lookedup, initialized, init_count, + before, reloaded) + + # Currently _PyState_AddModule() always replaces the cached module. + self.assertIs(basic.look_up_self(), mod) + self.assertEqual(basic.initialized_count(), expected_init_count) + + # The cached module shouldn't be changed after this point. + basic_lookedup = mod + + # Check its direct variant. + + name = f'{basename}_basic_copy' + expected_init_count += 1 + with self.subTest(name): + mod = load(name) + lookedup, initialized, cached = check_common(name, mod) + check_direct(name, mod, lookedup) + init_count = check_basic(mod, initialized) + self.assertEqual(init_count, expected_init_count) + + before, reloaded = re_load(name, mod) + check_common_reloaded(name, mod, cached, before, reloaded) + check_basic_reloaded(mod, lookedup, initialized, init_count, + before, reloaded) + + # This should change the cached module for _testsinglephase. + self.assertIs(basic.look_up_self(), basic_lookedup) + self.assertEqual(basic.initialized_count(), expected_init_count) + + # Check the non-basic variant that has no state. + + name = f'{basename}_with_reinit' + with self.subTest(name): + mod = load(name) + lookedup, initialized, cached = check_common(name, mod) + self.assertIs(initialized, None) + check_direct(name, mod, lookedup) + + before, reloaded = re_load(name, mod) + check_common_reloaded(name, mod, cached, before, reloaded) + check_with_reinit_reloaded(mod, lookedup, initialized, + before, reloaded) + + # This should change the cached module for _testsinglephase. + self.assertIs(basic.look_up_self(), basic_lookedup) + self.assertEqual(basic.initialized_count(), expected_init_count) + + # Check the basic variant that has state. + + name = f'{basename}_with_state' + with self.subTest(name): + mod = load(name) + lookedup, initialized, cached = check_common(name, mod) + self.assertIsNot(initialized, None) + check_direct(name, mod, lookedup) + + before, reloaded = re_load(name, mod) + check_common_reloaded(name, mod, cached, before, reloaded) + check_with_reinit_reloaded(mod, lookedup, initialized, + before, reloaded) + + # This should change the cached module for _testsinglephase. + self.assertIs(basic.look_up_self(), basic_lookedup) + self.assertEqual(basic.initialized_count(), expected_init_count) + @requires_load_dynamic def test_load_dynamic_ImportError_path(self): # Issue #1559549 added `name` and `path` attributes to ImportError diff --git a/Modules/_testsinglephase.c b/Modules/_testsinglephase.c index 3bfe159e54fe49..9e8dd647ee761a 100644 --- a/Modules/_testsinglephase.c +++ b/Modules/_testsinglephase.c @@ -5,74 +5,412 @@ # define Py_BUILD_CORE_MODULE 1 #endif +//#include #include "Python.h" #include "pycore_namespace.h" // _PyNamespace_New() +typedef struct { + _PyTime_t initialized; + PyObject *error; + PyObject *int_const; + PyObject *str_const; +} module_state; + +/* Process-global state is only used by _testsinglephase + since it's the only one that does not support re-init. */ +static struct { + int initialized_count; + module_state module; +} global_state = { .initialized_count = -1 }; + +static inline module_state * +get_module_state(PyObject *module) +{ + PyModuleDef *def = PyModule_GetDef(module); + if (def->m_size == -1) { + return &global_state.module; + } + else if (def->m_size == 0) { + return NULL; + } + else { + module_state *state = (module_state*)PyModule_GetState(module); + assert(state != NULL); + return state; + } +} + +static void +clear_state(module_state *state) +{ + state->initialized = 0; + Py_CLEAR(state->error); + Py_CLEAR(state->int_const); + Py_CLEAR(state->str_const); +} + +static int +_set_initialized(_PyTime_t *initialized) +{ + /* We go strictly monotonic to ensure each time is unique. */ + _PyTime_t prev; + if (_PyTime_GetMonotonicClockWithInfo(&prev, NULL) != 0) { + return -1; + } + /* We do a busy sleep since the interval should be super short. */ + _PyTime_t t; + do { + if (_PyTime_GetMonotonicClockWithInfo(&t, NULL) != 0) { + return -1; + } + } while (t == prev); + + *initialized = t; + return 0; +} + +static int +init_state(module_state *state) +{ + assert(state->initialized == 0 && + state->error == NULL && + state->int_const == NULL && + state->str_const == NULL); + + if (_set_initialized(&state->initialized) != 0) { + goto error; + } + assert(state->initialized > 0); + + /* Add an exception type */ + state->error = PyErr_NewException("_testsinglephase.error", NULL, NULL); + if (state->error == NULL) { + goto error; + } + + state->int_const = PyLong_FromLong(1969); + if (state->int_const == NULL) { + goto error; + } + + state->str_const = PyUnicode_FromString("something different"); + if (state->str_const == NULL) { + goto error; + } + + return 0; + +error: + clear_state(state); + return -1; +} + +static int +init_module(PyObject *module, module_state *state) +{ + if (PyModule_AddObjectRef(module, "error", state->error) != 0) { + return -1; + } + if (PyModule_AddObjectRef(module, "int_const", state->int_const) != 0) { + return -1; + } + if (PyModule_AddObjectRef(module, "str_const", state->str_const) != 0) { + return -1; + } + return 0; +} + + +PyDoc_STRVAR(common_initialized_doc, +"initialized()\n\ +\n\ +Return the seconds-since-epoch when the module was initialized."); + +static PyObject * +common_initialized(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + module_state *state = get_module_state(self); + if (state == NULL) { + Py_RETURN_NONE; + } + double d = _PyTime_AsSecondsDouble(state->initialized); + return PyFloat_FromDouble(d); +} + +#define INITIALIZED_METHODDEF \ + {"initialized", common_initialized, METH_NOARGS, \ + common_initialized_doc} + + +PyDoc_STRVAR(common_look_up_self_doc, +"look_up_self()\n\ +\n\ +Return the module associated with this module's def.m_base.m_index."); + +static PyObject * +common_look_up_self(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyModuleDef *def = PyModule_GetDef(self); + if (def == NULL) { + return NULL; + } + return Py_NewRef( + PyState_FindModule(def)); +} + +#define LOOK_UP_SELF_METHODDEF \ + {"look_up_self", common_look_up_self, METH_NOARGS, common_look_up_self_doc} + + /* Function of two integers returning integer */ -PyDoc_STRVAR(testexport_foo_doc, -"foo(i,j)\n\ +PyDoc_STRVAR(common_sum_doc, +"sum(i,j)\n\ \n\ Return the sum of i and j."); static PyObject * -testexport_foo(PyObject *self, PyObject *args) +common_sum(PyObject *self, PyObject *args) { long i, j; long res; - if (!PyArg_ParseTuple(args, "ll:foo", &i, &j)) + if (!PyArg_ParseTuple(args, "ll:sum", &i, &j)) return NULL; res = i + j; return PyLong_FromLong(res); } +#define SUM_METHODDEF \ + {"sum", common_sum, METH_VARARGS, common_sum_doc} -static PyMethodDef TestMethods[] = { - {"foo", testexport_foo, METH_VARARGS, - testexport_foo_doc}, - {NULL, NULL} /* sentinel */ -}; +PyDoc_STRVAR(basic_initialized_count_doc, +"initialized_count()\n\ +\n\ +Return how many times the module has been initialized."); + +static PyObject * +basic_initialized_count(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + assert(PyModule_GetDef(self)->m_size == -1); + return PyLong_FromLong(global_state.initialized_count); +} + +#define INITIALIZED_COUNT_METHODDEF \ + {"initialized_count", basic_initialized_count, METH_VARARGS, \ + basic_initialized_count_doc} + + +/*********************************************/ +/* the _testsinglephase module (and aliases) */ +/*********************************************/ + +/* This ia more typical of legacy extensions in the wild: + - single-phase init + - no module state + - does not support repeated initialization + (so m_copy is used) + - the module def is cached in _PyRuntime.extensions + (by name/filename) + + Also note that, because the module has single-phase init, + it is cached in interp->module_by_index (using mod->md_def->m_base.m_index). + */ -static struct PyModuleDef _testsinglephase = { +static PyMethodDef TestMethods_Basic[] = { + LOOK_UP_SELF_METHODDEF, + SUM_METHODDEF, + INITIALIZED_METHODDEF, + INITIALIZED_COUNT_METHODDEF, + {NULL, NULL} /* sentinel */ +}; + +static struct PyModuleDef _testsinglephase_basic = { PyModuleDef_HEAD_INIT, .m_name = "_testsinglephase", - .m_doc = PyDoc_STR("Test module _testsinglephase (main)"), + .m_doc = PyDoc_STR("Test module _testsinglephase"), .m_size = -1, // no module state - .m_methods = TestMethods, + .m_methods = TestMethods_Basic, }; +static PyObject * +init__testsinglephase_basic(PyModuleDef *def) +{ + if (global_state.initialized_count == -1) { + global_state.initialized_count = 0; + } + + PyObject *module = PyModule_Create(def); + if (module == NULL) { + return NULL; + } + + module_state *state = &global_state.module; + // It may have been set by a previous run or under a different name. + clear_state(state); + if (init_state(state) < 0) { + Py_CLEAR(module); + return NULL; + } + + if (init_module(module, state) < 0) { + Py_CLEAR(module); + goto finally; + } + + global_state.initialized_count++; + +finally: + return module; +} PyMODINIT_FUNC PyInit__testsinglephase(void) { - PyObject *module = PyModule_Create(&_testsinglephase); + return init__testsinglephase_basic(&_testsinglephase_basic); +} + + +PyMODINIT_FUNC +PyInit__testsinglephase_basic_wrapper(void) +{ + return PyInit__testsinglephase(); +} + + +PyMODINIT_FUNC +PyInit__testsinglephase_basic_copy(void) +{ + static struct PyModuleDef def = { + PyModuleDef_HEAD_INIT, + .m_name = "_testsinglephase_basic_copy", + .m_doc = PyDoc_STR("Test module _testsinglephase_basic_copy"), + .m_size = -1, // no module state + .m_methods = TestMethods_Basic, + }; + return init__testsinglephase_basic(&def); +} + + +/*******************************************/ +/* the _testsinglephase_with_reinit module */ +/*******************************************/ + +/* This ia less typical of legacy extensions in the wild: + - single-phase init (same as _testsinglephase above) + - no module state + - supports repeated initialization + (so m_copy is not used) + - the module def is not cached in _PyRuntime.extensions + + At this point most modules would reach for multi-phase init (PEP 489). + However, module state has been around a while (PEP 3121), + and most extensions predate multi-phase init. + + (This module is basically the same as _testsinglephase, + but supports repeated initialization.) + */ + +static PyMethodDef TestMethods_Reinit[] = { + LOOK_UP_SELF_METHODDEF, + SUM_METHODDEF, + INITIALIZED_METHODDEF, + {NULL, NULL} /* sentinel */ +}; + +static struct PyModuleDef _testsinglephase_with_reinit = { + PyModuleDef_HEAD_INIT, + .m_name = "_testsinglephase_with_reinit", + .m_doc = PyDoc_STR("Test module _testsinglephase_with_reinit"), + .m_size = 0, + .m_methods = TestMethods_Reinit, +}; + +PyMODINIT_FUNC +PyInit__testsinglephase_with_reinit(void) +{ + /* We purposefully do not try PyState_FindModule() first here + since we want to check the behavior of re-loading the module. */ + PyObject *module = PyModule_Create(&_testsinglephase_with_reinit); if (module == NULL) { return NULL; } - /* Add an exception type */ - PyObject *temp = PyErr_NewException("_testsinglephase.error", NULL, NULL); - if (temp == NULL) { - goto error; + assert(get_module_state(module) == NULL); + + module_state state = {0}; + if (init_state(&state) < 0) { + Py_CLEAR(module); + return NULL; } - if (PyModule_AddObject(module, "error", temp) != 0) { - Py_DECREF(temp); - goto error; + + if (init_module(module, &state) < 0) { + Py_CLEAR(module); + goto finally; } - if (PyModule_AddIntConstant(module, "int_const", 1969) != 0) { - goto error; +finally: + /* We only needed the module state for setting the module attrs. */ + clear_state(&state); + return module; +} + + +/******************************************/ +/* the _testsinglephase_with_state module */ +/******************************************/ + +/* This ia less typical of legacy extensions in the wild: + - single-phase init (same as _testsinglephase above) + - has some module state + - supports repeated initialization + (so m_copy is not used) + - the module def is not cached in _PyRuntime.extensions + + At this point most modules would reach for multi-phase init (PEP 489). + However, module state has been around a while (PEP 3121), + and most extensions predate multi-phase init. + */ + +static PyMethodDef TestMethods_WithState[] = { + LOOK_UP_SELF_METHODDEF, + SUM_METHODDEF, + INITIALIZED_METHODDEF, + {NULL, NULL} /* sentinel */ +}; + +static struct PyModuleDef _testsinglephase_with_state = { + PyModuleDef_HEAD_INIT, + .m_name = "_testsinglephase_with_state", + .m_doc = PyDoc_STR("Test module _testsinglephase_with_state"), + .m_size = sizeof(module_state), + .m_methods = TestMethods_WithState, +}; + +PyMODINIT_FUNC +PyInit__testsinglephase_with_state(void) +{ + /* We purposefully do not try PyState_FindModule() first here + since we want to check the behavior of re-loading the module. */ + PyObject *module = PyModule_Create(&_testsinglephase_with_state); + if (module == NULL) { + return NULL; } - if (PyModule_AddStringConstant(module, "str_const", "something different") != 0) { - goto error; + module_state *state = get_module_state(module); + assert(state != NULL); + if (init_state(state) < 0) { + Py_CLEAR(module); + return NULL; } - return module; + if (init_module(module, state) < 0) { + clear_state(state); + Py_CLEAR(module); + goto finally; + } -error: - Py_DECREF(module); - return NULL; +finally: + return module; } diff --git a/Python/import.c b/Python/import.c index 302255d76edcd5..63ed2443657b29 100644 --- a/Python/import.c +++ b/Python/import.c @@ -428,6 +428,71 @@ PyImport_GetMagicTag(void) } +/* +We support a number of kinds of single-phase init builtin/extension modules: + +* "basic" + * no module state (PyModuleDef.m_size == -1) + * does not support repeated init (we use PyModuleDef.m_base.m_copy) + * may have process-global state + * the module's def is cached in _PyRuntime.imports.extensions, + by (name, filename) +* "reinit" + * no module state (PyModuleDef.m_size == 0) + * supports repeated init (m_copy is never used) + * should not have any process-global state + * its def is never cached in _PyRuntime.imports.extensions + (except, currently, under the main interpreter, for some reason) +* "with state" (almost the same as reinit) + * has module state (PyModuleDef.m_size > 0) + * supports repeated init (m_copy is never used) + * should not have any process-global state + * its def is never cached in _PyRuntime.imports.extensions + (except, currently, under the main interpreter, for some reason) + +There are also variants within those classes: + +* two or more modules share a PyModuleDef + * a module's init func uses another module's PyModuleDef + * a module's init func calls another's module's init func + * a module's init "func" is actually a variable statically initialized + to another module's init func +* two or modules share "methods" + * a module's init func copies another module's PyModuleDef + (with a different name) +* (basic-only) two or modules share process-global state + +In the first case, where modules share a PyModuleDef, the following +notable weirdness happens: + +* the module's __name__ matches the def, not the requested name +* the last module (with the same def) to be imported for the first time wins + * returned by PyState_Find_Module() (via interp->modules_by_index) + * (non-basic-only) its init func is used when re-loading any of them + (via the def's m_init) + * (basic-only) the copy of its __dict__ is used when re-loading any of them + (via the def's m_copy) + +However, the following happens as expected: + +* a new module object (with its own __dict__) is created for each request +* the module's __spec__ has the requested name +* the loaded module is cached in sys.modules under the requested name +* the m_index field of the shared def is not changed, + so at least PyState_FindModule() will always look in the same place + +For "basic" modules there are other quirks: + +* (whether sharing a def or not) when loaded the first time, + m_copy is set before _init_module_attrs() is called + in importlib._bootstrap.module_from_spec(), + so when the module is re-loaded, the previous value + for __wpec__ (and others) is reset, possibly unexpectedly. + +Generally, when multiple interpreters are involved, some of the above +gets even messier. +*/ + /* Magic for extension modules (built-in as well as dynamically loaded). To prevent initializing an extension module more than once, we keep a static dictionary 'extensions' keyed by the tuple @@ -489,9 +554,8 @@ _extensions_cache_clear(void) Py_CLEAR(_PyRuntime.imports.extensions); } -int -_PyImport_FixupExtensionObject(PyObject *mod, PyObject *name, - PyObject *filename, PyObject *modules) +static int +fix_up_extension(PyObject *mod, PyObject *name, PyObject *filename) { if (mod == NULL || !PyModule_Check(mod)) { PyErr_BadInternalCall(); @@ -505,16 +569,13 @@ _PyImport_FixupExtensionObject(PyObject *mod, PyObject *name, } PyThreadState *tstate = _PyThreadState_GET(); - if (PyObject_SetItem(modules, name, mod) < 0) { - return -1; - } if (_PyState_AddModule(tstate, mod, def) < 0) { - PyMapping_DelItem(modules, name); return -1; } // bpo-44050: Extensions and def->m_base.m_copy can be updated // when the extension module doesn't support sub-interpreters. + // XXX Why special-case the main interpreter? if (_Py_IsMainInterpreter(tstate->interp) || def->m_size == -1) { if (def->m_size == -1) { if (def->m_base.m_copy) { @@ -541,15 +602,39 @@ _PyImport_FixupExtensionObject(PyObject *mod, PyObject *name, return 0; } +int +_PyImport_FixupExtensionObject(PyObject *mod, PyObject *name, + PyObject *filename, PyObject *modules) +{ + if (PyObject_SetItem(modules, name, mod) < 0) { + return -1; + } + if (fix_up_extension(mod, name, filename) < 0) { + PyMapping_DelItem(modules, name); + return -1; + } + return 0; +} + int _PyImport_FixupBuiltin(PyObject *mod, const char *name, PyObject *modules) { - int res; + int res = -1; PyObject *nameobj; nameobj = PyUnicode_InternFromString(name); - if (nameobj == NULL) + if (nameobj == NULL) { return -1; - res = _PyImport_FixupExtensionObject(mod, nameobj, nameobj, modules); + } + if (PyObject_SetItem(modules, nameobj, mod) < 0) { + goto finally; + } + if (fix_up_extension(mod, nameobj, nameobj) < 0) { + PyMapping_DelItem(modules, nameobj); + goto finally; + } + res = 0; + +finally: Py_DECREF(nameobj); return res; } From d777790bab878b8d1a218a1a60894b2823485cca Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Tue, 14 Feb 2023 15:57:01 -0800 Subject: [PATCH 096/247] gh-99108: Build the hashlib HACL* code as a static library. (#101917) This builds HACL* as a library in one place. A followup to #101707 which broke some WASM builds. This fixes 2/4 of them, but the enscripten toolchain in the others don't deduplicate linker arguments and error out. A follow-on PR will address those. --- Makefile.pre.in | 33 +++++++++++++++++-- Modules/Setup.stdlib.in | 4 +-- .../{include => }/python_hacl_namespaces.h | 1 + 3 files changed, 34 insertions(+), 4 deletions(-) rename Modules/_hacl/{include => }/python_hacl_namespaces.h (97%) diff --git a/Makefile.pre.in b/Makefile.pre.in index d42d4d8a3c1c9f..ce3fed3d648536 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -207,6 +207,7 @@ ENSUREPIP= @ENSUREPIP@ # Internal static libraries LIBMPDEC_A= Modules/_decimal/libmpdec/libmpdec.a LIBEXPAT_A= Modules/expat/libexpat.a +LIBHACL_A= Modules/_hacl/libHacl_Streaming_SHA2.a # Module state, compiler flags and linker flags # Empty CFLAGS and LDFLAGS are omitted. @@ -571,6 +572,23 @@ LIBEXPAT_HEADERS= \ Modules/expat/xmltok.h \ Modules/expat/xmltok_impl.h +########################################################################## +# hashlib's HACL* library + +LIBHACL_OBJS= \ + Modules/_hacl/Hacl_Streaming_SHA2.o + +LIBHACL_HEADERS= \ + Modules/_hacl/Hacl_Streaming_SHA2.h \ + Modules/_hacl/include/krml/FStar_UInt128_Verified.h \ + Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h \ + Modules/_hacl/include/krml/fstar_uint128_struct_endianness.h \ + Modules/_hacl/include/krml/internal/target.h \ + Modules/_hacl/include/krml/lowstar_endianness.h \ + Modules/_hacl/include/krml/types.h \ + Modules/_hacl/internal/Hacl_SHA2_Generic.h \ + Modules/_hacl/python_hacl_namespaces.h + ######################################################################### # Rules @@ -890,6 +908,17 @@ $(LIBEXPAT_A): $(LIBEXPAT_OBJS) -rm -f $@ $(AR) $(ARFLAGS) $@ $(LIBEXPAT_OBJS) +########################################################################## +# Build HACL* static libraries for hashlib: libHacl_Streaming_SHA2.a +LIBHACL_CFLAGS=-I$(srcdir)/Modules/_hacl/include -D_BSD_SOURCE -D_DEFAULT_SOURCE $(PY_STDMODULE_CFLAGS) $(CCSHARED) + +Modules/_hacl/Hacl_Streaming_SHA2.o: $(srcdir)/Modules/_hacl/Hacl_Streaming_SHA2.c $(LIBHACL_HEADERS) + $(CC) -c $(LIBHACL_CFLAGS) -o $@ $(srcdir)/Modules/_hacl/Hacl_Streaming_SHA2.c + +$(LIBHACL_A): $(LIBHACL_OBJS) + -rm -f $@ + $(AR) $(ARFLAGS) $@ $(LIBHACL_OBJS) + # create relative links from build/lib.platform/egg.so to Modules/egg.so # pybuilddir.txt is created too late. We cannot use it in Makefile # targets. ln --relative is not portable. @@ -2606,9 +2635,9 @@ MODULE__HASHLIB_DEPS=$(srcdir)/Modules/hashlib.h MODULE__IO_DEPS=$(srcdir)/Modules/_io/_iomodule.h MODULE__MD5_DEPS=$(srcdir)/Modules/hashlib.h MODULE__SHA1_DEPS=$(srcdir)/Modules/hashlib.h -MODULE__SHA256_DEPS=$(srcdir)/Modules/hashlib.h $(srcdir)/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h $(srcdir)/Modules/_hacl/include/krml/lowstar_endianness.h $(srcdir)/Modules/_hacl/include/krml/internal/target.h $(srcdir)/Modules/_hacl/Hacl_Streaming_SHA2.h +MODULE__SHA256_DEPS=$(srcdir)/Modules/hashlib.h $(LIBHACL_HEADERS) $(LIBHACL_A) MODULE__SHA3_DEPS=$(srcdir)/Modules/_sha3/sha3.c $(srcdir)/Modules/_sha3/sha3.h $(srcdir)/Modules/hashlib.h -MODULE__SHA512_DEPS=$(srcdir)/Modules/hashlib.h $(srcdir)/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h $(srcdir)/Modules/_hacl/include/krml/lowstar_endianness.h $(srcdir)/Modules/_hacl/include/krml/internal/target.h $(srcdir)/Modules/_hacl/Hacl_Streaming_SHA2.h +MODULE__SHA512_DEPS=$(srcdir)/Modules/hashlib.h $(LIBHACL_HEADERS) $(LIBHACL_A) MODULE__SOCKET_DEPS=$(srcdir)/Modules/socketmodule.h $(srcdir)/Modules/addrinfo.h $(srcdir)/Modules/getaddrinfo.c $(srcdir)/Modules/getnameinfo.c MODULE__SSL_DEPS=$(srcdir)/Modules/_ssl.h $(srcdir)/Modules/_ssl/cert.c $(srcdir)/Modules/_ssl/debughelpers.c $(srcdir)/Modules/_ssl/misc.c $(srcdir)/Modules/_ssl_data.h $(srcdir)/Modules/_ssl_data_111.h $(srcdir)/Modules/_ssl_data_300.h $(srcdir)/Modules/socketmodule.h MODULE__TESTCAPI_DEPS=$(srcdir)/Modules/_testcapi/testcapi_long.h $(srcdir)/Modules/_testcapi/parts.h diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index b6d13e04d3fa87..22bcb423db233f 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -79,8 +79,8 @@ # hashing builtins, can be disabled with --without-builtin-hashlib-hashes @MODULE__MD5_TRUE@_md5 md5module.c @MODULE__SHA1_TRUE@_sha1 sha1module.c -@MODULE__SHA256_TRUE@_sha256 sha256module.c _hacl/Hacl_Streaming_SHA2.c -@MODULE__SHA512_TRUE@_sha512 sha512module.c _hacl/Hacl_Streaming_SHA2.c +@MODULE__SHA256_TRUE@_sha256 sha256module.c -I$(srcdir)/Modules/_hacl/include Modules/_hacl/libHacl_Streaming_SHA2.a +@MODULE__SHA512_TRUE@_sha512 sha512module.c -I$(srcdir)/Modules/_hacl/include Modules/_hacl/libHacl_Streaming_SHA2.a @MODULE__SHA3_TRUE@_sha3 _sha3/sha3module.c @MODULE__BLAKE2_TRUE@_blake2 _blake2/blake2module.c _blake2/blake2b_impl.c _blake2/blake2s_impl.c diff --git a/Modules/_hacl/include/python_hacl_namespaces.h b/Modules/_hacl/python_hacl_namespaces.h similarity index 97% rename from Modules/_hacl/include/python_hacl_namespaces.h rename to Modules/_hacl/python_hacl_namespaces.h index 65608d1fd283c4..ac12f386257b19 100644 --- a/Modules/_hacl/include/python_hacl_namespaces.h +++ b/Modules/_hacl/python_hacl_namespaces.h @@ -25,6 +25,7 @@ #define Hacl_Streaming_SHA2_init_224 python_hashlib_Hacl_Streaming_SHA2_init_224 #define Hacl_Streaming_SHA2_init_512 python_hashlib_Hacl_Streaming_SHA2_init_512 #define Hacl_Streaming_SHA2_init_384 python_hashlib_Hacl_Streaming_SHA2_init_384 +#define Hacl_SHA2_Scalar32_sha512_init python_hashlib_Hacl_SHA2_Scalar32_sha512_init #define Hacl_Streaming_SHA2_update_256 python_hashlib_Hacl_Streaming_SHA2_update_256 #define Hacl_Streaming_SHA2_update_224 python_hashlib_Hacl_Streaming_SHA2_update_224 #define Hacl_Streaming_SHA2_update_512 python_hashlib_Hacl_Streaming_SHA2_update_512 From 8a2b7ee64d1bde762438b458ea7fe88f054a3a88 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 15 Feb 2023 06:27:16 +0100 Subject: [PATCH 097/247] gh-101693: In sqlite3, deprecate using named placeholders with parameters supplied as a sequence (#101698) --- Doc/library/sqlite3.rst | 19 ++++++++++++++++++- Doc/whatsnew/3.12.rst | 7 +++++++ Lib/test/test_sqlite3/test_dbapi.py | 15 +++++++++++++++ ...-02-08-18-20-58.gh-issue-101693.4_LPXj.rst | 6 ++++++ Modules/_sqlite/cursor.c | 13 +++++++++++++ 5 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2023-02-08-18-20-58.gh-issue-101693.4_LPXj.rst diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 8ffc0aad91995c..18d0a5e630f6a9 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -1442,6 +1442,14 @@ Cursor objects and there is no open transaction, a transaction is implicitly opened before executing *sql*. + .. deprecated-removed:: 3.12 3.14 + + :exc:`DeprecationWarning` is emitted if + :ref:`named placeholders ` are used + and *parameters* is a sequence instead of a :class:`dict`. + Starting with Python 3.14, :exc:`ProgrammingError` will + be raised instead. + Use :meth:`executescript` to execute multiple SQL statements. .. method:: executemany(sql, parameters, /) @@ -1476,6 +1484,15 @@ Cursor objects # cur is an sqlite3.Cursor object cur.executemany("INSERT INTO data VALUES(?)", rows) + .. deprecated-removed:: 3.12 3.14 + + :exc:`DeprecationWarning` is emitted if + :ref:`named placeholders ` are used + and the items in *parameters* are sequences + instead of :class:`dict`\s. + Starting with Python 3.14, :exc:`ProgrammingError` will + be raised instead. + .. method:: executescript(sql_script, /) Execute the SQL statements in *sql_script*. @@ -1971,7 +1988,7 @@ question marks (qmark style) or named placeholders (named style). For the qmark style, *parameters* must be a :term:`sequence` whose length must match the number of placeholders, or a :exc:`ProgrammingError` is raised. -For the named style, *parameters* should be +For the named style, *parameters* must be an instance of a :class:`dict` (or a subclass), which must contain keys for all named parameters; any extra items are ignored. diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 45a5e5062d55b6..c62f462a19a2df 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -415,6 +415,13 @@ Deprecated and tailor them to your needs. (Contributed by Erlend E. Aasland in :gh:`90016`.) +* In :meth:`~sqlite3.Cursor.execute`, :exc:`DeprecationWarning` is now emitted + when :ref:`named placeholders ` are used together with + parameters supplied as a :term:`sequence` instead of as a :class:`dict`. + Starting from Python 3.14, using named placeholders with parameters supplied + as a sequence will raise a :exc:`~sqlite3.ProgrammingError`. + (Contributed by Erlend E. Aasland in :gh:`101698`.) + * The 3-arg signatures (type, value, traceback) of :meth:`~coroutine.throw`, :meth:`~generator.throw` and :meth:`~agen.athrow` are deprecated and may be removed in a future version of Python. Use the single-arg versions diff --git a/Lib/test/test_sqlite3/test_dbapi.py b/Lib/test/test_sqlite3/test_dbapi.py index 363a308f3e5fec..695e213cdc7b75 100644 --- a/Lib/test/test_sqlite3/test_dbapi.py +++ b/Lib/test/test_sqlite3/test_dbapi.py @@ -861,6 +861,21 @@ def __getitem__(slf, x): with self.assertRaises(ZeroDivisionError): self.cu.execute("select name from test where name=?", L()) + def test_execute_named_param_and_sequence(self): + dataset = ( + ("select :a", (1,)), + ("select :a, ?, ?", (1, 2, 3)), + ("select ?, :b, ?", (1, 2, 3)), + ("select ?, ?, :c", (1, 2, 3)), + ("select :a, :b, ?", (1, 2, 3)), + ) + msg = "Binding.*is a named parameter" + for query, params in dataset: + with self.subTest(query=query, params=params): + with self.assertWarnsRegex(DeprecationWarning, msg) as cm: + self.cu.execute(query, params) + self.assertEqual(cm.filename, __file__) + def test_execute_too_many_params(self): category = sqlite.SQLITE_LIMIT_VARIABLE_NUMBER msg = "too many SQL variables" diff --git a/Misc/NEWS.d/next/Library/2023-02-08-18-20-58.gh-issue-101693.4_LPXj.rst b/Misc/NEWS.d/next/Library/2023-02-08-18-20-58.gh-issue-101693.4_LPXj.rst new file mode 100644 index 00000000000000..e436054b15b657 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-08-18-20-58.gh-issue-101693.4_LPXj.rst @@ -0,0 +1,6 @@ +In :meth:`sqlite3.Cursor.execute`, :exc:`DeprecationWarning` is now emitted +when :ref:`named placeholders ` are used together with +parameters supplied as a :term:`sequence` instead of as a :class:`dict`. +Starting from Python 3.14, using named placeholders with parameters supplied +as a sequence will raise a :exc:`~sqlite3.ProgrammingError`. +Patch by Erlend E. Aasland. diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c index a4e22bb4a2b58d..6f7970cf8197a2 100644 --- a/Modules/_sqlite/cursor.c +++ b/Modules/_sqlite/cursor.c @@ -662,6 +662,19 @@ bind_parameters(pysqlite_state *state, pysqlite_Statement *self, return; } for (i = 0; i < num_params; i++) { + const char *name = sqlite3_bind_parameter_name(self->st, i+1); + if (name != NULL) { + int ret = PyErr_WarnFormat(PyExc_DeprecationWarning, 1, + "Binding %d ('%s') is a named parameter, but you " + "supplied a sequence which requires nameless (qmark) " + "placeholders. Starting with Python 3.14 an " + "sqlite3.ProgrammingError will be raised.", + i+1, name); + if (ret < 0) { + return; + } + } + if (PyTuple_CheckExact(parameters)) { PyObject *item = PyTuple_GET_ITEM(parameters, i); current_param = Py_NewRef(item); From e8b6aaad2faf11fe315410138a5c5943d610d8d8 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 15 Feb 2023 11:18:27 +0100 Subject: [PATCH 098/247] gh-101819: Remove _testcapi dependencies on specific _io symbols (#101918) --- Modules/_io/_iomodule.c | 12 ++---------- Modules/_testcapimodule.c | 8 ++++++-- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c index 175fa97479d27d..811b1d221a0122 100644 --- a/Modules/_io/_iomodule.c +++ b/Modules/_io/_iomodule.c @@ -720,16 +720,8 @@ PyInit__io(void) // Add types for (size_t i=0; i < Py_ARRAY_LENGTH(static_types); i++) { PyTypeObject *type = static_types[i]; - // Private type not exposed in the _io module - if (type == &_PyBytesIOBuffer_Type) { - if (PyType_Ready(type) < 0) { - goto fail; - } - } - else { - if (PyModule_AddType(m, type) < 0) { - goto fail; - } + if (PyModule_AddType(m, type) < 0) { + goto fail; } } diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 3c411fa0d76358..5610a7689136f6 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -1448,12 +1448,10 @@ test_from_contiguous(PyObject* self, PyObject *Py_UNUSED(ignored)) } #if (defined(__linux__) || defined(__FreeBSD__)) && defined(__GNUC__) -extern PyTypeObject _PyBytesIOBuffer_Type; static PyObject * test_pep3118_obsolete_write_locks(PyObject* self, PyObject *Py_UNUSED(ignored)) { - PyTypeObject *type = &_PyBytesIOBuffer_Type; PyObject *b; char *dummy[1]; int ret, match; @@ -1466,7 +1464,13 @@ test_pep3118_obsolete_write_locks(PyObject* self, PyObject *Py_UNUSED(ignored)) goto error; /* bytesiobuf_getbuffer() */ + PyTypeObject *type = (PyTypeObject *)_PyImport_GetModuleAttrString( + "_io", "_BytesIOBuffer"); + if (type == NULL) { + return NULL; + } b = type->tp_alloc(type, 0); + Py_DECREF(type); if (b == NULL) { return NULL; } From c7766245c14fa03b8afd3aff9be30b13d0069f95 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 15 Feb 2023 12:21:40 +0000 Subject: [PATCH 099/247] GH-87849: Fix refleak in SEND instruction. (GH-101908) Fix refleak in SEND instruction. --- Python/bytecodes.c | 1 + Python/generated_cases.c.h | 1 + 2 files changed, 2 insertions(+) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index be54e5f6f589eb..d5d5034cbfbf74 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -727,6 +727,7 @@ dummy_func( else { assert(retval != NULL); } + Py_DECREF(v); } inst(SEND_GEN, (unused/1, receiver, v -- receiver)) { diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index beb797cbd233d7..8b8a7161ad898e 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -934,6 +934,7 @@ else { assert(retval != NULL); } + Py_DECREF(v); POKE(1, retval); JUMPBY(1); DISPATCH(); From eb0c485b6c836abb71932537a5058344d11d7bc8 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 15 Feb 2023 14:07:59 +0100 Subject: [PATCH 100/247] gh-101819: Remove _PyWindowsConsoleIO_Type from the Windows DLL (GH-101904) Automerge-Triggered-By: GH:erlend-aasland --- .../pycore_global_objects_fini_generated.h | 2 ++ Include/internal/pycore_global_strings.h | 2 ++ Include/internal/pycore_runtime_init_generated.h | 2 ++ Include/internal/pycore_unicodeobject_generated.h | 4 ++++ Modules/_io/_iomodule.h | 4 ---- Modules/_io/winconsoleio.c | 4 +--- PC/_testconsole.c | 11 +++++++++-- Python/pylifecycle.c | 14 +++++++++----- 8 files changed, 29 insertions(+), 14 deletions(-) diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index 8c210111b5899f..dc5cd58d853534 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -577,6 +577,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(True)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(WarningMessage)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_WindowsConsoleIO)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__IOBase_closed)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__abc_tpflags__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__abs__)); @@ -752,6 +753,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_get_sourcefile)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_handle_fromlist)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_initializing)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_io)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_is_text_encoding)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_length_)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_limbo)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index 6b1c8424424d96..8b23aa15479301 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -63,6 +63,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(True) STRUCT_FOR_ID(WarningMessage) STRUCT_FOR_ID(_) + STRUCT_FOR_ID(_WindowsConsoleIO) STRUCT_FOR_ID(__IOBase_closed) STRUCT_FOR_ID(__abc_tpflags__) STRUCT_FOR_ID(__abs__) @@ -238,6 +239,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(_get_sourcefile) STRUCT_FOR_ID(_handle_fromlist) STRUCT_FOR_ID(_initializing) + STRUCT_FOR_ID(_io) STRUCT_FOR_ID(_is_text_encoding) STRUCT_FOR_ID(_length_) STRUCT_FOR_ID(_limbo) diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index fcb613083ffe99..471efadb13bb4f 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -569,6 +569,7 @@ extern "C" { INIT_ID(True), \ INIT_ID(WarningMessage), \ INIT_ID(_), \ + INIT_ID(_WindowsConsoleIO), \ INIT_ID(__IOBase_closed), \ INIT_ID(__abc_tpflags__), \ INIT_ID(__abs__), \ @@ -744,6 +745,7 @@ extern "C" { INIT_ID(_get_sourcefile), \ INIT_ID(_handle_fromlist), \ INIT_ID(_initializing), \ + INIT_ID(_io), \ INIT_ID(_is_text_encoding), \ INIT_ID(_length_), \ INIT_ID(_limbo), \ diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index 301aee5210e799..b47d240e492ff9 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -32,6 +32,8 @@ _PyUnicode_InitStaticStrings(void) { PyUnicode_InternInPlace(&string); string = &_Py_ID(_); PyUnicode_InternInPlace(&string); + string = &_Py_ID(_WindowsConsoleIO); + PyUnicode_InternInPlace(&string); string = &_Py_ID(__IOBase_closed); PyUnicode_InternInPlace(&string); string = &_Py_ID(__abc_tpflags__); @@ -382,6 +384,8 @@ _PyUnicode_InitStaticStrings(void) { PyUnicode_InternInPlace(&string); string = &_Py_ID(_initializing); PyUnicode_InternInPlace(&string); + string = &_Py_ID(_io); + PyUnicode_InternInPlace(&string); string = &_Py_ID(_is_text_encoding); PyUnicode_InternInPlace(&string); string = &_Py_ID(_length_); diff --git a/Modules/_io/_iomodule.h b/Modules/_io/_iomodule.h index c260080f0e348b..7617cb8fb70e43 100644 --- a/Modules/_io/_iomodule.h +++ b/Modules/_io/_iomodule.h @@ -21,13 +21,9 @@ extern PyTypeObject PyBufferedRandom_Type; extern PyTypeObject PyTextIOWrapper_Type; extern PyTypeObject PyIncrementalNewlineDecoder_Type; -#ifndef Py_LIMITED_API #ifdef MS_WINDOWS extern PyTypeObject PyWindowsConsoleIO_Type; -PyAPI_DATA(PyObject *) _PyWindowsConsoleIO_Type; -#define PyWindowsConsoleIO_Check(op) (PyObject_TypeCheck((op), (PyTypeObject*)_PyWindowsConsoleIO_Type)) #endif /* MS_WINDOWS */ -#endif /* Py_LIMITED_API */ /* These functions are used as METH_NOARGS methods, are normally called * with args=NULL, and return a new reference. diff --git a/Modules/_io/winconsoleio.c b/Modules/_io/winconsoleio.c index 4f41ab965e2e67..e913d831874617 100644 --- a/Modules/_io/winconsoleio.c +++ b/Modules/_io/winconsoleio.c @@ -260,7 +260,7 @@ _io__WindowsConsoleIO___init___impl(winconsoleio *self, PyObject *nameobj, int fd_is_own = 0; HANDLE handle = NULL; - assert(PyWindowsConsoleIO_Check(self)); + assert(PyObject_TypeCheck(self, (PyTypeObject *)&PyWindowsConsoleIO_Type)); if (self->fd >= 0) { if (self->closefd) { /* Have to close the existing file first. */ @@ -1174,6 +1174,4 @@ PyTypeObject PyWindowsConsoleIO_Type = { 0, /* tp_finalize */ }; -PyObject * _PyWindowsConsoleIO_Type = (PyObject*)&PyWindowsConsoleIO_Type; - #endif /* MS_WINDOWS */ diff --git a/PC/_testconsole.c b/PC/_testconsole.c index a8308835d8f85d..f14a2d45b1be26 100644 --- a/PC/_testconsole.c +++ b/PC/_testconsole.c @@ -10,7 +10,7 @@ #ifdef MS_WINDOWS #include "pycore_fileutils.h" // _Py_get_osfhandle() -#include "..\modules\_io\_iomodule.h" +#include "pycore_runtime.h" // _Py_ID() #define WIN32_LEAN_AND_MEAN #include @@ -51,7 +51,14 @@ _testconsole_write_input_impl(PyObject *module, PyObject *file, { INPUT_RECORD *rec = NULL; - if (!PyWindowsConsoleIO_Check(file)) { + PyTypeObject *winconsoleio_type = (PyTypeObject *)_PyImport_GetModuleAttr( + &_Py_ID(_io), &_Py_ID(_WindowsConsoleIO)); + if (winconsoleio_type == NULL) { + return NULL; + } + int is_subclass = PyObject_TypeCheck(file, winconsoleio_type); + Py_DECREF(winconsoleio_type); + if (!is_subclass) { PyErr_SetString(PyExc_TypeError, "expected raw console object"); return NULL; } diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index a8a8e7f3d84f21..045a2996e8988b 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -54,10 +54,6 @@ extern void _PyIO_Fini(void); #ifdef MS_WINDOWS # undef BYTE - - extern PyTypeObject PyWindowsConsoleIO_Type; -# define PyWindowsConsoleIO_Check(op) \ - (PyObject_TypeCheck((op), &PyWindowsConsoleIO_Type)) #endif #define PUTS(fd, str) _Py_write_noraise(fd, str, (int)strlen(str)) @@ -2358,8 +2354,16 @@ create_stdio(const PyConfig *config, PyObject* io, #ifdef MS_WINDOWS /* Windows console IO is always UTF-8 encoded */ - if (PyWindowsConsoleIO_Check(raw)) + PyTypeObject *winconsoleio_type = (PyTypeObject *)_PyImport_GetModuleAttr( + &_Py_ID(_io), &_Py_ID(_WindowsConsoleIO)); + if (winconsoleio_type == NULL) { + goto error; + } + int is_subclass = PyObject_TypeCheck(raw, winconsoleio_type); + Py_DECREF(winconsoleio_type); + if (is_subclass) { encoding = L"utf-8"; + } #endif text = PyUnicode_FromString(name); From c1ce0d178fe57b50f37578b285a343d77485ac02 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 15 Feb 2023 22:58:48 +0100 Subject: [PATCH 101/247] gh-99138: Isolate _zoneinfo (#99218) * Convert zone info type to heap type and add it to module state * Add global variables to module state --- Doc/library/zoneinfo.rst | 2 +- Lib/test/test_zoneinfo/test_zoneinfo.py | 4 +- ...3-01-02-22-41-44.gh-issue-99138.17hp9U.rst | 1 + Modules/_zoneinfo.c | 519 +++++++++++------- Modules/clinic/_zoneinfo.c.h | 215 +++++++- Tools/c-analyzer/cpython/globals-to-fix.tsv | 11 - 6 files changed, 539 insertions(+), 213 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-01-02-22-41-44.gh-issue-99138.17hp9U.rst diff --git a/Doc/library/zoneinfo.rst b/Doc/library/zoneinfo.rst index d2e5619e7e47c2..f8624da6e51dbb 100644 --- a/Doc/library/zoneinfo.rst +++ b/Doc/library/zoneinfo.rst @@ -241,7 +241,7 @@ The following class methods are also available: .. warning:: Invoking this function may change the semantics of datetimes using - ``ZoneInfo`` in surprising ways; this modifies process-wide global state + ``ZoneInfo`` in surprising ways; this modifies module state and thus may have wide-ranging effects. Only use it if you know that you need to. diff --git a/Lib/test/test_zoneinfo/test_zoneinfo.py b/Lib/test/test_zoneinfo/test_zoneinfo.py index fd0e3bc032ec0c..82041a2b488334 100644 --- a/Lib/test/test_zoneinfo/test_zoneinfo.py +++ b/Lib/test/test_zoneinfo/test_zoneinfo.py @@ -1797,12 +1797,10 @@ def test_cache_location(self): self.assertTrue(hasattr(py_zoneinfo.ZoneInfo, "_weak_cache")) def test_gc_tracked(self): - # The pure Python version is tracked by the GC but (for now) the C - # version is not. import gc self.assertTrue(gc.is_tracked(py_zoneinfo.ZoneInfo)) - self.assertFalse(gc.is_tracked(c_zoneinfo.ZoneInfo)) + self.assertTrue(gc.is_tracked(c_zoneinfo.ZoneInfo)) @dataclasses.dataclass(frozen=True) diff --git a/Misc/NEWS.d/next/Library/2023-01-02-22-41-44.gh-issue-99138.17hp9U.rst b/Misc/NEWS.d/next/Library/2023-01-02-22-41-44.gh-issue-99138.17hp9U.rst new file mode 100644 index 00000000000000..3dd4646f40e1e5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-02-22-41-44.gh-issue-99138.17hp9U.rst @@ -0,0 +1 @@ +Apply :pep:`687` to :mod:`zoneinfo`. Patch by Erlend E. Aasland. diff --git a/Modules/_zoneinfo.c b/Modules/_zoneinfo.c index 9d38589ea3d1b0..9f423559f51a43 100644 --- a/Modules/_zoneinfo.c +++ b/Modules/_zoneinfo.c @@ -19,10 +19,6 @@ class zoneinfo.ZoneInfo "PyObject *" "PyTypeObject *" [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=d12c73c0eef36df8]*/ -// Imports -static PyObject *io_open = NULL; -static PyObject *_tzpath_find_tzfile = NULL; -static PyObject *_common_mod = NULL; typedef struct TransitionRuleType TransitionRuleType; typedef struct StrongCacheNode StrongCacheNode; @@ -90,15 +86,21 @@ struct StrongCacheNode { PyObject *zone; }; -static PyTypeObject PyZoneInfo_ZoneInfoType; +typedef struct { + PyTypeObject *ZoneInfoType; + + // Imports + PyObject *io_open; + PyObject *_tzpath_find_tzfile; + PyObject *_common_mod; -// Globals -static PyObject *TIMEDELTA_CACHE = NULL; -static PyObject *ZONEINFO_WEAK_CACHE = NULL; -static StrongCacheNode *ZONEINFO_STRONG_CACHE = NULL; -static size_t ZONEINFO_STRONG_CACHE_MAX_SIZE = 8; + // Caches + PyObject *TIMEDELTA_CACHE; + PyObject *ZONEINFO_WEAK_CACHE; + StrongCacheNode *ZONEINFO_STRONG_CACHE; -static _ttinfo NO_TTINFO = {NULL, NULL, NULL, 0}; + _ttinfo NO_TTINFO; +} zoneinfo_state; // Constants static const int EPOCHORDINAL = 719163; @@ -114,9 +116,12 @@ static const int SOURCE_NOCACHE = 0; static const int SOURCE_CACHE = 1; static const int SOURCE_FILE = 2; +static const size_t ZONEINFO_STRONG_CACHE_MAX_SIZE = 8; + // Forward declarations static int -load_data(PyZoneInfo_ZoneInfo *self, PyObject *file_obj); +load_data(zoneinfo_state *state, PyZoneInfo_ZoneInfo *self, + PyObject *file_obj); static void utcoff_to_dstoff(size_t *trans_idx, long *utcoffs, long *dstoffs, unsigned char *isdsts, size_t num_transitions, @@ -127,7 +132,7 @@ ts_to_local(size_t *trans_idx, int64_t *trans_utc, long *utcoff, size_t num_transitions); static int -parse_tz_str(PyObject *tz_str_obj, _tzrule *out); +parse_tz_str(zoneinfo_state *state, PyObject *tz_str_obj, _tzrule *out); static Py_ssize_t parse_abbr(const char *const p, PyObject **abbr); @@ -146,26 +151,27 @@ find_tzrule_ttinfo_fromutc(_tzrule *rule, int64_t ts, int year, unsigned char *fold); static int -build_ttinfo(long utcoffset, long dstoffset, PyObject *tzname, _ttinfo *out); +build_ttinfo(zoneinfo_state *state, long utcoffset, long dstoffset, + PyObject *tzname, _ttinfo *out); static void xdecref_ttinfo(_ttinfo *ttinfo); static int ttinfo_eq(const _ttinfo *const tti0, const _ttinfo *const tti1); static int -build_tzrule(PyObject *std_abbr, PyObject *dst_abbr, long std_offset, - long dst_offset, TransitionRuleType *start, +build_tzrule(zoneinfo_state *state, PyObject *std_abbr, PyObject *dst_abbr, + long std_offset, long dst_offset, TransitionRuleType *start, TransitionRuleType *end, _tzrule *out); static void free_tzrule(_tzrule *tzrule); static PyObject * -load_timedelta(long seconds); +load_timedelta(zoneinfo_state *state, long seconds); static int get_local_timestamp(PyObject *dt, int64_t *local_ts); static _ttinfo * -find_ttinfo(PyZoneInfo_ZoneInfo *self, PyObject *dt); +find_ttinfo(zoneinfo_state *state, PyZoneInfo_ZoneInfo *self, PyObject *dt); static int ymd_to_ord(int y, int m, int d); @@ -176,27 +182,57 @@ static size_t _bisect(const int64_t value, const int64_t *arr, size_t size); static int -eject_from_strong_cache(const PyTypeObject *const type, PyObject *key); +eject_from_strong_cache(zoneinfo_state *state, const PyTypeObject *const type, + PyObject *key); static void -clear_strong_cache(const PyTypeObject *const type); +clear_strong_cache(zoneinfo_state *state, const PyTypeObject *const type); static void -update_strong_cache(const PyTypeObject *const type, PyObject *key, - PyObject *zone); +update_strong_cache(zoneinfo_state *state, const PyTypeObject *const type, + PyObject *key, PyObject *zone); static PyObject * -zone_from_strong_cache(const PyTypeObject *const type, PyObject *const key); +zone_from_strong_cache(zoneinfo_state *state, const PyTypeObject *const type, + PyObject *const key); + +static inline zoneinfo_state * +zoneinfo_get_state(PyObject *mod) +{ + zoneinfo_state *state = (zoneinfo_state *)PyModule_GetState(mod); + assert(state != NULL); + return state; +} + +static inline zoneinfo_state * +zoneinfo_get_state_by_cls(PyTypeObject *cls) +{ + zoneinfo_state *state = (zoneinfo_state *)PyType_GetModuleState(cls); + assert(state != NULL); + return state; +} + +static struct PyModuleDef zoneinfomodule; + +static inline zoneinfo_state * +zoneinfo_get_state_by_self(PyTypeObject *self) +{ + PyObject *mod = PyType_GetModuleByDef(self, &zoneinfomodule); + assert(mod != NULL); + return zoneinfo_get_state(mod); +} static PyObject * -zoneinfo_new_instance(PyTypeObject *type, PyObject *key) +zoneinfo_new_instance(zoneinfo_state *state, PyTypeObject *type, PyObject *key) { PyObject *file_obj = NULL; PyObject *file_path = NULL; - file_path = PyObject_CallFunctionObjArgs(_tzpath_find_tzfile, key, NULL); + file_path = PyObject_CallFunctionObjArgs(state->_tzpath_find_tzfile, + key, NULL); if (file_path == NULL) { return NULL; } else if (file_path == Py_None) { - file_obj = PyObject_CallMethod(_common_mod, "load_tzdata", "O", key); + PyObject *meth = state->_common_mod; + file_obj = PyObject_CallMethod(meth, "load_tzdata", "O", key); if (file_obj == NULL) { Py_DECREF(file_path); return NULL; @@ -209,13 +245,14 @@ zoneinfo_new_instance(PyTypeObject *type, PyObject *key) } if (file_obj == NULL) { - file_obj = PyObject_CallFunction(io_open, "Os", file_path, "rb"); + PyObject *func = state->io_open; + file_obj = PyObject_CallFunction(func, "Os", file_path, "rb"); if (file_obj == NULL) { goto error; } } - if (load_data((PyZoneInfo_ZoneInfo *)self, file_obj)) { + if (load_data(state, (PyZoneInfo_ZoneInfo *)self, file_obj)) { goto error; } @@ -248,10 +285,10 @@ zoneinfo_new_instance(PyTypeObject *type, PyObject *key) } static PyObject * -get_weak_cache(PyTypeObject *type) +get_weak_cache(zoneinfo_state *state, PyTypeObject *type) { - if (type == &PyZoneInfo_ZoneInfoType) { - return ZONEINFO_WEAK_CACHE; + if (type == state->ZoneInfoType) { + return state->ZONEINFO_WEAK_CACHE; } else { PyObject *cache = @@ -273,12 +310,13 @@ zoneinfo_new(PyTypeObject *type, PyObject *args, PyObject *kw) return NULL; } - PyObject *instance = zone_from_strong_cache(type, key); + zoneinfo_state *state = zoneinfo_get_state_by_self(type); + PyObject *instance = zone_from_strong_cache(state, type, key); if (instance != NULL || PyErr_Occurred()) { return instance; } - PyObject *weak_cache = get_weak_cache(type); + PyObject *weak_cache = get_weak_cache(state, type); instance = PyObject_CallMethod(weak_cache, "get", "O", key, Py_None); if (instance == NULL) { return NULL; @@ -286,7 +324,7 @@ zoneinfo_new(PyTypeObject *type, PyObject *args, PyObject *kw) if (instance == Py_None) { Py_DECREF(instance); - PyObject *tmp = zoneinfo_new_instance(type, key); + PyObject *tmp = zoneinfo_new_instance(state, type, key); if (tmp == NULL) { return NULL; } @@ -300,14 +338,32 @@ zoneinfo_new(PyTypeObject *type, PyObject *args, PyObject *kw) ((PyZoneInfo_ZoneInfo *)instance)->source = SOURCE_CACHE; } - update_strong_cache(type, key, instance); + update_strong_cache(state, type, key, instance); return instance; } +static int +zoneinfo_traverse(PyZoneInfo_ZoneInfo *self, visitproc visit, void *arg) +{ + Py_VISIT(Py_TYPE(self)); + Py_VISIT(self->key); + return 0; +} + +static int +zoneinfo_clear(PyZoneInfo_ZoneInfo *self) +{ + Py_CLEAR(self->key); + Py_CLEAR(self->file_repr); + return 0; +} + static void zoneinfo_dealloc(PyObject *obj_self) { PyZoneInfo_ZoneInfo *self = (PyZoneInfo_ZoneInfo *)obj_self; + PyTypeObject *tp = Py_TYPE(self); + PyObject_GC_UnTrack(self); if (self->weakreflist != NULL) { PyObject_ClearWeakRefs(obj_self); @@ -336,16 +392,16 @@ zoneinfo_dealloc(PyObject *obj_self) free_tzrule(&(self->tzrule_after)); - Py_XDECREF(self->key); - Py_XDECREF(self->file_repr); - - Py_TYPE(self)->tp_free((PyObject *)self); + zoneinfo_clear(self); + tp->tp_free(obj_self); + Py_DECREF(tp); } /*[clinic input] @classmethod zoneinfo.ZoneInfo.from_file + cls: defining_class file_obj: object / key: object = None @@ -354,9 +410,9 @@ Create a ZoneInfo file from a file object. [clinic start generated code]*/ static PyObject * -zoneinfo_ZoneInfo_from_file_impl(PyTypeObject *type, PyObject *file_obj, - PyObject *key) -/*[clinic end generated code: output=68ed2022404ae5be input=ccfe73708133d2e4]*/ +zoneinfo_ZoneInfo_from_file_impl(PyTypeObject *type, PyTypeObject *cls, + PyObject *file_obj, PyObject *key) +/*[clinic end generated code: output=77887d1d56a48324 input=d26111f29eed6863]*/ { PyObject *file_repr = NULL; PyZoneInfo_ZoneInfo *self = NULL; @@ -372,7 +428,8 @@ zoneinfo_ZoneInfo_from_file_impl(PyTypeObject *type, PyObject *file_obj, goto error; } - if (load_data(self, file_obj)) { + zoneinfo_state *state = zoneinfo_get_state_by_cls(cls); + if (load_data(state, self, file_obj)) { goto error; } @@ -391,16 +448,20 @@ zoneinfo_ZoneInfo_from_file_impl(PyTypeObject *type, PyObject *file_obj, @classmethod zoneinfo.ZoneInfo.no_cache + cls: defining_class + / key: object Get a new instance of ZoneInfo, bypassing the cache. [clinic start generated code]*/ static PyObject * -zoneinfo_ZoneInfo_no_cache_impl(PyTypeObject *type, PyObject *key) -/*[clinic end generated code: output=751c6894ad66f91b input=bb24afd84a80ba46]*/ +zoneinfo_ZoneInfo_no_cache_impl(PyTypeObject *type, PyTypeObject *cls, + PyObject *key) +/*[clinic end generated code: output=b0b09b3344c171b7 input=0238f3d56b1ea3f1]*/ { - PyObject *out = zoneinfo_new_instance(type, key); + zoneinfo_state *state = zoneinfo_get_state_by_cls(cls); + PyObject *out = zoneinfo_new_instance(state, type, key); if (out != NULL) { ((PyZoneInfo_ZoneInfo *)out)->source = SOURCE_NOCACHE; } @@ -412,6 +473,8 @@ zoneinfo_ZoneInfo_no_cache_impl(PyTypeObject *type, PyObject *key) @classmethod zoneinfo.ZoneInfo.clear_cache + cls: defining_class + / * only_keys: object = None @@ -419,10 +482,12 @@ Clear the ZoneInfo cache. [clinic start generated code]*/ static PyObject * -zoneinfo_ZoneInfo_clear_cache_impl(PyTypeObject *type, PyObject *only_keys) -/*[clinic end generated code: output=eec0a3276f07bd90 input=8cff0182a95f295b]*/ +zoneinfo_ZoneInfo_clear_cache_impl(PyTypeObject *type, PyTypeObject *cls, + PyObject *only_keys) +/*[clinic end generated code: output=114d9b7c8a22e660 input=e32ca3bb396788ba]*/ { - PyObject *weak_cache = get_weak_cache(type); + zoneinfo_state *state = zoneinfo_get_state_by_cls(cls); + PyObject *weak_cache = get_weak_cache(state, type); if (only_keys == NULL || only_keys == Py_None) { PyObject *rv = PyObject_CallMethod(weak_cache, "clear", NULL); @@ -430,7 +495,7 @@ zoneinfo_ZoneInfo_clear_cache_impl(PyTypeObject *type, PyObject *only_keys) Py_DECREF(rv); } - clear_strong_cache(type); + clear_strong_cache(state, type); } else { PyObject *item = NULL; @@ -447,7 +512,7 @@ zoneinfo_ZoneInfo_clear_cache_impl(PyTypeObject *type, PyObject *only_keys) while ((item = PyIter_Next(iter))) { // Remove from strong cache - if (eject_from_strong_cache(type, item) < 0) { + if (eject_from_strong_cache(state, type, item) < 0) { Py_DECREF(item); break; } @@ -473,30 +538,68 @@ zoneinfo_ZoneInfo_clear_cache_impl(PyTypeObject *type, PyObject *only_keys) Py_RETURN_NONE; } +/*[clinic input] +zoneinfo.ZoneInfo.utcoffset + + cls: defining_class + dt: object + / + +Retrieve a timedelta representing the UTC offset in a zone at the given datetime. +[clinic start generated code]*/ + static PyObject * -zoneinfo_utcoffset(PyObject *self, PyObject *dt) +zoneinfo_ZoneInfo_utcoffset_impl(PyObject *self, PyTypeObject *cls, + PyObject *dt) +/*[clinic end generated code: output=b71016c319ba1f91 input=2bb6c5364938f19c]*/ { - _ttinfo *tti = find_ttinfo((PyZoneInfo_ZoneInfo *)self, dt); + zoneinfo_state *state = zoneinfo_get_state_by_cls(cls); + _ttinfo *tti = find_ttinfo(state, (PyZoneInfo_ZoneInfo *)self, dt); if (tti == NULL) { return NULL; } return Py_NewRef(tti->utcoff); } +/*[clinic input] +zoneinfo.ZoneInfo.dst + + cls: defining_class + dt: object + / + +Retrieve a timedelta representing the amount of DST applied in a zone at the given datetime. +[clinic start generated code]*/ + static PyObject * -zoneinfo_dst(PyObject *self, PyObject *dt) +zoneinfo_ZoneInfo_dst_impl(PyObject *self, PyTypeObject *cls, PyObject *dt) +/*[clinic end generated code: output=cb6168d7723a6ae6 input=2167fb80cf8645c6]*/ { - _ttinfo *tti = find_ttinfo((PyZoneInfo_ZoneInfo *)self, dt); + zoneinfo_state *state = zoneinfo_get_state_by_cls(cls); + _ttinfo *tti = find_ttinfo(state, (PyZoneInfo_ZoneInfo *)self, dt); if (tti == NULL) { return NULL; } return Py_NewRef(tti->dstoff); } +/*[clinic input] +zoneinfo.ZoneInfo.tzname + + cls: defining_class + dt: object + / + +Retrieve a string containing the abbreviation for the time zone that applies in a zone at a given datetime. +[clinic start generated code]*/ + static PyObject * -zoneinfo_tzname(PyObject *self, PyObject *dt) +zoneinfo_ZoneInfo_tzname_impl(PyObject *self, PyTypeObject *cls, + PyObject *dt) +/*[clinic end generated code: output=3b6ae6c3053ea75a input=15a59a4f92ed1f1f]*/ { - _ttinfo *tti = find_ttinfo((PyZoneInfo_ZoneInfo *)self, dt); + zoneinfo_state *state = zoneinfo_get_state_by_cls(cls); + _ttinfo *tti = find_ttinfo(state, (PyZoneInfo_ZoneInfo *)self, dt); if (tti == NULL) { return NULL; } @@ -693,28 +796,37 @@ zoneinfo_reduce(PyObject *obj_self, PyObject *unused) return rv; } +/*[clinic input] +@classmethod +zoneinfo.ZoneInfo._unpickle + + cls: defining_class + key: object + from_cache: unsigned_char(bitwise=True) + / + +Private method used in unpickling. +[clinic start generated code]*/ + static PyObject * -zoneinfo__unpickle(PyTypeObject *cls, PyObject *args) +zoneinfo_ZoneInfo__unpickle_impl(PyTypeObject *type, PyTypeObject *cls, + PyObject *key, unsigned char from_cache) +/*[clinic end generated code: output=556712fc709deecb input=6ac8c73eed3de316]*/ { - PyObject *key; - unsigned char from_cache; - if (!PyArg_ParseTuple(args, "OB", &key, &from_cache)) { - return NULL; - } - if (from_cache) { PyObject *val_args = Py_BuildValue("(O)", key); if (val_args == NULL) { return NULL; } - PyObject *rv = zoneinfo_new(cls, val_args, NULL); + PyObject *rv = zoneinfo_new(type, val_args, NULL); Py_DECREF(val_args); return rv; } else { - return zoneinfo_new_instance(cls, key); + zoneinfo_state *state = zoneinfo_get_state_by_cls(cls); + return zoneinfo_new_instance(state, type, key); } } @@ -732,14 +844,14 @@ zoneinfo__unpickle(PyTypeObject *cls, PyObject *args) * This returns a new reference to the timedelta. */ static PyObject * -load_timedelta(long seconds) +load_timedelta(zoneinfo_state *state, long seconds) { PyObject *rv; PyObject *pyoffset = PyLong_FromLong(seconds); if (pyoffset == NULL) { return NULL; } - rv = PyDict_GetItemWithError(TIMEDELTA_CACHE, pyoffset); + rv = PyDict_GetItemWithError(state->TIMEDELTA_CACHE, pyoffset); if (rv == NULL) { if (PyErr_Occurred()) { goto error; @@ -751,7 +863,7 @@ load_timedelta(long seconds) goto error; } - rv = PyDict_SetDefault(TIMEDELTA_CACHE, pyoffset, tmp); + rv = PyDict_SetDefault(state->TIMEDELTA_CACHE, pyoffset, tmp); Py_DECREF(tmp); } @@ -768,19 +880,20 @@ load_timedelta(long seconds) * initialized _ttinfo objects. */ static int -build_ttinfo(long utcoffset, long dstoffset, PyObject *tzname, _ttinfo *out) +build_ttinfo(zoneinfo_state *state, long utcoffset, long dstoffset, + PyObject *tzname, _ttinfo *out) { out->utcoff = NULL; out->dstoff = NULL; out->tzname = NULL; out->utcoff_seconds = utcoffset; - out->utcoff = load_timedelta(utcoffset); + out->utcoff = load_timedelta(state, utcoffset); if (out->utcoff == NULL) { return -1; } - out->dstoff = load_timedelta(dstoffset); + out->dstoff = load_timedelta(state, dstoffset); if (out->dstoff == NULL) { return -1; } @@ -836,7 +949,7 @@ ttinfo_eq(const _ttinfo *const tti0, const _ttinfo *const tti1) * the object only needs to be freed / deallocated if this succeeds. */ static int -load_data(PyZoneInfo_ZoneInfo *self, PyObject *file_obj) +load_data(zoneinfo_state *state, PyZoneInfo_ZoneInfo *self, PyObject *file_obj) { PyObject *data_tuple = NULL; @@ -854,7 +967,8 @@ load_data(PyZoneInfo_ZoneInfo *self, PyObject *file_obj) size_t ttinfos_allocated = 0; - data_tuple = PyObject_CallMethod(_common_mod, "load_data", "O", file_obj); + data_tuple = PyObject_CallMethod(state->_common_mod, "load_data", "O", + file_obj); if (data_tuple == NULL) { goto error; @@ -1012,7 +1126,9 @@ load_data(PyZoneInfo_ZoneInfo *self, PyObject *file_obj) } ttinfos_allocated++; - if (build_ttinfo(utcoff[i], dstoff[i], tzname, &(self->_ttinfos[i]))) { + int rc = build_ttinfo(state, utcoff[i], dstoff[i], tzname, + &(self->_ttinfos[i])); + if (rc) { goto error; } } @@ -1044,7 +1160,7 @@ load_data(PyZoneInfo_ZoneInfo *self, PyObject *file_obj) } if (tz_str != Py_None && PyObject_IsTrue(tz_str)) { - if (parse_tz_str(tz_str, &(self->tzrule_after))) { + if (parse_tz_str(state, tz_str, &(self->tzrule_after))) { goto error; } } @@ -1063,8 +1179,8 @@ load_data(PyZoneInfo_ZoneInfo *self, PyObject *file_obj) } _ttinfo *tti = &(self->_ttinfos[idx]); - build_tzrule(tti->tzname, NULL, tti->utcoff_seconds, 0, NULL, NULL, - &(self->tzrule_after)); + build_tzrule(state, tti->tzname, NULL, tti->utcoff_seconds, 0, NULL, + NULL, &(self->tzrule_after)); // We've abused the build_tzrule constructor to construct an STD-only // rule mimicking whatever ttinfo we've picked up, but it's possible @@ -1463,7 +1579,7 @@ find_tzrule_ttinfo_fromutc(_tzrule *rule, int64_t ts, int year, * https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html */ static int -parse_tz_str(PyObject *tz_str_obj, _tzrule *out) +parse_tz_str(zoneinfo_state *state, PyObject *tz_str_obj, _tzrule *out) { PyObject *std_abbr = NULL; PyObject *dst_abbr = NULL; @@ -1555,7 +1671,8 @@ parse_tz_str(PyObject *tz_str_obj, _tzrule *out) } complete: - build_tzrule(std_abbr, dst_abbr, std_offset, dst_offset, start, end, out); + build_tzrule(state, std_abbr, dst_abbr, std_offset, dst_offset, + start, end, out); Py_DECREF(std_abbr); Py_XDECREF(dst_abbr); @@ -1913,8 +2030,8 @@ parse_transition_time(const char *const p, int8_t *hour, int8_t *minute, * Returns 0 on success. */ static int -build_tzrule(PyObject *std_abbr, PyObject *dst_abbr, long std_offset, - long dst_offset, TransitionRuleType *start, +build_tzrule(zoneinfo_state *state, PyObject *std_abbr, PyObject *dst_abbr, + long std_offset, long dst_offset, TransitionRuleType *start, TransitionRuleType *end, _tzrule *out) { _tzrule rv = {{0}}; @@ -1922,13 +2039,13 @@ build_tzrule(PyObject *std_abbr, PyObject *dst_abbr, long std_offset, rv.start = start; rv.end = end; - if (build_ttinfo(std_offset, 0, std_abbr, &rv.std)) { + if (build_ttinfo(state, std_offset, 0, std_abbr, &rv.std)) { goto error; } if (dst_abbr != NULL) { rv.dst_diff = dst_offset - std_offset; - if (build_ttinfo(dst_offset, rv.dst_diff, dst_abbr, &rv.dst)) { + if (build_ttinfo(state, dst_offset, rv.dst_diff, dst_abbr, &rv.dst)) { goto error; } } @@ -2132,7 +2249,7 @@ _bisect(const int64_t value, const int64_t *arr, size_t size) /* Find the ttinfo rules that apply at a given local datetime. */ static _ttinfo * -find_ttinfo(PyZoneInfo_ZoneInfo *self, PyObject *dt) +find_ttinfo(zoneinfo_state *state, PyZoneInfo_ZoneInfo *self, PyObject *dt) { // datetime.time has a .tzinfo attribute that passes None as the dt // argument; it only really has meaning for fixed-offset zones. @@ -2141,7 +2258,7 @@ find_ttinfo(PyZoneInfo_ZoneInfo *self, PyObject *dt) return &(self->tzrule_after.std); } else { - return &NO_TTINFO; + return &(state->NO_TTINFO); } } @@ -2317,10 +2434,10 @@ strong_cache_free(StrongCacheNode *root) * the front of the cache. */ static void -remove_from_strong_cache(StrongCacheNode *node) +remove_from_strong_cache(zoneinfo_state *state, StrongCacheNode *node) { - if (ZONEINFO_STRONG_CACHE == node) { - ZONEINFO_STRONG_CACHE = node->next; + if (state->ZONEINFO_STRONG_CACHE == node) { + state->ZONEINFO_STRONG_CACHE = node->next; } if (node->prev != NULL) { @@ -2366,15 +2483,17 @@ find_in_strong_cache(const StrongCacheNode *const root, PyObject *const key) * This function is used to enable the per-key functionality in clear_cache. */ static int -eject_from_strong_cache(const PyTypeObject *const type, PyObject *key) +eject_from_strong_cache(zoneinfo_state *state, const PyTypeObject *const type, + PyObject *key) { - if (type != &PyZoneInfo_ZoneInfoType) { + if (type != state->ZoneInfoType) { return 0; } - StrongCacheNode *node = find_in_strong_cache(ZONEINFO_STRONG_CACHE, key); + StrongCacheNode *cache = state->ZONEINFO_STRONG_CACHE; + StrongCacheNode *node = find_in_strong_cache(cache, key); if (node != NULL) { - remove_from_strong_cache(node); + remove_from_strong_cache(state, node); strong_cache_node_free(node); } @@ -2390,14 +2509,15 @@ eject_from_strong_cache(const PyTypeObject *const type, PyObject *key) * it is not at the front of the cache, it needs to be moved there. */ static void -move_strong_cache_node_to_front(StrongCacheNode **root, StrongCacheNode *node) +move_strong_cache_node_to_front(zoneinfo_state *state, StrongCacheNode **root, + StrongCacheNode *node) { StrongCacheNode *root_p = *root; if (root_p == node) { return; } - remove_from_strong_cache(node); + remove_from_strong_cache(state, node); node->prev = NULL; node->next = root_p; @@ -2419,16 +2539,19 @@ move_strong_cache_node_to_front(StrongCacheNode **root, StrongCacheNode *node) * always returns a cache miss for subclasses. */ static PyObject * -zone_from_strong_cache(const PyTypeObject *const type, PyObject *const key) +zone_from_strong_cache(zoneinfo_state *state, const PyTypeObject *const type, + PyObject *const key) { - if (type != &PyZoneInfo_ZoneInfoType) { + if (type != state->ZoneInfoType) { return NULL; // Strong cache currently only implemented for base class } - StrongCacheNode *node = find_in_strong_cache(ZONEINFO_STRONG_CACHE, key); + StrongCacheNode *cache = state->ZONEINFO_STRONG_CACHE; + StrongCacheNode *node = find_in_strong_cache(cache, key); if (node != NULL) { - move_strong_cache_node_to_front(&ZONEINFO_STRONG_CACHE, node); + StrongCacheNode **root = &(state->ZONEINFO_STRONG_CACHE); + move_strong_cache_node_to_front(state, root, node); return Py_NewRef(node->zone); } @@ -2442,16 +2565,16 @@ zone_from_strong_cache(const PyTypeObject *const type, PyObject *const key) * the cache to at most ZONEINFO_STRONG_CACHE_MAX_SIZE). */ static void -update_strong_cache(const PyTypeObject *const type, PyObject *key, - PyObject *zone) +update_strong_cache(zoneinfo_state *state, const PyTypeObject *const type, + PyObject *key, PyObject *zone) { - if (type != &PyZoneInfo_ZoneInfoType) { + if (type != state->ZoneInfoType) { return; } StrongCacheNode *new_node = strong_cache_node_new(key, zone); - - move_strong_cache_node_to_front(&ZONEINFO_STRONG_CACHE, new_node); + StrongCacheNode **root = &(state->ZONEINFO_STRONG_CACHE); + move_strong_cache_node_to_front(state, root, new_node); StrongCacheNode *node = new_node->next; for (size_t i = 1; i < ZONEINFO_STRONG_CACHE_MAX_SIZE; ++i) { @@ -2476,14 +2599,14 @@ update_strong_cache(const PyTypeObject *const type, PyObject *key, * for everything except the base class. */ void -clear_strong_cache(const PyTypeObject *const type) +clear_strong_cache(zoneinfo_state *state, const PyTypeObject *const type) { - if (type != &PyZoneInfo_ZoneInfoType) { + if (type != state->ZoneInfoType) { return; } - strong_cache_free(ZONEINFO_STRONG_CACHE); - ZONEINFO_STRONG_CACHE = NULL; + strong_cache_free(state->ZONEINFO_STRONG_CACHE); + state->ZONEINFO_STRONG_CACHE = NULL; } static PyObject * @@ -2499,29 +2622,17 @@ new_weak_cache(void) return weak_cache; } +// This function is not idempotent and must be called on a new module object. static int -initialize_caches(void) +initialize_caches(zoneinfo_state *state) { - // TODO: Move to a PyModule_GetState / PEP 573 based caching system. - if (TIMEDELTA_CACHE == NULL) { - TIMEDELTA_CACHE = PyDict_New(); - } - else { - Py_INCREF(TIMEDELTA_CACHE); - } - - if (TIMEDELTA_CACHE == NULL) { + state->TIMEDELTA_CACHE = PyDict_New(); + if (state->TIMEDELTA_CACHE == NULL) { return -1; } - if (ZONEINFO_WEAK_CACHE == NULL) { - ZONEINFO_WEAK_CACHE = new_weak_cache(); - } - else { - Py_INCREF(ZONEINFO_WEAK_CACHE); - } - - if (ZONEINFO_WEAK_CACHE == NULL) { + state->ZONEINFO_WEAK_CACHE = new_weak_cache(); + if (state->ZONEINFO_WEAK_CACHE == NULL) { return -1; } @@ -2551,22 +2662,15 @@ static PyMethodDef zoneinfo_methods[] = { ZONEINFO_ZONEINFO_CLEAR_CACHE_METHODDEF ZONEINFO_ZONEINFO_NO_CACHE_METHODDEF ZONEINFO_ZONEINFO_FROM_FILE_METHODDEF - {"utcoffset", (PyCFunction)zoneinfo_utcoffset, METH_O, - PyDoc_STR("Retrieve a timedelta representing the UTC offset in a zone at " - "the given datetime.")}, - {"dst", (PyCFunction)zoneinfo_dst, METH_O, - PyDoc_STR("Retrieve a timedelta representing the amount of DST applied " - "in a zone at the given datetime.")}, - {"tzname", (PyCFunction)zoneinfo_tzname, METH_O, - PyDoc_STR("Retrieve a string containing the abbreviation for the time " - "zone that applies in a zone at a given datetime.")}, + ZONEINFO_ZONEINFO_UTCOFFSET_METHODDEF + ZONEINFO_ZONEINFO_DST_METHODDEF + ZONEINFO_ZONEINFO_TZNAME_METHODDEF {"fromutc", (PyCFunction)zoneinfo_fromutc, METH_O, PyDoc_STR("Given a datetime with local time in UTC, retrieve an adjusted " "datetime in local time.")}, {"__reduce__", (PyCFunction)zoneinfo_reduce, METH_NOARGS, PyDoc_STR("Function for serialization with the pickle protocol.")}, - {"_unpickle", (PyCFunction)zoneinfo__unpickle, METH_VARARGS | METH_CLASS, - PyDoc_STR("Private method used in unpickling.")}, + ZONEINFO_ZONEINFO__UNPICKLE_METHODDEF {"__init_subclass__", (PyCFunction)(void (*)(void))zoneinfo_init_subclass, METH_VARARGS | METH_KEYWORDS | METH_CLASS, PyDoc_STR("Function to initialize subclasses.")}, @@ -2579,50 +2683,88 @@ static PyMemberDef zoneinfo_members[] = { .type = T_OBJECT_EX, .flags = READONLY, .doc = NULL}, + {.name = "__weaklistoffset__", + .offset = offsetof(PyZoneInfo_ZoneInfo, weakreflist), + .type = T_PYSSIZET, + .flags = READONLY}, {NULL}, /* Sentinel */ }; -static PyTypeObject PyZoneInfo_ZoneInfoType = { - PyVarObject_HEAD_INIT(NULL, 0) // - .tp_name = "zoneinfo.ZoneInfo", - .tp_basicsize = sizeof(PyZoneInfo_ZoneInfo), - .tp_weaklistoffset = offsetof(PyZoneInfo_ZoneInfo, weakreflist), - .tp_repr = (reprfunc)zoneinfo_repr, - .tp_str = (reprfunc)zoneinfo_str, - .tp_getattro = PyObject_GenericGetAttr, - .tp_flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE), - /* .tp_doc = zoneinfo_doc, */ - .tp_methods = zoneinfo_methods, - .tp_members = zoneinfo_members, - .tp_new = zoneinfo_new, - .tp_dealloc = zoneinfo_dealloc, +static PyType_Slot zoneinfo_slots[] = { + {Py_tp_repr, zoneinfo_repr}, + {Py_tp_str, zoneinfo_str}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_methods, zoneinfo_methods}, + {Py_tp_members, zoneinfo_members}, + {Py_tp_new, zoneinfo_new}, + {Py_tp_dealloc, zoneinfo_dealloc}, + {Py_tp_traverse, zoneinfo_traverse}, + {Py_tp_clear, zoneinfo_clear}, + {0, NULL}, +}; + +static PyType_Spec zoneinfo_spec = { + .name = "zoneinfo.ZoneInfo", + .basicsize = sizeof(PyZoneInfo_ZoneInfo), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE), + .slots = zoneinfo_slots, }; ///// // Specify the _zoneinfo module static PyMethodDef module_methods[] = {{NULL, NULL}}; -static void -module_free(void *m) + +static int +module_traverse(PyObject *mod, visitproc visit, void *arg) { - Py_CLEAR(_tzpath_find_tzfile); - Py_CLEAR(_common_mod); - Py_CLEAR(io_open); + zoneinfo_state *state = zoneinfo_get_state(mod); - xdecref_ttinfo(&NO_TTINFO); + Py_VISIT(state->ZoneInfoType); + Py_VISIT(state->io_open); + Py_VISIT(state->_tzpath_find_tzfile); + Py_VISIT(state->_common_mod); + Py_VISIT(state->TIMEDELTA_CACHE); + Py_VISIT(state->ZONEINFO_WEAK_CACHE); - if (TIMEDELTA_CACHE != NULL && Py_REFCNT(TIMEDELTA_CACHE) > 1) { - Py_DECREF(TIMEDELTA_CACHE); - } else { - Py_CLEAR(TIMEDELTA_CACHE); + StrongCacheNode *node = state->ZONEINFO_STRONG_CACHE; + while (node != NULL) { + StrongCacheNode *next = node->next; + Py_VISIT(node->key); + Py_VISIT(node->zone); + node = next; } - if (ZONEINFO_WEAK_CACHE != NULL && Py_REFCNT(ZONEINFO_WEAK_CACHE) > 1) { - Py_DECREF(ZONEINFO_WEAK_CACHE); - } else { - Py_CLEAR(ZONEINFO_WEAK_CACHE); - } + Py_VISIT(state->NO_TTINFO.utcoff); + Py_VISIT(state->NO_TTINFO.dstoff); + Py_VISIT(state->NO_TTINFO.tzname); - clear_strong_cache(&PyZoneInfo_ZoneInfoType); + return 0; +} + +static int +module_clear(PyObject *mod) +{ + zoneinfo_state *state = zoneinfo_get_state(mod); + + Py_CLEAR(state->ZoneInfoType); + Py_CLEAR(state->io_open); + Py_CLEAR(state->_tzpath_find_tzfile); + Py_CLEAR(state->_common_mod); + Py_CLEAR(state->TIMEDELTA_CACHE); + Py_CLEAR(state->ZONEINFO_WEAK_CACHE); + clear_strong_cache(state, state->ZoneInfoType); + Py_CLEAR(state->NO_TTINFO.utcoff); + Py_CLEAR(state->NO_TTINFO.dstoff); + Py_CLEAR(state->NO_TTINFO.tzname); + + return 0; +} + +static void +module_free(void *mod) +{ + (void)module_clear((PyObject *)mod); } static int @@ -2632,39 +2774,45 @@ zoneinfomodule_exec(PyObject *m) if (PyDateTimeAPI == NULL) { goto error; } - PyZoneInfo_ZoneInfoType.tp_base = PyDateTimeAPI->TZInfoType; - if (PyType_Ready(&PyZoneInfo_ZoneInfoType) < 0) { + + zoneinfo_state *state = zoneinfo_get_state(m); + PyObject *base = (PyObject *)PyDateTimeAPI->TZInfoType; + state->ZoneInfoType = (PyTypeObject *)PyType_FromModuleAndSpec(m, + &zoneinfo_spec, base); + if (state->ZoneInfoType == NULL) { goto error; } - if (PyModule_AddObjectRef(m, "ZoneInfo", (PyObject *)&PyZoneInfo_ZoneInfoType) < 0) { + int rc = PyModule_AddObjectRef(m, "ZoneInfo", + (PyObject *)state->ZoneInfoType); + if (rc < 0) { goto error; } /* Populate imports */ - _tzpath_find_tzfile = + state->_tzpath_find_tzfile = _PyImport_GetModuleAttrString("zoneinfo._tzpath", "find_tzfile"); - if (_tzpath_find_tzfile == NULL) { + if (state->_tzpath_find_tzfile == NULL) { goto error; } - io_open = _PyImport_GetModuleAttrString("io", "open"); - if (io_open == NULL) { + state->io_open = _PyImport_GetModuleAttrString("io", "open"); + if (state->io_open == NULL) { goto error; } - _common_mod = PyImport_ImportModule("zoneinfo._common"); - if (_common_mod == NULL) { + state->_common_mod = PyImport_ImportModule("zoneinfo._common"); + if (state->_common_mod == NULL) { goto error; } - if (NO_TTINFO.utcoff == NULL) { - NO_TTINFO.utcoff = Py_NewRef(Py_None); - NO_TTINFO.dstoff = Py_NewRef(Py_None); - NO_TTINFO.tzname = Py_NewRef(Py_None); + if (state->NO_TTINFO.utcoff == NULL) { + state->NO_TTINFO.utcoff = Py_NewRef(Py_None); + state->NO_TTINFO.dstoff = Py_NewRef(Py_None); + state->NO_TTINFO.tzname = Py_NewRef(Py_None); } - if (initialize_caches()) { + if (initialize_caches(state)) { goto error; } @@ -2678,13 +2826,16 @@ static PyModuleDef_Slot zoneinfomodule_slots[] = { {Py_mod_exec, zoneinfomodule_exec}, {0, NULL}}; static struct PyModuleDef zoneinfomodule = { - PyModuleDef_HEAD_INIT, + .m_base = PyModuleDef_HEAD_INIT, .m_name = "_zoneinfo", .m_doc = "C implementation of the zoneinfo module", - .m_size = 0, + .m_size = sizeof(zoneinfo_state), .m_methods = module_methods, .m_slots = zoneinfomodule_slots, - .m_free = (freefunc)module_free}; + .m_traverse = module_traverse, + .m_clear = module_clear, + .m_free = module_free, +}; PyMODINIT_FUNC PyInit__zoneinfo(void) diff --git a/Modules/clinic/_zoneinfo.c.h b/Modules/clinic/_zoneinfo.c.h index 78fcbfa9411bb8..ae62865e0f67df 100644 --- a/Modules/clinic/_zoneinfo.c.h +++ b/Modules/clinic/_zoneinfo.c.h @@ -15,14 +15,14 @@ PyDoc_STRVAR(zoneinfo_ZoneInfo_from_file__doc__, "Create a ZoneInfo file from a file object."); #define ZONEINFO_ZONEINFO_FROM_FILE_METHODDEF \ - {"from_file", _PyCFunction_CAST(zoneinfo_ZoneInfo_from_file), METH_FASTCALL|METH_KEYWORDS|METH_CLASS, zoneinfo_ZoneInfo_from_file__doc__}, + {"from_file", _PyCFunction_CAST(zoneinfo_ZoneInfo_from_file), METH_METHOD|METH_FASTCALL|METH_KEYWORDS|METH_CLASS, zoneinfo_ZoneInfo_from_file__doc__}, static PyObject * -zoneinfo_ZoneInfo_from_file_impl(PyTypeObject *type, PyObject *file_obj, - PyObject *key); +zoneinfo_ZoneInfo_from_file_impl(PyTypeObject *type, PyTypeObject *cls, + PyObject *file_obj, PyObject *key); static PyObject * -zoneinfo_ZoneInfo_from_file(PyTypeObject *type, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +zoneinfo_ZoneInfo_from_file(PyTypeObject *type, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) @@ -65,7 +65,7 @@ zoneinfo_ZoneInfo_from_file(PyTypeObject *type, PyObject *const *args, Py_ssize_ } key = args[1]; skip_optional_pos: - return_value = zoneinfo_ZoneInfo_from_file_impl(type, file_obj, key); + return_value = zoneinfo_ZoneInfo_from_file_impl(type, cls, file_obj, key); exit: return return_value; @@ -78,13 +78,14 @@ PyDoc_STRVAR(zoneinfo_ZoneInfo_no_cache__doc__, "Get a new instance of ZoneInfo, bypassing the cache."); #define ZONEINFO_ZONEINFO_NO_CACHE_METHODDEF \ - {"no_cache", _PyCFunction_CAST(zoneinfo_ZoneInfo_no_cache), METH_FASTCALL|METH_KEYWORDS|METH_CLASS, zoneinfo_ZoneInfo_no_cache__doc__}, + {"no_cache", _PyCFunction_CAST(zoneinfo_ZoneInfo_no_cache), METH_METHOD|METH_FASTCALL|METH_KEYWORDS|METH_CLASS, zoneinfo_ZoneInfo_no_cache__doc__}, static PyObject * -zoneinfo_ZoneInfo_no_cache_impl(PyTypeObject *type, PyObject *key); +zoneinfo_ZoneInfo_no_cache_impl(PyTypeObject *type, PyTypeObject *cls, + PyObject *key); static PyObject * -zoneinfo_ZoneInfo_no_cache(PyTypeObject *type, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +zoneinfo_ZoneInfo_no_cache(PyTypeObject *type, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) @@ -120,7 +121,7 @@ zoneinfo_ZoneInfo_no_cache(PyTypeObject *type, PyObject *const *args, Py_ssize_t goto exit; } key = args[0]; - return_value = zoneinfo_ZoneInfo_no_cache_impl(type, key); + return_value = zoneinfo_ZoneInfo_no_cache_impl(type, cls, key); exit: return return_value; @@ -133,13 +134,14 @@ PyDoc_STRVAR(zoneinfo_ZoneInfo_clear_cache__doc__, "Clear the ZoneInfo cache."); #define ZONEINFO_ZONEINFO_CLEAR_CACHE_METHODDEF \ - {"clear_cache", _PyCFunction_CAST(zoneinfo_ZoneInfo_clear_cache), METH_FASTCALL|METH_KEYWORDS|METH_CLASS, zoneinfo_ZoneInfo_clear_cache__doc__}, + {"clear_cache", _PyCFunction_CAST(zoneinfo_ZoneInfo_clear_cache), METH_METHOD|METH_FASTCALL|METH_KEYWORDS|METH_CLASS, zoneinfo_ZoneInfo_clear_cache__doc__}, static PyObject * -zoneinfo_ZoneInfo_clear_cache_impl(PyTypeObject *type, PyObject *only_keys); +zoneinfo_ZoneInfo_clear_cache_impl(PyTypeObject *type, PyTypeObject *cls, + PyObject *only_keys); static PyObject * -zoneinfo_ZoneInfo_clear_cache(PyTypeObject *type, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +zoneinfo_ZoneInfo_clear_cache(PyTypeObject *type, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) @@ -180,9 +182,194 @@ zoneinfo_ZoneInfo_clear_cache(PyTypeObject *type, PyObject *const *args, Py_ssiz } only_keys = args[0]; skip_optional_kwonly: - return_value = zoneinfo_ZoneInfo_clear_cache_impl(type, only_keys); + return_value = zoneinfo_ZoneInfo_clear_cache_impl(type, cls, only_keys); exit: return return_value; } -/*[clinic end generated code: output=d2da73ef66146b83 input=a9049054013a1b77]*/ + +PyDoc_STRVAR(zoneinfo_ZoneInfo_utcoffset__doc__, +"utcoffset($self, dt, /)\n" +"--\n" +"\n" +"Retrieve a timedelta representing the UTC offset in a zone at the given datetime."); + +#define ZONEINFO_ZONEINFO_UTCOFFSET_METHODDEF \ + {"utcoffset", _PyCFunction_CAST(zoneinfo_ZoneInfo_utcoffset), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, zoneinfo_ZoneInfo_utcoffset__doc__}, + +static PyObject * +zoneinfo_ZoneInfo_utcoffset_impl(PyObject *self, PyTypeObject *cls, + PyObject *dt); + +static PyObject * +zoneinfo_ZoneInfo_utcoffset(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "utcoffset", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *dt; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + dt = args[0]; + return_value = zoneinfo_ZoneInfo_utcoffset_impl(self, cls, dt); + +exit: + return return_value; +} + +PyDoc_STRVAR(zoneinfo_ZoneInfo_dst__doc__, +"dst($self, dt, /)\n" +"--\n" +"\n" +"Retrieve a timedelta representing the amount of DST applied in a zone at the given datetime."); + +#define ZONEINFO_ZONEINFO_DST_METHODDEF \ + {"dst", _PyCFunction_CAST(zoneinfo_ZoneInfo_dst), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, zoneinfo_ZoneInfo_dst__doc__}, + +static PyObject * +zoneinfo_ZoneInfo_dst_impl(PyObject *self, PyTypeObject *cls, PyObject *dt); + +static PyObject * +zoneinfo_ZoneInfo_dst(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "dst", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *dt; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + dt = args[0]; + return_value = zoneinfo_ZoneInfo_dst_impl(self, cls, dt); + +exit: + return return_value; +} + +PyDoc_STRVAR(zoneinfo_ZoneInfo_tzname__doc__, +"tzname($self, dt, /)\n" +"--\n" +"\n" +"Retrieve a string containing the abbreviation for the time zone that applies in a zone at a given datetime."); + +#define ZONEINFO_ZONEINFO_TZNAME_METHODDEF \ + {"tzname", _PyCFunction_CAST(zoneinfo_ZoneInfo_tzname), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, zoneinfo_ZoneInfo_tzname__doc__}, + +static PyObject * +zoneinfo_ZoneInfo_tzname_impl(PyObject *self, PyTypeObject *cls, + PyObject *dt); + +static PyObject * +zoneinfo_ZoneInfo_tzname(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "tzname", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *dt; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + dt = args[0]; + return_value = zoneinfo_ZoneInfo_tzname_impl(self, cls, dt); + +exit: + return return_value; +} + +PyDoc_STRVAR(zoneinfo_ZoneInfo__unpickle__doc__, +"_unpickle($type, key, from_cache, /)\n" +"--\n" +"\n" +"Private method used in unpickling."); + +#define ZONEINFO_ZONEINFO__UNPICKLE_METHODDEF \ + {"_unpickle", _PyCFunction_CAST(zoneinfo_ZoneInfo__unpickle), METH_METHOD|METH_FASTCALL|METH_KEYWORDS|METH_CLASS, zoneinfo_ZoneInfo__unpickle__doc__}, + +static PyObject * +zoneinfo_ZoneInfo__unpickle_impl(PyTypeObject *type, PyTypeObject *cls, + PyObject *key, unsigned char from_cache); + +static PyObject * +zoneinfo_ZoneInfo__unpickle(PyTypeObject *type, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", "", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "_unpickle", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *key; + unsigned char from_cache; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + if (!args) { + goto exit; + } + key = args[0]; + { + unsigned long ival = PyLong_AsUnsignedLongMask(args[1]); + if (ival == (unsigned long)-1 && PyErr_Occurred()) { + goto exit; + } + else { + from_cache = (unsigned char) ival; + } + } + return_value = zoneinfo_ZoneInfo__unpickle_impl(type, cls, key, from_cache); + +exit: + return return_value; +} +/*[clinic end generated code: output=54051388dfc408af input=a9049054013a1b77]*/ diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index 52ea0b4901d4bb..6011b1604508af 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -403,7 +403,6 @@ Modules/_pickle.c - PicklerMemoProxyType - Modules/_pickle.c - Pickler_Type - Modules/_pickle.c - UnpicklerMemoProxyType - Modules/_pickle.c - Unpickler_Type - -Modules/_zoneinfo.c - PyZoneInfo_ZoneInfoType - Modules/ossaudiodev.c - OSSAudioType - Modules/ossaudiodev.c - OSSMixerType - Modules/socketmodule.c - sock_type - @@ -442,11 +441,6 @@ Modules/xxmodule.c - ErrorObject - Modules/_ctypes/callproc.c _ctypes_get_errobj error_object_name - Modules/_ctypes/_ctypes.c CreateSwappedType suffix - -## other - during module init -Modules/_zoneinfo.c - io_open - -Modules/_zoneinfo.c - _tzpath_find_tzfile - -Modules/_zoneinfo.c - _common_mod - - ##----------------------- ## other @@ -481,8 +475,6 @@ Modules/_tkinter.c - tcl_lock - Modules/_tkinter.c - excInCmd - Modules/_tkinter.c - valInCmd - Modules/_tkinter.c - trbInCmd - -Modules/_zoneinfo.c - TIMEDELTA_CACHE - -Modules/_zoneinfo.c - ZONEINFO_WEAK_CACHE - ################################## @@ -556,9 +548,6 @@ Modules/_tkinter.c - HeadFHCD - Modules/_tkinter.c - stdin_ready - Modules/_tkinter.c - event_tstate - Modules/_xxsubinterpretersmodule.c - _globals - -Modules/_zoneinfo.c - ZONEINFO_STRONG_CACHE - -Modules/_zoneinfo.c - ZONEINFO_STRONG_CACHE_MAX_SIZE - -Modules/_zoneinfo.c - NO_TTINFO - Modules/readline.c - completer_word_break_characters - Modules/readline.c - _history_length - Modules/readline.c - should_auto_add_history - From b2fc5492789623d656953d458f3eeaac03c1ef56 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 15 Feb 2023 15:32:31 -0700 Subject: [PATCH 102/247] gh-101758: Clean Up Uses of Import State (gh-101919) This change is almost entirely moving code around and hiding import state behind internal API. We introduce no changes to behavior, nor to non-internal API. (Since there was already going to be a lot of churn, I took this as an opportunity to re-organize import.c into topically-grouped sections of code.) The motivation is to simplify a number of upcoming changes. Specific changes: * move existing import-related code to import.c, wherever possible * add internal API for interacting with import state (both global and per-interpreter) * use only API outside of import.c (to limit churn there when changing the location, etc.) * consolidate the import-related state of PyInterpreterState into a single struct field (this changes layout slightly) * add macros for import state in import.c (to simplify changing the location) * group code in import.c into sections *remove _PyState_AddModule() https://github.com/python/cpython/issues/101758 --- Include/internal/pycore_import.h | 103 +- Include/internal/pycore_interp.h | 35 +- Include/internal/pycore_pylifecycle.h | 4 - Include/internal/pycore_pystate.h | 6 - Include/internal/pycore_runtime_init.h | 15 +- Include/internal/pycore_sysmodule.h | 3 + Lib/test/test_imp.py | 2 +- Lib/test/test_stable_abi_ctypes.py | 1 - Misc/stable_abi.toml | 3 - Objects/moduleobject.c | 25 +- PC/python3dll.c | 1 - Python/_warnings.c | 11 +- Python/ceval.c | 2 +- Python/dynload_shlib.c | 2 +- Python/import.c | 2210 +++++++++++++++--------- Python/importdl.c | 17 +- Python/pylifecycle.c | 127 +- Python/pystate.c | 151 +- Python/pythonrun.c | 10 +- Python/sysmodule.c | 40 +- 20 files changed, 1586 insertions(+), 1182 deletions(-) diff --git a/Include/internal/pycore_import.h b/Include/internal/pycore_import.h index 9036dff6725330..da766253ef6b9c 100644 --- a/Include/internal/pycore_import.h +++ b/Include/internal/pycore_import.h @@ -36,11 +36,112 @@ struct _import_runtime_state { const char * pkgcontext; }; +struct _import_state { + /* cached sys.modules dictionary */ + PyObject *modules; + /* This is the list of module objects for all legacy (single-phase init) + extension modules ever loaded in this process (i.e. imported + in this interpreter or in any other). Py_None stands in for + modules that haven't actually been imported in this interpreter. + + A module's index (PyModuleDef.m_base.m_index) is used to look up + the corresponding module object for this interpreter, if any. + (See PyState_FindModule().) When any extension module + is initialized during import, its moduledef gets initialized by + PyModuleDef_Init(), and the first time that happens for each + PyModuleDef, its index gets set to the current value of + a global counter (see _PyRuntimeState.imports.last_module_index). + The entry for that index in this interpreter remains unset until + the module is actually imported here. (Py_None is used as + a placeholder.) Note that multi-phase init modules always get + an index for which there will never be a module set. + + This is initialized lazily in PyState_AddModule(), which is also + where modules get added. */ + PyObject *modules_by_index; + /* importlib module._bootstrap */ + PyObject *importlib; + /* override for config->use_frozen_modules (for tests) + (-1: "off", 1: "on", 0: no override) */ + int override_frozen_modules; +#ifdef HAVE_DLOPEN + int dlopenflags; +#endif + PyObject *import_func; +}; + +#ifdef HAVE_DLOPEN +# include +# if HAVE_DECL_RTLD_NOW +# define _Py_DLOPEN_FLAGS RTLD_NOW +# else +# define _Py_DLOPEN_FLAGS RTLD_LAZY +# endif +# define DLOPENFLAGS_INIT .dlopenflags = _Py_DLOPEN_FLAGS, +#else +# define _Py_DLOPEN_FLAGS 0 +# define DLOPENFLAGS_INIT +#endif + +#define IMPORTS_INIT \ + { \ + .override_frozen_modules = 0, \ + DLOPENFLAGS_INIT \ + } + +extern void _PyImport_ClearCore(PyInterpreterState *interp); + +extern Py_ssize_t _PyImport_GetNextModuleIndex(void); +extern const char * _PyImport_ResolveNameWithPackageContext(const char *name); +extern const char * _PyImport_SwapPackageContext(const char *newcontext); + +extern int _PyImport_GetDLOpenFlags(PyInterpreterState *interp); +extern void _PyImport_SetDLOpenFlags(PyInterpreterState *interp, int new_val); + +extern PyObject * _PyImport_InitModules(PyInterpreterState *interp); +extern PyObject * _PyImport_GetModules(PyInterpreterState *interp); +extern void _PyImport_ClearModules(PyInterpreterState *interp); + +extern void _PyImport_ClearModulesByIndex(PyInterpreterState *interp); + +extern int _PyImport_InitDefaultImportFunc(PyInterpreterState *interp); +extern int _PyImport_IsDefaultImportFunc( + PyInterpreterState *interp, + PyObject *func); + +extern PyObject * _PyImport_GetImportlibLoader( + PyInterpreterState *interp, + const char *loader_name); +extern PyObject * _PyImport_GetImportlibExternalLoader( + PyInterpreterState *interp, + const char *loader_name); +extern PyObject * _PyImport_BlessMyLoader( + PyInterpreterState *interp, + PyObject *module_globals); +extern PyObject * _PyImport_ImportlibModuleRepr( + PyInterpreterState *interp, + PyObject *module); + + +extern PyStatus _PyImport_Init(void); +extern void _PyImport_Fini(void); +extern void _PyImport_Fini2(void); + +extern PyStatus _PyImport_InitCore( + PyThreadState *tstate, + PyObject *sysmod, + int importlib); +extern PyStatus _PyImport_InitExternal(PyThreadState *tstate); +extern void _PyImport_FiniCore(PyInterpreterState *interp); +extern void _PyImport_FiniExternal(PyInterpreterState *interp); + #ifdef HAVE_FORK extern PyStatus _PyImport_ReInitLock(void); #endif -extern PyObject* _PyImport_BootstrapImp(PyThreadState *tstate); + + +extern PyObject* _PyImport_GetBuiltinModuleNames(void); struct _module_alias { const char *name; /* ASCII encoded string */ diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index 0e3d46852f2e6d..60de31b336f613 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -21,6 +21,7 @@ extern "C" { #include "pycore_function.h" // FUNC_MAX_WATCHERS #include "pycore_genobject.h" // struct _Py_async_gen_state #include "pycore_gc.h" // struct _gc_runtime_state +#include "pycore_import.h" // struct _import_state #include "pycore_list.h" // struct _Py_list_state #include "pycore_global_objects.h" // struct _Py_interp_static_objects #include "pycore_tuple.h" // struct _Py_tuple_state @@ -92,37 +93,12 @@ struct _is { struct _ceval_state ceval; struct _gc_runtime_state gc; - // sys.modules dictionary - PyObject *modules; - /* This is the list of module objects for all legacy (single-phase init) - extension modules ever loaded in this process (i.e. imported - in this interpreter or in any other). Py_None stands in for - modules that haven't actually been imported in this interpreter. - - A module's index (PyModuleDef.m_base.m_index) is used to look up - the corresponding module object for this interpreter, if any. - (See PyState_FindModule().) When any extension module - is initialized during import, its moduledef gets initialized by - PyModuleDef_Init(), and the first time that happens for each - PyModuleDef, its index gets set to the current value of - a global counter (see _PyRuntimeState.imports.last_module_index). - The entry for that index in this interpreter remains unset until - the module is actually imported here. (Py_None is used as - a placeholder.) Note that multi-phase init modules always get - an index for which there will never be a module set. - - This is initialized lazily in _PyState_AddModule(), which is also - where modules get added. */ - PyObject *modules_by_index; + struct _import_state imports; + // Dictionary of the sys module PyObject *sysdict; // Dictionary of the builtins module PyObject *builtins; - // importlib module - PyObject *importlib; - // override for config->use_frozen_modules (for tests) - // (-1: "off", 1: "on", 0: no override) - int override_frozen_modules; PyObject *codec_search_path; PyObject *codec_search_cache; @@ -130,15 +106,11 @@ struct _is { int codecs_initialized; PyConfig config; -#ifdef HAVE_DLOPEN - int dlopenflags; -#endif unsigned long feature_flags; PyObject *dict; /* Stores per-interpreter state */ PyObject *builtins_copy; - PyObject *import_func; // Initialized to _PyEval_EvalFrameDefault(). _PyFrameEvalFunction eval_frame; @@ -205,7 +177,6 @@ struct _is { /* other API */ -extern void _PyInterpreterState_ClearModules(PyInterpreterState *interp); extern void _PyInterpreterState_Clear(PyThreadState *tstate); diff --git a/Include/internal/pycore_pylifecycle.h b/Include/internal/pycore_pylifecycle.h index 2d431befd74f99..e7a31807205254 100644 --- a/Include/internal/pycore_pylifecycle.h +++ b/Include/internal/pycore_pylifecycle.h @@ -30,7 +30,6 @@ PyAPI_FUNC(int) _Py_IsLocaleCoercionTarget(const char *ctype_loc); /* Various one-time initializers */ extern void _Py_InitVersion(void); -extern PyStatus _PyImport_Init(void); extern PyStatus _PyFaulthandler_Init(int enable); extern int _PyTraceMalloc_Init(int enable); extern PyObject * _PyBuiltin_Init(PyInterpreterState *interp); @@ -45,7 +44,6 @@ extern int _PyBuiltins_AddExceptions(PyObject * bltinmod); extern PyStatus _Py_HashRandomization_Init(const PyConfig *); extern PyStatus _PyTime_Init(void); -extern PyStatus _PyImportZip_Init(PyThreadState *tstate); extern PyStatus _PyGC_Init(PyInterpreterState *interp); extern PyStatus _PyAtExit_Init(PyInterpreterState *interp); extern int _Py_Deepfreeze_Init(void); @@ -55,8 +53,6 @@ extern int _Py_Deepfreeze_Init(void); extern int _PySignal_Init(int install_signal_handlers); extern void _PySignal_Fini(void); -extern void _PyImport_Fini(void); -extern void _PyImport_Fini2(void); extern void _PyGC_Fini(PyInterpreterState *interp); extern void _Py_HashRandomization_Fini(void); extern void _PyFaulthandler_Fini(void); diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h index 7046ec8d9adaaf..638b86253879ea 100644 --- a/Include/internal/pycore_pystate.h +++ b/Include/internal/pycore_pystate.h @@ -152,12 +152,6 @@ extern void _PySignal_AfterFork(void); #endif -PyAPI_FUNC(int) _PyState_AddModule( - PyThreadState *tstate, - PyObject* module, - PyModuleDef* def); - - PyAPI_FUNC(int) _PyOS_InterruptOccurred(PyThreadState *tstate); #define HEAD_LOCK(runtime) \ diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h index c6a27d076eae2d..a8d5953ff98b0b 100644 --- a/Include/internal/pycore_runtime_init.h +++ b/Include/internal/pycore_runtime_init.h @@ -97,23 +97,10 @@ extern "C" { ._main_interpreter = _PyInterpreterState_INIT, \ } -#ifdef HAVE_DLOPEN -# include -# if HAVE_DECL_RTLD_NOW -# define _Py_DLOPEN_FLAGS RTLD_NOW -# else -# define _Py_DLOPEN_FLAGS RTLD_LAZY -# endif -# define DLOPENFLAGS_INIT .dlopenflags = _Py_DLOPEN_FLAGS, -#else -# define _Py_DLOPEN_FLAGS 0 -# define DLOPENFLAGS_INIT -#endif - #define _PyInterpreterState_INIT \ { \ .id_refcount = -1, \ - DLOPENFLAGS_INIT \ + .imports = IMPORTS_INIT, \ .ceval = { \ .recursion_limit = Py_DEFAULT_RECURSION_LIMIT, \ }, \ diff --git a/Include/internal/pycore_sysmodule.h b/Include/internal/pycore_sysmodule.h index 10d092cdc30a2c..b4b1febafa4479 100644 --- a/Include/internal/pycore_sysmodule.h +++ b/Include/internal/pycore_sysmodule.h @@ -20,6 +20,9 @@ extern void _PySys_ClearAuditHooks(PyThreadState *tstate); PyAPI_FUNC(int) _PySys_SetAttr(PyObject *, PyObject *); +extern int _PySys_ClearAttrString(PyInterpreterState *interp, + const char *name, int verbose); + #ifdef __cplusplus } #endif diff --git a/Lib/test/test_imp.py b/Lib/test/test_imp.py index 31dce21587e2ca..c85ab92307de78 100644 --- a/Lib/test/test_imp.py +++ b/Lib/test/test_imp.py @@ -387,7 +387,7 @@ def check_with_reinit_reloaded(module, lookedup, initialized, check_basic_reloaded(mod, lookedup, initialized, init_count, before, reloaded) - # Currently _PyState_AddModule() always replaces the cached module. + # Currently PyState_AddModule() always replaces the cached module. self.assertIs(basic.look_up_self(), mod) self.assertEqual(basic.initialized_count(), expected_init_count) diff --git a/Lib/test/test_stable_abi_ctypes.py b/Lib/test/test_stable_abi_ctypes.py index e77c1c8409880d..7e50fbda2c07cb 100644 --- a/Lib/test/test_stable_abi_ctypes.py +++ b/Lib/test/test_stable_abi_ctypes.py @@ -864,7 +864,6 @@ def test_windows_feature_macros(self): "_PyObject_GC_Resize", "_PyObject_New", "_PyObject_NewVar", - "_PyState_AddModule", "_PyThreadState_Init", "_PyThreadState_Prealloc", "_PyWeakref_CallableProxyType", diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml index 21ff9616133445..c04a3a228caf56 100644 --- a/Misc/stable_abi.toml +++ b/Misc/stable_abi.toml @@ -1684,9 +1684,6 @@ [function._PyObject_NewVar] added = '3.2' abi_only = true -[function._PyState_AddModule] - added = '3.2' - abi_only = true [function._PyThreadState_Init] added = '3.2' abi_only = true diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index 24190e320ee6d6..a0be19a3ca8ac8 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -42,10 +42,9 @@ PyModuleDef_Init(PyModuleDef* def) { assert(PyModuleDef_Type.tp_flags & Py_TPFLAGS_READY); if (def->m_base.m_index == 0) { - _PyRuntime.imports.last_module_index++; Py_SET_REFCNT(def, 1); Py_SET_TYPE(def, &PyModuleDef_Type); - def->m_base.m_index = _PyRuntime.imports.last_module_index; + def->m_base.m_index = _PyImport_GetNextModuleIndex(); } return (PyObject*)def; } @@ -209,24 +208,7 @@ _PyModule_CreateInitialized(PyModuleDef* module, int module_api_version) "module %s: PyModule_Create is incompatible with m_slots", name); return NULL; } - /* Make sure name is fully qualified. - - This is a bit of a hack: when the shared library is loaded, - the module name is "package.module", but the module calls - PyModule_Create*() with just "module" for the name. The shared - library loader squirrels away the true name of the module in - _Py_PackageContext, and PyModule_Create*() will substitute this - (if the name actually matches). - */ -#define _Py_PackageContext (_PyRuntime.imports.pkgcontext) - if (_Py_PackageContext != NULL) { - const char *p = strrchr(_Py_PackageContext, '.'); - if (p != NULL && strcmp(module->m_name, p+1) == 0) { - name = _Py_PackageContext; - _Py_PackageContext = NULL; - } - } -#undef _Py_PackageContext + name = _PyImport_ResolveNameWithPackageContext(name); if ((m = (PyModuleObject*)PyModule_New(name)) == NULL) return NULL; @@ -710,8 +692,7 @@ static PyObject * module_repr(PyModuleObject *m) { PyInterpreterState *interp = _PyInterpreterState_GET(); - - return PyObject_CallMethod(interp->importlib, "_module_repr", "O", m); + return _PyImport_ImportlibModuleRepr(interp, (PyObject *)m); } /* Check if the "_initializing" attribute of the module spec is set to true. diff --git a/PC/python3dll.c b/PC/python3dll.c index e300819365756e..79f09037282f54 100755 --- a/PC/python3dll.c +++ b/PC/python3dll.c @@ -34,7 +34,6 @@ EXPORT_FUNC(_PyObject_GC_NewVar) EXPORT_FUNC(_PyObject_GC_Resize) EXPORT_FUNC(_PyObject_New) EXPORT_FUNC(_PyObject_NewVar) -EXPORT_FUNC(_PyState_AddModule) EXPORT_FUNC(_PyThreadState_Init) EXPORT_FUNC(_PyThreadState_Prealloc) EXPORT_FUNC(Py_AddPendingCall) diff --git a/Python/_warnings.c b/Python/_warnings.c index e78f21644f372b..d510381c365b66 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -214,7 +214,7 @@ get_warnings_attr(PyInterpreterState *interp, PyObject *attr, int try_import) gone, then we can't even use PyImport_GetModule without triggering an interpreter abort. */ - if (!interp->modules) { + if (!_PyImport_GetModules(interp)) { return NULL; } warnings_module = PyImport_GetModule(&_Py_ID(warnings)); @@ -1050,7 +1050,6 @@ warnings_warn_impl(PyObject *module, PyObject *message, PyObject *category, static PyObject * get_source_line(PyInterpreterState *interp, PyObject *module_globals, int lineno) { - PyObject *external; PyObject *loader; PyObject *module_name; PyObject *get_source; @@ -1059,13 +1058,7 @@ get_source_line(PyInterpreterState *interp, PyObject *module_globals, int lineno PyObject *source_line; /* stolen from import.c */ - external = PyObject_GetAttrString(interp->importlib, "_bootstrap_external"); - if (external == NULL) { - return NULL; - } - - loader = PyObject_CallMethod(external, "_bless_my_loader", "O", module_globals, NULL); - Py_DECREF(external); + loader = _PyImport_BlessMyLoader(interp, module_globals); if (loader == NULL) { return NULL; } diff --git a/Python/ceval.c b/Python/ceval.c index 611d62b0eba9af..09fd2f29266c87 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2688,7 +2688,7 @@ import_name(PyThreadState *tstate, _PyInterpreterFrame *frame, } PyObject *locals = frame->f_locals; /* Fast path for not overloaded __import__. */ - if (import_func == tstate->interp->import_func) { + if (_PyImport_IsDefaultImportFunc(tstate->interp, import_func)) { int ilevel = _PyLong_AsInt(level); if (ilevel == -1 && _PyErr_Occurred(tstate)) { return NULL; diff --git a/Python/dynload_shlib.c b/Python/dynload_shlib.c index 3c5fd83df584d5..6761bba457983b 100644 --- a/Python/dynload_shlib.c +++ b/Python/dynload_shlib.c @@ -75,7 +75,7 @@ _PyImport_FindSharedFuncptr(const char *prefix, return NULL; } - dlopenflags = _PyInterpreterState_GET()->dlopenflags; + dlopenflags = _PyImport_GetDLOpenFlags(_PyInterpreterState_GET()); handle = dlopen(pathname, dlopenflags); diff --git a/Python/import.c b/Python/import.c index 63ed2443657b29..ae27aaf56848d6 100644 --- a/Python/import.c +++ b/Python/import.c @@ -4,7 +4,7 @@ #include "pycore_import.h" // _PyImport_BootstrapImp() #include "pycore_initconfig.h" // _PyStatus_OK() -#include "pycore_interp.h" // _PyInterpreterState_ClearModules() +#include "pycore_interp.h" // struct _import_runtime_state #include "pycore_namespace.h" // _PyNamespace_Type #include "pycore_pyerrors.h" // _PyErr_SetString() #include "pycore_pyhash.h" // _Py_KeyedHash() @@ -24,8 +24,18 @@ extern "C" { #endif -/* Forward references */ -static PyObject *import_add_module(PyThreadState *tstate, PyObject *name); + +/*[clinic input] +module _imp +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=9c332475d8686284]*/ + +#include "clinic/import.c.h" + + +/*******************************/ +/* process-global import state */ +/*******************************/ /* This table is defined in config.c: */ extern struct _inittab _PyImport_Inittab[]; @@ -37,67 +47,51 @@ struct _inittab *PyImport_Inittab = _PyImport_Inittab; // we track the pointer here so we can deallocate it during finalization. static struct _inittab *inittab_copy = NULL; -/*[clinic input] -module _imp -[clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=9c332475d8686284]*/ - -#include "clinic/import.c.h" - -/* Initialize things */ -PyStatus -_PyImportZip_Init(PyThreadState *tstate) -{ - PyObject *path_hooks; - int err = 0; +/*******************************/ +/* runtime-global import state */ +/*******************************/ - path_hooks = PySys_GetObject("path_hooks"); - if (path_hooks == NULL) { - _PyErr_SetString(tstate, PyExc_RuntimeError, - "unable to get sys.path_hooks"); - goto error; - } +#define INITTAB _PyRuntime.imports.inittab +#define LAST_MODULE_INDEX _PyRuntime.imports.last_module_index +#define EXTENSIONS _PyRuntime.imports.extensions - int verbose = _PyInterpreterState_GetConfig(tstate->interp)->verbose; - if (verbose) { - PySys_WriteStderr("# installing zipimport hook\n"); - } +#define import_lock _PyRuntime.imports.lock.mutex +#define import_lock_thread _PyRuntime.imports.lock.thread +#define import_lock_level _PyRuntime.imports.lock.level - PyObject *zipimporter = _PyImport_GetModuleAttrString("zipimport", "zipimporter"); - if (zipimporter == NULL) { - _PyErr_Clear(tstate); /* No zipimporter object -- okay */ - if (verbose) { - PySys_WriteStderr("# can't import zipimport.zipimporter\n"); - } - } - else { - /* sys.path_hooks.insert(0, zipimporter) */ - err = PyList_Insert(path_hooks, 0, zipimporter); - Py_DECREF(zipimporter); - if (err < 0) { - goto error; - } - if (verbose) { - PySys_WriteStderr("# installed zipimport hook\n"); - } - } +#define FIND_AND_LOAD _PyRuntime.imports.find_and_load +#define PKGCONTEXT (_PyRuntime.imports.pkgcontext) + + +/*******************************/ +/* interpreter import state */ +/*******************************/ + +#define MODULES(interp) \ + (interp)->imports.modules +#define MODULES_BY_INDEX(interp) \ + (interp)->imports.modules_by_index +#define IMPORTLIB(interp) \ + (interp)->imports.importlib +#define OVERRIDE_FROZEN_MODULES(interp) \ + (interp)->imports.override_frozen_modules +#ifdef HAVE_DLOPEN +# define DLOPENFLAGS(interp) \ + (interp)->imports.dlopenflags +#endif +#define IMPORT_FUNC(interp) \ + (interp)->imports.import_func - return _PyStatus_OK(); - error: - PyErr_Print(); - return _PyStatus_ERR("initializing zipimport failed"); -} +/*******************/ +/* the import lock */ +/*******************/ /* Locking primitives to prevent parallel imports of the same module in different threads to return with a partially loaded module. These calls are serialized by the global interpreter lock. */ -#define import_lock _PyRuntime.imports.lock.mutex -#define import_lock_thread _PyRuntime.imports.lock.thread -#define import_lock_level _PyRuntime.imports.lock.level - void _PyImport_AcquireLock(void) { @@ -170,154 +164,45 @@ _PyImport_ReInitLock(void) } #endif -/*[clinic input] -_imp.lock_held - -Return True if the import lock is currently held, else False. - -On platforms without threads, return False. -[clinic start generated code]*/ - -static PyObject * -_imp_lock_held_impl(PyObject *module) -/*[clinic end generated code: output=8b89384b5e1963fc input=9b088f9b217d9bdf]*/ -{ - return PyBool_FromLong(import_lock_thread != PYTHREAD_INVALID_THREAD_ID); -} - -/*[clinic input] -_imp.acquire_lock - -Acquires the interpreter's import lock for the current thread. - -This lock should be used by import hooks to ensure thread-safety when importing -modules. On platforms without threads, this function does nothing. -[clinic start generated code]*/ - -static PyObject * -_imp_acquire_lock_impl(PyObject *module) -/*[clinic end generated code: output=1aff58cb0ee1b026 input=4a2d4381866d5fdc]*/ -{ - _PyImport_AcquireLock(); - Py_RETURN_NONE; -} - -/*[clinic input] -_imp.release_lock - -Release the interpreter's import lock. -On platforms without threads, this function does nothing. -[clinic start generated code]*/ +/***************/ +/* sys.modules */ +/***************/ -static PyObject * -_imp_release_lock_impl(PyObject *module) -/*[clinic end generated code: output=7faab6d0be178b0a input=934fb11516dd778b]*/ +PyObject * +_PyImport_InitModules(PyInterpreterState *interp) { - if (_PyImport_ReleaseLock() < 0) { - PyErr_SetString(PyExc_RuntimeError, - "not holding the import lock"); + assert(MODULES(interp) == NULL); + MODULES(interp) = PyDict_New(); + if (MODULES(interp) == NULL) { return NULL; } - Py_RETURN_NONE; -} - -PyStatus -_PyImport_Init(void) -{ - if (_PyRuntime.imports.inittab != NULL) { - return _PyStatus_ERR("global import state already initialized"); - } - PyStatus status = _PyStatus_OK(); - - size_t size; - for (size = 0; PyImport_Inittab[size].name != NULL; size++) - ; - size++; - - /* Force default raw memory allocator to get a known allocator to be able - to release the memory in _PyImport_Fini() */ - PyMemAllocatorEx old_alloc; - _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - - /* Make the copy. */ - struct _inittab *copied = PyMem_RawMalloc(size * sizeof(struct _inittab)); - if (copied == NULL) { - status = PyStatus_NoMemory(); - goto done; - } - memcpy(copied, PyImport_Inittab, size * sizeof(struct _inittab)); - _PyRuntime.imports.inittab = copied; - -done: - PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - return status; + return MODULES(interp); } -static inline void _extensions_cache_clear(void); - -void -_PyImport_Fini(void) +PyObject * +_PyImport_GetModules(PyInterpreterState *interp) { - _extensions_cache_clear(); - if (import_lock != NULL) { - PyThread_free_lock(import_lock); - import_lock = NULL; - } - - /* Use the same memory allocator as _PyImport_Init(). */ - PyMemAllocatorEx old_alloc; - _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - - /* Free memory allocated by _PyImport_Init() */ - struct _inittab *inittab = _PyRuntime.imports.inittab; - _PyRuntime.imports.inittab = NULL; - PyMem_RawFree(inittab); - - PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + return MODULES(interp); } void -_PyImport_Fini2(void) +_PyImport_ClearModules(PyInterpreterState *interp) { - /* Use the same memory allocator than PyImport_ExtendInittab(). */ - PyMemAllocatorEx old_alloc; - _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - - // Reset PyImport_Inittab - PyImport_Inittab = _PyImport_Inittab; - - /* Free memory allocated by PyImport_ExtendInittab() */ - PyMem_RawFree(inittab_copy); - inittab_copy = NULL; - - PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + Py_SETREF(MODULES(interp), NULL); } -/* Helper for sys */ - PyObject * PyImport_GetModuleDict(void) { PyInterpreterState *interp = _PyInterpreterState_GET(); - if (interp->modules == NULL) { + if (MODULES(interp) == NULL) { Py_FatalError("interpreter has no modules dictionary"); } - return interp->modules; -} - -/* In some corner cases it is important to be sure that the import - machinery has been initialized (or not cleaned up yet). For - example, see issue #4236 and PyModule_Create2(). */ - -int -_PyImport_IsInitialized(PyInterpreterState *interp) -{ - if (interp->modules == NULL) - return 0; - return 1; + return MODULES(interp); } +// This is only kept around for extensions that use _Py_IDENTIFIER. PyObject * _PyImport_GetModuleId(_Py_Identifier *nameid) { @@ -332,7 +217,7 @@ int _PyImport_SetModule(PyObject *name, PyObject *m) { PyInterpreterState *interp = _PyInterpreterState_GET(); - PyObject *modules = interp->modules; + PyObject *modules = MODULES(interp); return PyObject_SetItem(modules, name, m); } @@ -340,14 +225,14 @@ int _PyImport_SetModuleString(const char *name, PyObject *m) { PyInterpreterState *interp = _PyInterpreterState_GET(); - PyObject *modules = interp->modules; + PyObject *modules = MODULES(interp); return PyMapping_SetItemString(modules, name, m); } static PyObject * import_get_module(PyThreadState *tstate, PyObject *name) { - PyObject *modules = tstate->interp->modules; + PyObject *modules = MODULES(tstate->interp); if (modules == NULL) { _PyErr_SetString(tstate, PyExc_RuntimeError, "unable to get sys.modules"); @@ -370,7 +255,6 @@ import_get_module(PyThreadState *tstate, PyObject *name) return m; } - static int import_ensure_initialized(PyInterpreterState *interp, PyObject *mod, PyObject *name) { @@ -387,7 +271,7 @@ import_ensure_initialized(PyInterpreterState *interp, PyObject *mod, PyObject *n if (busy) { /* Wait until module is done importing. */ PyObject *value = _PyObject_CallMethodOneArg( - interp->importlib, &_Py_ID(_lock_unlock_module), name); + IMPORTLIB(interp), &_Py_ID(_lock_unlock_module), name); if (value == NULL) { return -1; } @@ -396,47 +280,381 @@ import_ensure_initialized(PyInterpreterState *interp, PyObject *mod, PyObject *n return 0; } +static void remove_importlib_frames(PyThreadState *tstate); -/* Helper for pythonrun.c -- return magic number and tag. */ - -long -PyImport_GetMagicNumber(void) +PyObject * +PyImport_GetModule(PyObject *name) { - long res; - PyInterpreterState *interp = _PyInterpreterState_GET(); - PyObject *external, *pyc_magic; + PyThreadState *tstate = _PyThreadState_GET(); + PyObject *mod; - external = PyObject_GetAttrString(interp->importlib, "_bootstrap_external"); - if (external == NULL) - return -1; - pyc_magic = PyObject_GetAttrString(external, "_RAW_MAGIC_NUMBER"); - Py_DECREF(external); - if (pyc_magic == NULL) - return -1; - res = PyLong_AsLong(pyc_magic); - Py_DECREF(pyc_magic); - return res; + mod = import_get_module(tstate, name); + if (mod != NULL && mod != Py_None) { + if (import_ensure_initialized(tstate->interp, mod, name) < 0) { + Py_DECREF(mod); + remove_importlib_frames(tstate); + return NULL; + } + } + return mod; } +/* Get the module object corresponding to a module name. + First check the modules dictionary if there's one there, + if not, create a new one and insert it in the modules dictionary. */ -extern const char * _PySys_ImplCacheTag; - -const char * -PyImport_GetMagicTag(void) +static PyObject * +import_add_module(PyThreadState *tstate, PyObject *name) { - return _PySys_ImplCacheTag; -} + PyObject *modules = MODULES(tstate->interp); + if (modules == NULL) { + _PyErr_SetString(tstate, PyExc_RuntimeError, + "no import module dictionary"); + return NULL; + } + PyObject *m; + if (PyDict_CheckExact(modules)) { + m = Py_XNewRef(PyDict_GetItemWithError(modules, name)); + } + else { + m = PyObject_GetItem(modules, name); + // For backward-compatibility we copy the behavior + // of PyDict_GetItemWithError(). + if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { + _PyErr_Clear(tstate); + } + } + if (_PyErr_Occurred(tstate)) { + return NULL; + } + if (m != NULL && PyModule_Check(m)) { + return m; + } + Py_XDECREF(m); + m = PyModule_NewObject(name); + if (m == NULL) + return NULL; + if (PyObject_SetItem(modules, name, m) != 0) { + Py_DECREF(m); + return NULL; + } -/* -We support a number of kinds of single-phase init builtin/extension modules: + return m; +} -* "basic" - * no module state (PyModuleDef.m_size == -1) - * does not support repeated init (we use PyModuleDef.m_base.m_copy) - * may have process-global state - * the module's def is cached in _PyRuntime.imports.extensions, - by (name, filename) +PyObject * +PyImport_AddModuleObject(PyObject *name) +{ + PyThreadState *tstate = _PyThreadState_GET(); + PyObject *mod = import_add_module(tstate, name); + if (mod) { + PyObject *ref = PyWeakref_NewRef(mod, NULL); + Py_DECREF(mod); + if (ref == NULL) { + return NULL; + } + mod = PyWeakref_GetObject(ref); + Py_DECREF(ref); + } + return mod; /* borrowed reference */ +} + + +PyObject * +PyImport_AddModule(const char *name) +{ + PyObject *nameobj = PyUnicode_FromString(name); + if (nameobj == NULL) { + return NULL; + } + PyObject *module = PyImport_AddModuleObject(nameobj); + Py_DECREF(nameobj); + return module; +} + + +/* Remove name from sys.modules, if it's there. + * Can be called with an exception raised. + * If fail to remove name a new exception will be chained with the old + * exception, otherwise the old exception is preserved. + */ +static void +remove_module(PyThreadState *tstate, PyObject *name) +{ + PyObject *type, *value, *traceback; + _PyErr_Fetch(tstate, &type, &value, &traceback); + + PyObject *modules = MODULES(tstate->interp); + if (PyDict_CheckExact(modules)) { + PyObject *mod = _PyDict_Pop(modules, name, Py_None); + Py_XDECREF(mod); + } + else if (PyMapping_DelItem(modules, name) < 0) { + if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { + _PyErr_Clear(tstate); + } + } + + _PyErr_ChainExceptions(type, value, traceback); +} + + +/************************************/ +/* per-interpreter modules-by-index */ +/************************************/ + +Py_ssize_t +_PyImport_GetNextModuleIndex(void) +{ + LAST_MODULE_INDEX++; + return LAST_MODULE_INDEX; +} + +static const char * +_modules_by_index_check(PyInterpreterState *interp, Py_ssize_t index) +{ + if (index == 0) { + return "invalid module index"; + } + if (MODULES_BY_INDEX(interp) == NULL) { + return "Interpreters module-list not accessible."; + } + if (index > PyList_GET_SIZE(MODULES_BY_INDEX(interp))) { + return "Module index out of bounds."; + } + return NULL; +} + +static PyObject * +_modules_by_index_get(PyInterpreterState *interp, PyModuleDef *def) +{ + Py_ssize_t index = def->m_base.m_index; + if (_modules_by_index_check(interp, index) != NULL) { + return NULL; + } + PyObject *res = PyList_GET_ITEM(MODULES_BY_INDEX(interp), index); + return res==Py_None ? NULL : res; +} + +static int +_modules_by_index_set(PyInterpreterState *interp, + PyModuleDef *def, PyObject *module) +{ + assert(def != NULL); + assert(def->m_slots == NULL); + assert(def->m_base.m_index > 0); + + if (MODULES_BY_INDEX(interp) == NULL) { + MODULES_BY_INDEX(interp) = PyList_New(0); + if (MODULES_BY_INDEX(interp) == NULL) { + return -1; + } + } + + Py_ssize_t index = def->m_base.m_index; + while (PyList_GET_SIZE(MODULES_BY_INDEX(interp)) <= index) { + if (PyList_Append(MODULES_BY_INDEX(interp), Py_None) < 0) { + return -1; + } + } + + return PyList_SetItem(MODULES_BY_INDEX(interp), index, Py_NewRef(module)); +} + +static int +_modules_by_index_clear(PyInterpreterState *interp, PyModuleDef *def) +{ + Py_ssize_t index = def->m_base.m_index; + const char *err = _modules_by_index_check(interp, index); + if (err != NULL) { + Py_FatalError(err); + return -1; + } + return PyList_SetItem(MODULES_BY_INDEX(interp), index, Py_NewRef(Py_None)); +} + + +PyObject* +PyState_FindModule(PyModuleDef* module) +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); + if (module->m_slots) { + return NULL; + } + return _modules_by_index_get(interp, module); +} + +int +PyState_AddModule(PyObject* module, PyModuleDef* def) +{ + if (!def) { + Py_FatalError("module definition is NULL"); + return -1; + } + + PyThreadState *tstate = _PyThreadState_GET(); + if (def->m_slots) { + _PyErr_SetString(tstate, + PyExc_SystemError, + "PyState_AddModule called on module with slots"); + return -1; + } + + PyInterpreterState *interp = tstate->interp; + Py_ssize_t index = def->m_base.m_index; + if (MODULES_BY_INDEX(interp) && + index < PyList_GET_SIZE(MODULES_BY_INDEX(interp)) && + module == PyList_GET_ITEM(MODULES_BY_INDEX(interp), index)) + { + _Py_FatalErrorFormat(__func__, "module %p already added", module); + return -1; + } + + return _modules_by_index_set(interp, def, module); +} + +int +PyState_RemoveModule(PyModuleDef* def) +{ + PyThreadState *tstate = _PyThreadState_GET(); + if (def->m_slots) { + _PyErr_SetString(tstate, + PyExc_SystemError, + "PyState_RemoveModule called on module with slots"); + return -1; + } + return _modules_by_index_clear(tstate->interp, def); +} + + +// Used by finalize_modules() +void +_PyImport_ClearModulesByIndex(PyInterpreterState *interp) +{ + if (!MODULES_BY_INDEX(interp)) { + return; + } + + Py_ssize_t i; + for (i = 0; i < PyList_GET_SIZE(MODULES_BY_INDEX(interp)); i++) { + PyObject *m = PyList_GET_ITEM(MODULES_BY_INDEX(interp), i); + if (PyModule_Check(m)) { + /* cleanup the saved copy of module dicts */ + PyModuleDef *md = PyModule_GetDef(m); + if (md) { + Py_CLEAR(md->m_base.m_copy); + } + } + } + + /* Setting modules_by_index to NULL could be dangerous, so we + clear the list instead. */ + if (PyList_SetSlice(MODULES_BY_INDEX(interp), + 0, PyList_GET_SIZE(MODULES_BY_INDEX(interp)), + NULL)) { + PyErr_WriteUnraisable(MODULES_BY_INDEX(interp)); + } +} + + +/*********************/ +/* extension modules */ +/*********************/ + +/* Make sure name is fully qualified. + + This is a bit of a hack: when the shared library is loaded, + the module name is "package.module", but the module calls + PyModule_Create*() with just "module" for the name. The shared + library loader squirrels away the true name of the module in + _PyRuntime.imports.pkgcontext, and PyModule_Create*() will + substitute this (if the name actually matches). +*/ +const char * +_PyImport_ResolveNameWithPackageContext(const char *name) +{ + if (PKGCONTEXT != NULL) { + const char *p = strrchr(PKGCONTEXT, '.'); + if (p != NULL && strcmp(name, p+1) == 0) { + name = PKGCONTEXT; + PKGCONTEXT = NULL; + } + } + return name; +} + +const char * +_PyImport_SwapPackageContext(const char *newcontext) +{ + const char *oldcontext = PKGCONTEXT; + PKGCONTEXT = newcontext; + return oldcontext; +} + +#ifdef HAVE_DLOPEN +int +_PyImport_GetDLOpenFlags(PyInterpreterState *interp) +{ + return DLOPENFLAGS(interp); +} + +void +_PyImport_SetDLOpenFlags(PyInterpreterState *interp, int new_val) +{ + DLOPENFLAGS(interp) = new_val; +} +#endif // HAVE_DLOPEN + + +/* Common implementation for _imp.exec_dynamic and _imp.exec_builtin */ +static int +exec_builtin_or_dynamic(PyObject *mod) { + PyModuleDef *def; + void *state; + + if (!PyModule_Check(mod)) { + return 0; + } + + def = PyModule_GetDef(mod); + if (def == NULL) { + return 0; + } + + state = PyModule_GetState(mod); + if (state) { + /* Already initialized; skip reload */ + return 0; + } + + return PyModule_ExecDef(mod, def); +} + + +/*******************/ + +#if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) +#include +EM_JS(PyObject*, _PyImport_InitFunc_TrampolineCall, (PyModInitFunction func), { + return wasmTable.get(func)(); +}); +#endif // __EMSCRIPTEN__ && PY_CALL_TRAMPOLINE + + +/*****************************/ +/* single-phase init modules */ +/*****************************/ + +/* +We support a number of kinds of single-phase init builtin/extension modules: + +* "basic" + * no module state (PyModuleDef.m_size == -1) + * does not support repeated init (we use PyModuleDef.m_base.m_copy) + * may have process-global state + * the module's def is cached in _PyRuntime.imports.extensions, + by (name, filename) * "reinit" * no module state (PyModuleDef.m_size == 0) * supports repeated init (m_copy is never used) @@ -512,7 +730,7 @@ gets even messier. static PyModuleDef * _extensions_cache_get(PyObject *filename, PyObject *name) { - PyObject *extensions = _PyRuntime.imports.extensions; + PyObject *extensions = EXTENSIONS; if (extensions == NULL) { return NULL; } @@ -528,13 +746,13 @@ _extensions_cache_get(PyObject *filename, PyObject *name) static int _extensions_cache_set(PyObject *filename, PyObject *name, PyModuleDef *def) { - PyObject *extensions = _PyRuntime.imports.extensions; + PyObject *extensions = EXTENSIONS; if (extensions == NULL) { extensions = PyDict_New(); if (extensions == NULL) { return -1; } - _PyRuntime.imports.extensions = extensions; + EXTENSIONS = extensions; } PyObject *key = PyTuple_Pack(2, filename, name); if (key == NULL) { @@ -551,7 +769,7 @@ _extensions_cache_set(PyObject *filename, PyObject *name, PyModuleDef *def) static void _extensions_cache_clear(void) { - Py_CLEAR(_PyRuntime.imports.extensions); + Py_CLEAR(EXTENSIONS); } static int @@ -569,7 +787,7 @@ fix_up_extension(PyObject *mod, PyObject *name, PyObject *filename) } PyThreadState *tstate = _PyThreadState_GET(); - if (_PyState_AddModule(tstate, mod, def) < 0) { + if (_modules_by_index_set(tstate->interp, def, mod) < 0) { return -1; } @@ -616,40 +834,19 @@ _PyImport_FixupExtensionObject(PyObject *mod, PyObject *name, return 0; } -int -_PyImport_FixupBuiltin(PyObject *mod, const char *name, PyObject *modules) -{ - int res = -1; - PyObject *nameobj; - nameobj = PyUnicode_InternFromString(name); - if (nameobj == NULL) { - return -1; - } - if (PyObject_SetItem(modules, nameobj, mod) < 0) { - goto finally; - } - if (fix_up_extension(mod, nameobj, nameobj) < 0) { - PyMapping_DelItem(modules, nameobj); - goto finally; - } - res = 0; - -finally: - Py_DECREF(nameobj); - return res; -} static PyObject * import_find_extension(PyThreadState *tstate, PyObject *name, PyObject *filename) { + /* Only single-phase init modules will be in the cache. */ PyModuleDef *def = _extensions_cache_get(filename, name); if (def == NULL) { return NULL; } PyObject *mod, *mdict; - PyObject *modules = tstate->interp->modules; + PyObject *modules = MODULES(tstate->interp); if (def->m_size == -1) { /* Module does not support repeated initialization */ @@ -679,7 +876,7 @@ import_find_extension(PyThreadState *tstate, PyObject *name, return NULL; } } - if (_PyState_AddModule(tstate, mod, def) < 0) { + if (_modules_by_index_set(tstate->interp, def, mod) < 0) { PyMapping_DelItem(modules, name); Py_DECREF(mod); return NULL; @@ -694,107 +891,268 @@ import_find_extension(PyThreadState *tstate, PyObject *name, } -/* Get the module object corresponding to a module name. - First check the modules dictionary if there's one there, - if not, create a new one and insert it in the modules dictionary. */ +/*******************/ +/* builtin modules */ +/*******************/ -static PyObject * -import_add_module(PyThreadState *tstate, PyObject *name) +int +_PyImport_FixupBuiltin(PyObject *mod, const char *name, PyObject *modules) { - PyObject *modules = tstate->interp->modules; - if (modules == NULL) { - _PyErr_SetString(tstate, PyExc_RuntimeError, - "no import module dictionary"); - return NULL; + int res = -1; + PyObject *nameobj; + nameobj = PyUnicode_InternFromString(name); + if (nameobj == NULL) { + return -1; } - - PyObject *m; - if (PyDict_CheckExact(modules)) { - m = Py_XNewRef(PyDict_GetItemWithError(modules, name)); + if (PyObject_SetItem(modules, nameobj, mod) < 0) { + goto finally; } - else { - m = PyObject_GetItem(modules, name); - // For backward-compatibility we copy the behavior - // of PyDict_GetItemWithError(). - if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { - _PyErr_Clear(tstate); - } + if (fix_up_extension(mod, nameobj, nameobj) < 0) { + PyMapping_DelItem(modules, nameobj); + goto finally; } - if (_PyErr_Occurred(tstate)) { - return NULL; + res = 0; + +finally: + Py_DECREF(nameobj); + return res; +} + +/* Helper to test for built-in module */ + +static int +is_builtin(PyObject *name) +{ + int i; + struct _inittab *inittab = INITTAB; + for (i = 0; inittab[i].name != NULL; i++) { + if (_PyUnicode_EqualToASCIIString(name, inittab[i].name)) { + if (inittab[i].initfunc == NULL) + return -1; + else + return 1; + } } - if (m != NULL && PyModule_Check(m)) { - return m; + return 0; +} + +static PyObject* +create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec) +{ + PyObject *mod = import_find_extension(tstate, name, name); + if (mod || _PyErr_Occurred(tstate)) { + return mod; } - Py_XDECREF(m); - m = PyModule_NewObject(name); - if (m == NULL) - return NULL; - if (PyObject_SetItem(modules, name, m) != 0) { - Py_DECREF(m); - return NULL; + + PyObject *modules = MODULES(tstate->interp); + for (struct _inittab *p = INITTAB; p->name != NULL; p++) { + if (_PyUnicode_EqualToASCIIString(name, p->name)) { + if (p->initfunc == NULL) { + /* Cannot re-init internal module ("sys" or "builtins") */ + mod = PyImport_AddModuleObject(name); + return Py_XNewRef(mod); + } + mod = _PyImport_InitFunc_TrampolineCall(*p->initfunc); + if (mod == NULL) { + return NULL; + } + + if (PyObject_TypeCheck(mod, &PyModuleDef_Type)) { + return PyModule_FromDefAndSpec((PyModuleDef*)mod, spec); + } + else { + /* Remember pointer to module init function. */ + PyModuleDef *def = PyModule_GetDef(mod); + if (def == NULL) { + return NULL; + } + + def->m_base.m_init = p->initfunc; + if (_PyImport_FixupExtensionObject(mod, name, name, + modules) < 0) { + return NULL; + } + return mod; + } + } } - return m; + // not found + Py_RETURN_NONE; } -PyObject * -PyImport_AddModuleObject(PyObject *name) + +/*****************************/ +/* the builtin modules table */ +/*****************************/ + +/* API for embedding applications that want to add their own entries + to the table of built-in modules. This should normally be called + *before* Py_Initialize(). When the table resize fails, -1 is + returned and the existing table is unchanged. + + After a similar function by Just van Rossum. */ + +int +PyImport_ExtendInittab(struct _inittab *newtab) { - PyThreadState *tstate = _PyThreadState_GET(); - PyObject *mod = import_add_module(tstate, name); - if (mod) { - PyObject *ref = PyWeakref_NewRef(mod, NULL); - Py_DECREF(mod); - if (ref == NULL) { - return NULL; - } - mod = PyWeakref_GetObject(ref); - Py_DECREF(ref); + struct _inittab *p; + size_t i, n; + int res = 0; + + if (INITTAB != NULL) { + Py_FatalError("PyImport_ExtendInittab() may not be called after Py_Initialize()"); } - return mod; /* borrowed reference */ + + /* Count the number of entries in both tables */ + for (n = 0; newtab[n].name != NULL; n++) + ; + if (n == 0) + return 0; /* Nothing to do */ + for (i = 0; PyImport_Inittab[i].name != NULL; i++) + ; + + /* Force default raw memory allocator to get a known allocator to be able + to release the memory in _PyImport_Fini2() */ + PyMemAllocatorEx old_alloc; + _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + + /* Allocate new memory for the combined table */ + p = NULL; + if (i + n <= SIZE_MAX / sizeof(struct _inittab) - 1) { + size_t size = sizeof(struct _inittab) * (i + n + 1); + p = PyMem_RawRealloc(inittab_copy, size); + } + if (p == NULL) { + res = -1; + goto done; + } + + /* Copy the tables into the new memory at the first call + to PyImport_ExtendInittab(). */ + if (inittab_copy != PyImport_Inittab) { + memcpy(p, PyImport_Inittab, (i+1) * sizeof(struct _inittab)); + } + memcpy(p + i, newtab, (n + 1) * sizeof(struct _inittab)); + PyImport_Inittab = inittab_copy = p; + +done: + PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + return res; } +/* Shorthand to add a single entry given a name and a function */ -PyObject * -PyImport_AddModule(const char *name) +int +PyImport_AppendInittab(const char *name, PyObject* (*initfunc)(void)) { - PyObject *nameobj = PyUnicode_FromString(name); - if (nameobj == NULL) { - return NULL; + struct _inittab newtab[2]; + + if (INITTAB != NULL) { + Py_FatalError("PyImport_AppendInittab() may not be called after Py_Initialize()"); } - PyObject *module = PyImport_AddModuleObject(nameobj); - Py_DECREF(nameobj); - return module; + + memset(newtab, '\0', sizeof newtab); + + newtab[0].name = name; + newtab[0].initfunc = initfunc; + + return PyImport_ExtendInittab(newtab); } -/* Remove name from sys.modules, if it's there. - * Can be called with an exception raised. - * If fail to remove name a new exception will be chained with the old - * exception, otherwise the old exception is preserved. - */ +/* the internal table */ + +static int +init_builtin_modules_table(void) +{ + size_t size; + for (size = 0; PyImport_Inittab[size].name != NULL; size++) + ; + size++; + + /* Make the copy. */ + struct _inittab *copied = PyMem_RawMalloc(size * sizeof(struct _inittab)); + if (copied == NULL) { + return -1; + } + memcpy(copied, PyImport_Inittab, size * sizeof(struct _inittab)); + INITTAB = copied; + return 0; +} + static void -remove_module(PyThreadState *tstate, PyObject *name) +fini_builtin_modules_table(void) { - PyObject *type, *value, *traceback; - _PyErr_Fetch(tstate, &type, &value, &traceback); + struct _inittab *inittab = INITTAB; + INITTAB = NULL; + PyMem_RawFree(inittab); +} - PyObject *modules = tstate->interp->modules; - if (PyDict_CheckExact(modules)) { - PyObject *mod = _PyDict_Pop(modules, name, Py_None); - Py_XDECREF(mod); +PyObject * +_PyImport_GetBuiltinModuleNames(void) +{ + PyObject *list = PyList_New(0); + if (list == NULL) { + return NULL; } - else if (PyMapping_DelItem(modules, name) < 0) { - if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { - _PyErr_Clear(tstate); + struct _inittab *inittab = INITTAB; + for (Py_ssize_t i = 0; inittab[i].name != NULL; i++) { + PyObject *name = PyUnicode_FromString(inittab[i].name); + if (name == NULL) { + Py_DECREF(list); + return NULL; } + if (PyList_Append(list, name) < 0) { + Py_DECREF(name); + Py_DECREF(list); + return NULL; + } + Py_DECREF(name); } + return list; +} - _PyErr_ChainExceptions(type, value, traceback); + +/********************/ +/* the magic number */ +/********************/ + +/* Helper for pythonrun.c -- return magic number and tag. */ + +long +PyImport_GetMagicNumber(void) +{ + long res; + PyInterpreterState *interp = _PyInterpreterState_GET(); + PyObject *external, *pyc_magic; + + external = PyObject_GetAttrString(IMPORTLIB(interp), "_bootstrap_external"); + if (external == NULL) + return -1; + pyc_magic = PyObject_GetAttrString(external, "_RAW_MAGIC_NUMBER"); + Py_DECREF(external); + if (pyc_magic == NULL) + return -1; + res = PyLong_AsLong(pyc_magic); + Py_DECREF(pyc_magic); + return res; +} + + +extern const char * _PySys_ImplCacheTag; + +const char * +PyImport_GetMagicTag(void) +{ + return _PySys_ImplCacheTag; } +/*********************************/ +/* a Python module's code object */ +/*********************************/ + /* Execute a code object in a module and return the module object * WITH INCREMENTED REFERENCE COUNT. If an error occurs, name is * removed from sys.modules, to avoid leaving damaged module objects @@ -851,7 +1209,7 @@ PyImport_ExecCodeModuleWithPathnames(const char *name, PyObject *co, Py_FatalError("no current interpreter"); } - external= PyObject_GetAttrString(interp->importlib, + external= PyObject_GetAttrString(IMPORTLIB(interp), "_bootstrap_external"); if (external != NULL) { pathobj = _PyObject_CallMethodOneArg( @@ -936,7 +1294,7 @@ PyImport_ExecCodeModuleObject(PyObject *name, PyObject *co, PyObject *pathname, if (pathname == NULL) { pathname = ((PyCodeObject *)co)->co_filename; } - external = PyObject_GetAttrString(tstate->interp->importlib, + external = PyObject_GetAttrString(IMPORTLIB(tstate->interp), "_bootstrap_external"); if (external == NULL) { Py_DECREF(d); @@ -989,204 +1347,10 @@ update_compiled_module(PyCodeObject *co, PyObject *newname) Py_DECREF(oldname); } -/*[clinic input] -_imp._fix_co_filename - - code: object(type="PyCodeObject *", subclass_of="&PyCode_Type") - Code object to change. - - path: unicode - File path to use. - / - -Changes code.co_filename to specify the passed-in file path. -[clinic start generated code]*/ - -static PyObject * -_imp__fix_co_filename_impl(PyObject *module, PyCodeObject *code, - PyObject *path) -/*[clinic end generated code: output=1d002f100235587d input=895ba50e78b82f05]*/ - -{ - update_compiled_module(code, path); - - Py_RETURN_NONE; -} - - -/* Helper to test for built-in module */ - -static int -is_builtin(PyObject *name) -{ - int i; - struct _inittab *inittab = _PyRuntime.imports.inittab; - for (i = 0; inittab[i].name != NULL; i++) { - if (_PyUnicode_EqualToASCIIString(name, inittab[i].name)) { - if (inittab[i].initfunc == NULL) - return -1; - else - return 1; - } - } - return 0; -} - - -/* Return a finder object for a sys.path/pkg.__path__ item 'p', - possibly by fetching it from the path_importer_cache dict. If it - wasn't yet cached, traverse path_hooks until a hook is found - that can handle the path item. Return None if no hook could; - this tells our caller that the path based finder could not find - a finder for this path item. Cache the result in - path_importer_cache. */ - -static PyObject * -get_path_importer(PyThreadState *tstate, PyObject *path_importer_cache, - PyObject *path_hooks, PyObject *p) -{ - PyObject *importer; - Py_ssize_t j, nhooks; - - /* These conditions are the caller's responsibility: */ - assert(PyList_Check(path_hooks)); - assert(PyDict_Check(path_importer_cache)); - - nhooks = PyList_Size(path_hooks); - if (nhooks < 0) - return NULL; /* Shouldn't happen */ - - importer = PyDict_GetItemWithError(path_importer_cache, p); - if (importer != NULL || _PyErr_Occurred(tstate)) { - return Py_XNewRef(importer); - } - - /* set path_importer_cache[p] to None to avoid recursion */ - if (PyDict_SetItem(path_importer_cache, p, Py_None) != 0) - return NULL; - - for (j = 0; j < nhooks; j++) { - PyObject *hook = PyList_GetItem(path_hooks, j); - if (hook == NULL) - return NULL; - importer = PyObject_CallOneArg(hook, p); - if (importer != NULL) - break; - - if (!_PyErr_ExceptionMatches(tstate, PyExc_ImportError)) { - return NULL; - } - _PyErr_Clear(tstate); - } - if (importer == NULL) { - Py_RETURN_NONE; - } - if (PyDict_SetItem(path_importer_cache, p, importer) < 0) { - Py_DECREF(importer); - return NULL; - } - return importer; -} - -PyObject * -PyImport_GetImporter(PyObject *path) -{ - PyThreadState *tstate = _PyThreadState_GET(); - PyObject *path_importer_cache = PySys_GetObject("path_importer_cache"); - PyObject *path_hooks = PySys_GetObject("path_hooks"); - if (path_importer_cache == NULL || path_hooks == NULL) { - return NULL; - } - return get_path_importer(tstate, path_importer_cache, path_hooks, path); -} - -#if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) -#include -EM_JS(PyObject*, _PyImport_InitFunc_TrampolineCall, (PyModInitFunction func), { - return wasmTable.get(func)(); -}); -#endif // __EMSCRIPTEN__ && PY_CALL_TRAMPOLINE - -static PyObject* -create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec) -{ - PyObject *mod = import_find_extension(tstate, name, name); - if (mod || _PyErr_Occurred(tstate)) { - return mod; - } - - PyObject *modules = tstate->interp->modules; - for (struct _inittab *p = _PyRuntime.imports.inittab; p->name != NULL; p++) { - if (_PyUnicode_EqualToASCIIString(name, p->name)) { - if (p->initfunc == NULL) { - /* Cannot re-init internal module ("sys" or "builtins") */ - mod = PyImport_AddModuleObject(name); - return Py_XNewRef(mod); - } - mod = _PyImport_InitFunc_TrampolineCall(*p->initfunc); - if (mod == NULL) { - return NULL; - } - - if (PyObject_TypeCheck(mod, &PyModuleDef_Type)) { - return PyModule_FromDefAndSpec((PyModuleDef*)mod, spec); - } - else { - /* Remember pointer to module init function. */ - PyModuleDef *def = PyModule_GetDef(mod); - if (def == NULL) { - return NULL; - } - - def->m_base.m_init = p->initfunc; - if (_PyImport_FixupExtensionObject(mod, name, name, - modules) < 0) { - return NULL; - } - return mod; - } - } - } - - // not found - Py_RETURN_NONE; -} - - - -/*[clinic input] -_imp.create_builtin - - spec: object - / - -Create an extension module. -[clinic start generated code]*/ - -static PyObject * -_imp_create_builtin(PyObject *module, PyObject *spec) -/*[clinic end generated code: output=ace7ff22271e6f39 input=37f966f890384e47]*/ -{ - PyThreadState *tstate = _PyThreadState_GET(); - - PyObject *name = PyObject_GetAttrString(spec, "name"); - if (name == NULL) { - return NULL; - } - - if (!PyUnicode_Check(name)) { - PyErr_Format(PyExc_TypeError, - "name must be string, not %.200s", - Py_TYPE(name)->tp_name); - Py_DECREF(name); - return NULL; - } - - PyObject *mod = create_builtin(tstate, name, spec); - Py_DECREF(name); - return mod; -} +/******************/ +/* frozen modules */ +/******************/ /* Return true if the name is an alias. In that case, "alias" is set to the original module name. If it is an alias but the original @@ -1210,14 +1374,11 @@ resolve_module_alias(const char *name, const struct _module_alias *aliases, } } - -/* Frozen modules */ - static bool use_frozen(void) { PyInterpreterState *interp = _PyInterpreterState_GET(); - int override = interp->override_frozen_modules; + int override = OVERRIDE_FROZEN_MODULES(interp); if (override > 0) { return true; } @@ -1587,54 +1748,315 @@ PyImport_ImportFrozenModule(const char *name) } -/* Import a module, either built-in, frozen, or external, and return - its module object WITH INCREMENTED REFERENCE COUNT */ +/*************/ +/* importlib */ +/*************/ -PyObject * -PyImport_ImportModule(const char *name) +/* Import the _imp extension by calling manually _imp.create_builtin() and + _imp.exec_builtin() since importlib is not initialized yet. Initializing + importlib requires the _imp module: this function fix the bootstrap issue. + */ +static PyObject* +bootstrap_imp(PyThreadState *tstate) { - PyObject *pname; - PyObject *result; - - pname = PyUnicode_FromString(name); - if (pname == NULL) + PyObject *name = PyUnicode_FromString("_imp"); + if (name == NULL) { return NULL; - result = PyImport_Import(pname); - Py_DECREF(pname); - return result; -} + } + // Mock a ModuleSpec object just good enough for PyModule_FromDefAndSpec(): + // an object with just a name attribute. + // + // _imp.__spec__ is overridden by importlib._bootstrap._instal() anyway. + PyObject *attrs = Py_BuildValue("{sO}", "name", name); + if (attrs == NULL) { + goto error; + } + PyObject *spec = _PyNamespace_New(attrs); + Py_DECREF(attrs); + if (spec == NULL) { + goto error; + } -/* Import a module without blocking - * - * At first it tries to fetch the module from sys.modules. If the module was - * never loaded before it loads it with PyImport_ImportModule() unless another - * thread holds the import lock. In the latter case the function raises an - * ImportError instead of blocking. - * - * Returns the module object with incremented ref count. - */ -PyObject * -PyImport_ImportModuleNoBlock(const char *name) -{ - return PyImport_ImportModule(name); + // Create the _imp module from its definition. + PyObject *mod = create_builtin(tstate, name, spec); + Py_CLEAR(name); + Py_DECREF(spec); + if (mod == NULL) { + goto error; + } + assert(mod != Py_None); // not found + + // Execute the _imp module: call imp_module_exec(). + if (exec_builtin_or_dynamic(mod) < 0) { + Py_DECREF(mod); + goto error; + } + return mod; + +error: + Py_XDECREF(name); + return NULL; } +/* Global initializations. Can be undone by Py_FinalizeEx(). Don't + call this twice without an intervening Py_FinalizeEx() call. When + initializations fail, a fatal error is issued and the function does + not return. On return, the first thread and interpreter state have + been created. -/* Remove importlib frames from the traceback, - * except in Verbose mode. */ -static void -remove_importlib_frames(PyThreadState *tstate) + Locking: you must hold the interpreter lock while calling this. + (If the lock has not yet been initialized, that's equivalent to + having the lock, but you cannot use multiple threads.) + +*/ +static int +init_importlib(PyThreadState *tstate, PyObject *sysmod) { - const char *importlib_filename = ""; - const char *external_filename = ""; - const char *remove_frames = "_call_with_frames_removed"; - int always_trim = 0; - int in_importlib = 0; - PyObject *exception, *value, *base_tb, *tb; - PyObject **prev_link, **outer_link = NULL; + assert(!_PyErr_Occurred(tstate)); - /* Synopsis: if it's an ImportError, we trim all importlib chunks + PyInterpreterState *interp = tstate->interp; + int verbose = _PyInterpreterState_GetConfig(interp)->verbose; + + // Import _importlib through its frozen version, _frozen_importlib. + if (verbose) { + PySys_FormatStderr("import _frozen_importlib # frozen\n"); + } + if (PyImport_ImportFrozenModule("_frozen_importlib") <= 0) { + return -1; + } + PyObject *importlib = PyImport_AddModule("_frozen_importlib"); // borrowed + if (importlib == NULL) { + return -1; + } + IMPORTLIB(interp) = Py_NewRef(importlib); + + // Import the _imp module + if (verbose) { + PySys_FormatStderr("import _imp # builtin\n"); + } + PyObject *imp_mod = bootstrap_imp(tstate); + if (imp_mod == NULL) { + return -1; + } + if (_PyImport_SetModuleString("_imp", imp_mod) < 0) { + Py_DECREF(imp_mod); + return -1; + } + + // Install importlib as the implementation of import + PyObject *value = PyObject_CallMethod(importlib, "_install", + "OO", sysmod, imp_mod); + Py_DECREF(imp_mod); + if (value == NULL) { + return -1; + } + Py_DECREF(value); + + assert(!_PyErr_Occurred(tstate)); + return 0; +} + + +static int +init_importlib_external(PyInterpreterState *interp) +{ + PyObject *value; + value = PyObject_CallMethod(IMPORTLIB(interp), + "_install_external_importers", ""); + if (value == NULL) { + return -1; + } + Py_DECREF(value); + return 0; +} + +PyObject * +_PyImport_GetImportlibLoader(PyInterpreterState *interp, + const char *loader_name) +{ + return PyObject_GetAttrString(IMPORTLIB(interp), loader_name); +} + +PyObject * +_PyImport_GetImportlibExternalLoader(PyInterpreterState *interp, + const char *loader_name) +{ + PyObject *bootstrap = PyObject_GetAttrString(IMPORTLIB(interp), + "_bootstrap_external"); + if (bootstrap == NULL) { + return NULL; + } + + PyObject *loader_type = PyObject_GetAttrString(bootstrap, loader_name); + Py_DECREF(bootstrap); + return loader_type; +} + +PyObject * +_PyImport_BlessMyLoader(PyInterpreterState *interp, PyObject *module_globals) +{ + PyObject *external = PyObject_GetAttrString(IMPORTLIB(interp), + "_bootstrap_external"); + if (external == NULL) { + return NULL; + } + + PyObject *loader = PyObject_CallMethod(external, "_bless_my_loader", + "O", module_globals, NULL); + Py_DECREF(external); + return loader; +} + +PyObject * +_PyImport_ImportlibModuleRepr(PyInterpreterState *interp, PyObject *m) +{ + return PyObject_CallMethod(IMPORTLIB(interp), "_module_repr", "O", m); +} + + +/*******************/ + +/* Return a finder object for a sys.path/pkg.__path__ item 'p', + possibly by fetching it from the path_importer_cache dict. If it + wasn't yet cached, traverse path_hooks until a hook is found + that can handle the path item. Return None if no hook could; + this tells our caller that the path based finder could not find + a finder for this path item. Cache the result in + path_importer_cache. */ + +static PyObject * +get_path_importer(PyThreadState *tstate, PyObject *path_importer_cache, + PyObject *path_hooks, PyObject *p) +{ + PyObject *importer; + Py_ssize_t j, nhooks; + + /* These conditions are the caller's responsibility: */ + assert(PyList_Check(path_hooks)); + assert(PyDict_Check(path_importer_cache)); + + nhooks = PyList_Size(path_hooks); + if (nhooks < 0) + return NULL; /* Shouldn't happen */ + + importer = PyDict_GetItemWithError(path_importer_cache, p); + if (importer != NULL || _PyErr_Occurred(tstate)) { + return Py_XNewRef(importer); + } + + /* set path_importer_cache[p] to None to avoid recursion */ + if (PyDict_SetItem(path_importer_cache, p, Py_None) != 0) + return NULL; + + for (j = 0; j < nhooks; j++) { + PyObject *hook = PyList_GetItem(path_hooks, j); + if (hook == NULL) + return NULL; + importer = PyObject_CallOneArg(hook, p); + if (importer != NULL) + break; + + if (!_PyErr_ExceptionMatches(tstate, PyExc_ImportError)) { + return NULL; + } + _PyErr_Clear(tstate); + } + if (importer == NULL) { + Py_RETURN_NONE; + } + if (PyDict_SetItem(path_importer_cache, p, importer) < 0) { + Py_DECREF(importer); + return NULL; + } + return importer; +} + +PyObject * +PyImport_GetImporter(PyObject *path) +{ + PyThreadState *tstate = _PyThreadState_GET(); + PyObject *path_importer_cache = PySys_GetObject("path_importer_cache"); + PyObject *path_hooks = PySys_GetObject("path_hooks"); + if (path_importer_cache == NULL || path_hooks == NULL) { + return NULL; + } + return get_path_importer(tstate, path_importer_cache, path_hooks, path); +} + + +/*********************/ +/* importing modules */ +/*********************/ + +int +_PyImport_InitDefaultImportFunc(PyInterpreterState *interp) +{ + // Get the __import__ function + PyObject *import_func = _PyDict_GetItemStringWithError(interp->builtins, + "__import__"); + if (import_func == NULL) { + return -1; + } + IMPORT_FUNC(interp) = Py_NewRef(import_func); + return 0; +} + +int +_PyImport_IsDefaultImportFunc(PyInterpreterState *interp, PyObject *func) +{ + return func == IMPORT_FUNC(interp); +} + + +/* Import a module, either built-in, frozen, or external, and return + its module object WITH INCREMENTED REFERENCE COUNT */ + +PyObject * +PyImport_ImportModule(const char *name) +{ + PyObject *pname; + PyObject *result; + + pname = PyUnicode_FromString(name); + if (pname == NULL) + return NULL; + result = PyImport_Import(pname); + Py_DECREF(pname); + return result; +} + + +/* Import a module without blocking + * + * At first it tries to fetch the module from sys.modules. If the module was + * never loaded before it loads it with PyImport_ImportModule() unless another + * thread holds the import lock. In the latter case the function raises an + * ImportError instead of blocking. + * + * Returns the module object with incremented ref count. + */ +PyObject * +PyImport_ImportModuleNoBlock(const char *name) +{ + return PyImport_ImportModule(name); +} + + +/* Remove importlib frames from the traceback, + * except in Verbose mode. */ +static void +remove_importlib_frames(PyThreadState *tstate) +{ + const char *importlib_filename = ""; + const char *external_filename = ""; + const char *remove_frames = "_call_with_frames_removed"; + int always_trim = 0; + int in_importlib = 0; + PyObject *exception, *value, *base_tb, *tb; + PyObject **prev_link, **outer_link = NULL; + + /* Synopsis: if it's an ImportError, we trim all importlib chunks from the traceback. We always trim chunks which end with a call to "_call_with_frames_removed". */ @@ -1851,8 +2273,8 @@ import_find_and_load(PyThreadState *tstate, PyObject *abs_name) PyObject *mod = NULL; PyInterpreterState *interp = tstate->interp; int import_time = _PyInterpreterState_GetConfig(interp)->import_time; -#define import_level _PyRuntime.imports.find_and_load.import_level -#define accumulated _PyRuntime.imports.find_and_load.accumulated +#define import_level FIND_AND_LOAD.import_level +#define accumulated FIND_AND_LOAD.accumulated _PyTime_t t1 = 0, accumulated_copy = accumulated; @@ -1873,7 +2295,7 @@ import_find_and_load(PyThreadState *tstate, PyObject *abs_name) * _PyDict_GetItemIdWithError(). */ if (import_time) { -#define header _PyRuntime.imports.find_and_load.header +#define header FIND_AND_LOAD.header if (header) { fputs("import time: self [us] | cumulative | imported package\n", stderr); @@ -1889,8 +2311,8 @@ import_find_and_load(PyThreadState *tstate, PyObject *abs_name) if (PyDTrace_IMPORT_FIND_LOAD_START_ENABLED()) PyDTrace_IMPORT_FIND_LOAD_START(PyUnicode_AsUTF8(abs_name)); - mod = PyObject_CallMethodObjArgs(interp->importlib, &_Py_ID(_find_and_load), - abs_name, interp->import_func, NULL); + mod = PyObject_CallMethodObjArgs(IMPORTLIB(interp), &_Py_ID(_find_and_load), + abs_name, IMPORT_FUNC(interp), NULL); if (PyDTrace_IMPORT_FIND_LOAD_DONE_ENABLED()) PyDTrace_IMPORT_FIND_LOAD_DONE(PyUnicode_AsUTF8(abs_name), @@ -1913,23 +2335,6 @@ import_find_and_load(PyThreadState *tstate, PyObject *abs_name) #undef accumulated } -PyObject * -PyImport_GetModule(PyObject *name) -{ - PyThreadState *tstate = _PyThreadState_GET(); - PyObject *mod; - - mod = import_get_module(tstate, name); - if (mod != NULL && mod != Py_None) { - if (import_ensure_initialized(tstate->interp, mod, name) < 0) { - Py_DECREF(mod); - remove_importlib_frames(tstate); - return NULL; - } - } - return mod; -} - PyObject * PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals, PyObject *locals, PyObject *fromlist, @@ -2059,8 +2464,8 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals, if (path) { Py_DECREF(path); final_mod = PyObject_CallMethodObjArgs( - interp->importlib, &_Py_ID(_handle_fromlist), - mod, fromlist, interp->import_func, NULL); + IMPORTLIB(interp), &_Py_ID(_handle_fromlist), + mod, fromlist, IMPORT_FUNC(interp), NULL); } else { final_mod = Py_NewRef(mod); @@ -2129,72 +2534,429 @@ PyImport_ReloadModule(PyObject *m) PyObject * PyImport_Import(PyObject *module_name) { - PyThreadState *tstate = _PyThreadState_GET(); - PyObject *globals = NULL; - PyObject *import = NULL; - PyObject *builtins = NULL; - PyObject *r = NULL; + PyThreadState *tstate = _PyThreadState_GET(); + PyObject *globals = NULL; + PyObject *import = NULL; + PyObject *builtins = NULL; + PyObject *r = NULL; + + PyObject *from_list = PyList_New(0); + if (from_list == NULL) { + goto err; + } + + /* Get the builtins from current globals */ + globals = PyEval_GetGlobals(); + if (globals != NULL) { + Py_INCREF(globals); + builtins = PyObject_GetItem(globals, &_Py_ID(__builtins__)); + if (builtins == NULL) + goto err; + } + else { + /* No globals -- use standard builtins, and fake globals */ + builtins = PyImport_ImportModuleLevel("builtins", + NULL, NULL, NULL, 0); + if (builtins == NULL) { + goto err; + } + globals = Py_BuildValue("{OO}", &_Py_ID(__builtins__), builtins); + if (globals == NULL) + goto err; + } + + /* Get the __import__ function from the builtins */ + if (PyDict_Check(builtins)) { + import = PyObject_GetItem(builtins, &_Py_ID(__import__)); + if (import == NULL) { + _PyErr_SetObject(tstate, PyExc_KeyError, &_Py_ID(__import__)); + } + } + else + import = PyObject_GetAttr(builtins, &_Py_ID(__import__)); + if (import == NULL) + goto err; + + /* Call the __import__ function with the proper argument list + Always use absolute import here. + Calling for side-effect of import. */ + r = PyObject_CallFunction(import, "OOOOi", module_name, globals, + globals, from_list, 0, NULL); + if (r == NULL) + goto err; + Py_DECREF(r); + + r = import_get_module(tstate, module_name); + if (r == NULL && !_PyErr_Occurred(tstate)) { + _PyErr_SetObject(tstate, PyExc_KeyError, module_name); + } + + err: + Py_XDECREF(globals); + Py_XDECREF(builtins); + Py_XDECREF(import); + Py_XDECREF(from_list); + + return r; +} + + +/*********************/ +/* runtime lifecycle */ +/*********************/ + +PyStatus +_PyImport_Init(void) +{ + if (INITTAB != NULL) { + return _PyStatus_ERR("global import state already initialized"); + } + + PyStatus status = _PyStatus_OK(); + + /* Force default raw memory allocator to get a known allocator to be able + to release the memory in _PyImport_Fini() */ + PyMemAllocatorEx old_alloc; + _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + + if (init_builtin_modules_table() != 0) { + status = PyStatus_NoMemory(); + goto done; + } + +done: + PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + return status; +} + +void +_PyImport_Fini(void) +{ + /* Destroy the database used by _PyImport_{Fixup,Find}Extension */ + _extensions_cache_clear(); + if (import_lock != NULL) { + PyThread_free_lock(import_lock); + import_lock = NULL; + } + + /* Use the same memory allocator as _PyImport_Init(). */ + PyMemAllocatorEx old_alloc; + _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + + /* Free memory allocated by _PyImport_Init() */ + fini_builtin_modules_table(); + + PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); +} + +void +_PyImport_Fini2(void) +{ + /* Use the same memory allocator than PyImport_ExtendInittab(). */ + PyMemAllocatorEx old_alloc; + _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + + // Reset PyImport_Inittab + PyImport_Inittab = _PyImport_Inittab; + + /* Free memory allocated by PyImport_ExtendInittab() */ + PyMem_RawFree(inittab_copy); + inittab_copy = NULL; + + PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); +} + + +/*************************/ +/* interpreter lifecycle */ +/*************************/ + +PyStatus +_PyImport_InitCore(PyThreadState *tstate, PyObject *sysmod, int importlib) +{ + // XXX Initialize here: interp->modules and interp->import_func. + // XXX Initialize here: sys.modules and sys.meta_path. + + if (importlib) { + /* This call sets up builtin and frozen import support */ + if (init_importlib(tstate, sysmod) < 0) { + return _PyStatus_ERR("failed to initialize importlib"); + } + } + + return _PyStatus_OK(); +} + +/* In some corner cases it is important to be sure that the import + machinery has been initialized (or not cleaned up yet). For + example, see issue #4236 and PyModule_Create2(). */ + +int +_PyImport_IsInitialized(PyInterpreterState *interp) +{ + if (MODULES(interp) == NULL) + return 0; + return 1; +} + +/* Clear the direct per-interpreter import state, if not cleared already. */ +void +_PyImport_ClearCore(PyInterpreterState *interp) +{ + /* interp->modules should have been cleaned up and cleared already + by _PyImport_FiniCore(). */ + Py_CLEAR(MODULES(interp)); + Py_CLEAR(MODULES_BY_INDEX(interp)); + Py_CLEAR(IMPORTLIB(interp)); + Py_CLEAR(IMPORT_FUNC(interp)); +} + +void +_PyImport_FiniCore(PyInterpreterState *interp) +{ + int verbose = _PyInterpreterState_GetConfig(interp)->verbose; + + if (_PySys_ClearAttrString(interp, "meta_path", verbose) < 0) { + PyErr_WriteUnraisable(NULL); + } + + // XXX Pull in most of finalize_modules() in pylifecycle.c. + + if (_PySys_ClearAttrString(interp, "modules", verbose) < 0) { + PyErr_WriteUnraisable(NULL); + } + + _PyImport_ClearCore(interp); +} + +// XXX Add something like _PyImport_Disable() for use early in interp fini? + + +/* "external" imports */ + +static int +init_zipimport(PyThreadState *tstate, int verbose) +{ + PyObject *path_hooks = PySys_GetObject("path_hooks"); + if (path_hooks == NULL) { + _PyErr_SetString(tstate, PyExc_RuntimeError, + "unable to get sys.path_hooks"); + return -1; + } + + if (verbose) { + PySys_WriteStderr("# installing zipimport hook\n"); + } + + PyObject *zipimporter = _PyImport_GetModuleAttrString("zipimport", "zipimporter"); + if (zipimporter == NULL) { + _PyErr_Clear(tstate); /* No zipimporter object -- okay */ + if (verbose) { + PySys_WriteStderr("# can't import zipimport.zipimporter\n"); + } + } + else { + /* sys.path_hooks.insert(0, zipimporter) */ + int err = PyList_Insert(path_hooks, 0, zipimporter); + Py_DECREF(zipimporter); + if (err < 0) { + return -1; + } + if (verbose) { + PySys_WriteStderr("# installed zipimport hook\n"); + } + } + + return 0; +} + +PyStatus +_PyImport_InitExternal(PyThreadState *tstate) +{ + int verbose = _PyInterpreterState_GetConfig(tstate->interp)->verbose; + + // XXX Initialize here: sys.path_hooks and sys.path_importer_cache. + + if (init_importlib_external(tstate->interp) != 0) { + _PyErr_Print(tstate); + return _PyStatus_ERR("external importer setup failed"); + } + + if (init_zipimport(tstate, verbose) != 0) { + PyErr_Print(); + return _PyStatus_ERR("initializing zipimport failed"); + } + + return _PyStatus_OK(); +} + +void +_PyImport_FiniExternal(PyInterpreterState *interp) +{ + int verbose = _PyInterpreterState_GetConfig(interp)->verbose; + + // XXX Uninstall importlib metapath importers here? + + if (_PySys_ClearAttrString(interp, "path_importer_cache", verbose) < 0) { + PyErr_WriteUnraisable(NULL); + } + if (_PySys_ClearAttrString(interp, "path_hooks", verbose) < 0) { + PyErr_WriteUnraisable(NULL); + } +} + + +/******************/ +/* module helpers */ +/******************/ + +PyObject * +_PyImport_GetModuleAttr(PyObject *modname, PyObject *attrname) +{ + PyObject *mod = PyImport_Import(modname); + if (mod == NULL) { + return NULL; + } + PyObject *result = PyObject_GetAttr(mod, attrname); + Py_DECREF(mod); + return result; +} + +PyObject * +_PyImport_GetModuleAttrString(const char *modname, const char *attrname) +{ + PyObject *pmodname = PyUnicode_FromString(modname); + if (pmodname == NULL) { + return NULL; + } + PyObject *pattrname = PyUnicode_FromString(attrname); + if (pattrname == NULL) { + Py_DECREF(pmodname); + return NULL; + } + PyObject *result = _PyImport_GetModuleAttr(pmodname, pattrname); + Py_DECREF(pattrname); + Py_DECREF(pmodname); + return result; +} + + +/**************/ +/* the module */ +/**************/ + +/*[clinic input] +_imp.lock_held + +Return True if the import lock is currently held, else False. + +On platforms without threads, return False. +[clinic start generated code]*/ + +static PyObject * +_imp_lock_held_impl(PyObject *module) +/*[clinic end generated code: output=8b89384b5e1963fc input=9b088f9b217d9bdf]*/ +{ + return PyBool_FromLong(import_lock_thread != PYTHREAD_INVALID_THREAD_ID); +} + +/*[clinic input] +_imp.acquire_lock + +Acquires the interpreter's import lock for the current thread. + +This lock should be used by import hooks to ensure thread-safety when importing +modules. On platforms without threads, this function does nothing. +[clinic start generated code]*/ + +static PyObject * +_imp_acquire_lock_impl(PyObject *module) +/*[clinic end generated code: output=1aff58cb0ee1b026 input=4a2d4381866d5fdc]*/ +{ + _PyImport_AcquireLock(); + Py_RETURN_NONE; +} + +/*[clinic input] +_imp.release_lock + +Release the interpreter's import lock. + +On platforms without threads, this function does nothing. +[clinic start generated code]*/ + +static PyObject * +_imp_release_lock_impl(PyObject *module) +/*[clinic end generated code: output=7faab6d0be178b0a input=934fb11516dd778b]*/ +{ + if (_PyImport_ReleaseLock() < 0) { + PyErr_SetString(PyExc_RuntimeError, + "not holding the import lock"); + return NULL; + } + Py_RETURN_NONE; +} + + +/*[clinic input] +_imp._fix_co_filename + + code: object(type="PyCodeObject *", subclass_of="&PyCode_Type") + Code object to change. + + path: unicode + File path to use. + / + +Changes code.co_filename to specify the passed-in file path. +[clinic start generated code]*/ + +static PyObject * +_imp__fix_co_filename_impl(PyObject *module, PyCodeObject *code, + PyObject *path) +/*[clinic end generated code: output=1d002f100235587d input=895ba50e78b82f05]*/ + +{ + update_compiled_module(code, path); - PyObject *from_list = PyList_New(0); - if (from_list == NULL) { - goto err; - } + Py_RETURN_NONE; +} - /* Get the builtins from current globals */ - globals = PyEval_GetGlobals(); - if (globals != NULL) { - Py_INCREF(globals); - builtins = PyObject_GetItem(globals, &_Py_ID(__builtins__)); - if (builtins == NULL) - goto err; - } - else { - /* No globals -- use standard builtins, and fake globals */ - builtins = PyImport_ImportModuleLevel("builtins", - NULL, NULL, NULL, 0); - if (builtins == NULL) { - goto err; - } - globals = Py_BuildValue("{OO}", &_Py_ID(__builtins__), builtins); - if (globals == NULL) - goto err; - } - /* Get the __import__ function from the builtins */ - if (PyDict_Check(builtins)) { - import = PyObject_GetItem(builtins, &_Py_ID(__import__)); - if (import == NULL) { - _PyErr_SetObject(tstate, PyExc_KeyError, &_Py_ID(__import__)); - } - } - else - import = PyObject_GetAttr(builtins, &_Py_ID(__import__)); - if (import == NULL) - goto err; +/*[clinic input] +_imp.create_builtin - /* Call the __import__ function with the proper argument list - Always use absolute import here. - Calling for side-effect of import. */ - r = PyObject_CallFunction(import, "OOOOi", module_name, globals, - globals, from_list, 0, NULL); - if (r == NULL) - goto err; - Py_DECREF(r); + spec: object + / - r = import_get_module(tstate, module_name); - if (r == NULL && !_PyErr_Occurred(tstate)) { - _PyErr_SetObject(tstate, PyExc_KeyError, module_name); +Create an extension module. +[clinic start generated code]*/ + +static PyObject * +_imp_create_builtin(PyObject *module, PyObject *spec) +/*[clinic end generated code: output=ace7ff22271e6f39 input=37f966f890384e47]*/ +{ + PyThreadState *tstate = _PyThreadState_GET(); + + PyObject *name = PyObject_GetAttrString(spec, "name"); + if (name == NULL) { + return NULL; } - err: - Py_XDECREF(globals); - Py_XDECREF(builtins); - Py_XDECREF(import); - Py_XDECREF(from_list); + if (!PyUnicode_Check(name)) { + PyErr_Format(PyExc_TypeError, + "name must be string, not %.200s", + Py_TYPE(name)->tp_name); + Py_DECREF(name); + return NULL; + } - return r; + PyObject *mod = create_builtin(tstate, name, spec); + Py_DECREF(name); + return mod; } + /*[clinic input] _imp.extension_suffixes @@ -2459,34 +3221,10 @@ _imp__override_frozen_modules_for_tests_impl(PyObject *module, int override) /*[clinic end generated code: output=36d5cb1594160811 input=8f1f95a3ef21aec3]*/ { PyInterpreterState *interp = _PyInterpreterState_GET(); - interp->override_frozen_modules = override; + OVERRIDE_FROZEN_MODULES(interp) = override; Py_RETURN_NONE; } -/* Common implementation for _imp.exec_dynamic and _imp.exec_builtin */ -static int -exec_builtin_or_dynamic(PyObject *mod) { - PyModuleDef *def; - void *state; - - if (!PyModule_Check(mod)) { - return 0; - } - - def = PyModule_GetDef(mod); - if (def == NULL) { - return 0; - } - - state = PyModule_GetState(mod); - if (state) { - /* Already initialized; skip reload */ - return 0; - } - - return PyModule_ExecDef(mod, def); -} - #ifdef HAVE_DYNAMIC_LOADING /*[clinic input] @@ -2674,158 +3412,6 @@ PyInit__imp(void) } -// Import the _imp extension by calling manually _imp.create_builtin() and -// _imp.exec_builtin() since importlib is not initialized yet. Initializing -// importlib requires the _imp module: this function fix the bootstrap issue. -PyObject* -_PyImport_BootstrapImp(PyThreadState *tstate) -{ - PyObject *name = PyUnicode_FromString("_imp"); - if (name == NULL) { - return NULL; - } - - // Mock a ModuleSpec object just good enough for PyModule_FromDefAndSpec(): - // an object with just a name attribute. - // - // _imp.__spec__ is overridden by importlib._bootstrap._instal() anyway. - PyObject *attrs = Py_BuildValue("{sO}", "name", name); - if (attrs == NULL) { - goto error; - } - PyObject *spec = _PyNamespace_New(attrs); - Py_DECREF(attrs); - if (spec == NULL) { - goto error; - } - - // Create the _imp module from its definition. - PyObject *mod = create_builtin(tstate, name, spec); - Py_CLEAR(name); - Py_DECREF(spec); - if (mod == NULL) { - goto error; - } - assert(mod != Py_None); // not found - - // Execute the _imp module: call imp_module_exec(). - if (exec_builtin_or_dynamic(mod) < 0) { - Py_DECREF(mod); - goto error; - } - return mod; - -error: - Py_XDECREF(name); - return NULL; -} - - -/* API for embedding applications that want to add their own entries - to the table of built-in modules. This should normally be called - *before* Py_Initialize(). When the table resize fails, -1 is - returned and the existing table is unchanged. - - After a similar function by Just van Rossum. */ - -int -PyImport_ExtendInittab(struct _inittab *newtab) -{ - struct _inittab *p; - size_t i, n; - int res = 0; - - if (_PyRuntime.imports.inittab != NULL) { - Py_FatalError("PyImport_ExtendInittab() may not be called after Py_Initialize()"); - } - - /* Count the number of entries in both tables */ - for (n = 0; newtab[n].name != NULL; n++) - ; - if (n == 0) - return 0; /* Nothing to do */ - for (i = 0; PyImport_Inittab[i].name != NULL; i++) - ; - - /* Force default raw memory allocator to get a known allocator to be able - to release the memory in _PyImport_Fini2() */ - PyMemAllocatorEx old_alloc; - _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - - /* Allocate new memory for the combined table */ - p = NULL; - if (i + n <= SIZE_MAX / sizeof(struct _inittab) - 1) { - size_t size = sizeof(struct _inittab) * (i + n + 1); - p = PyMem_RawRealloc(inittab_copy, size); - } - if (p == NULL) { - res = -1; - goto done; - } - - /* Copy the tables into the new memory at the first call - to PyImport_ExtendInittab(). */ - if (inittab_copy != PyImport_Inittab) { - memcpy(p, PyImport_Inittab, (i+1) * sizeof(struct _inittab)); - } - memcpy(p + i, newtab, (n + 1) * sizeof(struct _inittab)); - PyImport_Inittab = inittab_copy = p; - -done: - PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - return res; -} - -/* Shorthand to add a single entry given a name and a function */ - -int -PyImport_AppendInittab(const char *name, PyObject* (*initfunc)(void)) -{ - struct _inittab newtab[2]; - - if (_PyRuntime.imports.inittab != NULL) { - Py_FatalError("PyImport_AppendInittab() may not be called after Py_Initialize()"); - } - - memset(newtab, '\0', sizeof newtab); - - newtab[0].name = name; - newtab[0].initfunc = initfunc; - - return PyImport_ExtendInittab(newtab); -} - - -PyObject * -_PyImport_GetModuleAttr(PyObject *modname, PyObject *attrname) -{ - PyObject *mod = PyImport_Import(modname); - if (mod == NULL) { - return NULL; - } - PyObject *result = PyObject_GetAttr(mod, attrname); - Py_DECREF(mod); - return result; -} - -PyObject * -_PyImport_GetModuleAttrString(const char *modname, const char *attrname) -{ - PyObject *pmodname = PyUnicode_FromString(modname); - if (pmodname == NULL) { - return NULL; - } - PyObject *pattrname = PyUnicode_FromString(attrname); - if (pattrname == NULL) { - Py_DECREF(pmodname); - return NULL; - } - PyObject *result = _PyImport_GetModuleAttr(pmodname, pattrname); - Py_DECREF(pattrname); - Py_DECREF(pmodname); - return result; -} - #ifdef __cplusplus } #endif diff --git a/Python/importdl.c b/Python/importdl.c index 91fa06f49c2897..6dafb4541486e9 100644 --- a/Python/importdl.c +++ b/Python/importdl.c @@ -99,7 +99,7 @@ _PyImport_LoadDynamicModuleWithSpec(PyObject *spec, FILE *fp) #endif PyObject *name_unicode = NULL, *name = NULL, *path = NULL, *m = NULL; const char *name_buf, *hook_prefix; - const char *oldcontext; + const char *oldcontext, *newcontext; dl_funcptr exportfunc; PyModuleDef *def; PyModInitFunction p0; @@ -113,6 +113,10 @@ _PyImport_LoadDynamicModuleWithSpec(PyObject *spec, FILE *fp) "spec.name must be a string"); goto error; } + newcontext = PyUnicode_AsUTF8(name_unicode); + if (newcontext == NULL) { + goto error; + } name = get_encoded_name(name_unicode, &hook_prefix); if (name == NULL) { @@ -160,16 +164,9 @@ _PyImport_LoadDynamicModuleWithSpec(PyObject *spec, FILE *fp) p0 = (PyModInitFunction)exportfunc; /* Package context is needed for single-phase init */ -#define _Py_PackageContext (_PyRuntime.imports.pkgcontext) - oldcontext = _Py_PackageContext; - _Py_PackageContext = PyUnicode_AsUTF8(name_unicode); - if (_Py_PackageContext == NULL) { - _Py_PackageContext = oldcontext; - goto error; - } + oldcontext = _PyImport_SwapPackageContext(newcontext); m = _PyImport_InitFunc_TrampolineCall(p0); - _Py_PackageContext = oldcontext; -#undef _Py_PackageContext + _PyImport_SwapPackageContext(oldcontext); if (m == NULL) { if (!PyErr_Occurred()) { diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 045a2996e8988b..281035dafa9577 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -156,79 +156,6 @@ Py_IsInitialized(void) } -/* Global initializations. Can be undone by Py_FinalizeEx(). Don't - call this twice without an intervening Py_FinalizeEx() call. When - initializations fail, a fatal error is issued and the function does - not return. On return, the first thread and interpreter state have - been created. - - Locking: you must hold the interpreter lock while calling this. - (If the lock has not yet been initialized, that's equivalent to - having the lock, but you cannot use multiple threads.) - -*/ -static int -init_importlib(PyThreadState *tstate, PyObject *sysmod) -{ - assert(!_PyErr_Occurred(tstate)); - - PyInterpreterState *interp = tstate->interp; - int verbose = _PyInterpreterState_GetConfig(interp)->verbose; - - // Import _importlib through its frozen version, _frozen_importlib. - if (verbose) { - PySys_FormatStderr("import _frozen_importlib # frozen\n"); - } - if (PyImport_ImportFrozenModule("_frozen_importlib") <= 0) { - return -1; - } - PyObject *importlib = PyImport_AddModule("_frozen_importlib"); // borrowed - if (importlib == NULL) { - return -1; - } - interp->importlib = Py_NewRef(importlib); - - // Import the _imp module - if (verbose) { - PySys_FormatStderr("import _imp # builtin\n"); - } - PyObject *imp_mod = _PyImport_BootstrapImp(tstate); - if (imp_mod == NULL) { - return -1; - } - if (_PyImport_SetModuleString("_imp", imp_mod) < 0) { - Py_DECREF(imp_mod); - return -1; - } - - // Install importlib as the implementation of import - PyObject *value = PyObject_CallMethod(importlib, "_install", - "OO", sysmod, imp_mod); - Py_DECREF(imp_mod); - if (value == NULL) { - return -1; - } - Py_DECREF(value); - - assert(!_PyErr_Occurred(tstate)); - return 0; -} - - -static PyStatus -init_importlib_external(PyThreadState *tstate) -{ - PyObject *value; - value = PyObject_CallMethod(tstate->interp->importlib, - "_install_external_importers", ""); - if (value == NULL) { - _PyErr_Print(tstate); - return _PyStatus_ERR("external importer setup failed"); - } - Py_DECREF(value); - return _PyImportZip_Init(tstate); -} - /* Helper functions to better handle the legacy C locale * * The legacy C locale assumes ASCII as the default text encoding, which @@ -814,7 +741,8 @@ pycore_init_builtins(PyThreadState *tstate) goto error; } - if (_PyImport_FixupBuiltin(bimod, "builtins", interp->modules) < 0) { + PyObject *modules = _PyImport_GetModules(interp); + if (_PyImport_FixupBuiltin(bimod, "builtins", modules) < 0) { goto error; } @@ -850,13 +778,9 @@ pycore_init_builtins(PyThreadState *tstate) } Py_DECREF(bimod); - // Get the __import__ function - PyObject *import_func = _PyDict_GetItemStringWithError(interp->builtins, - "__import__"); - if (import_func == NULL) { + if (_PyImport_InitDefaultImportFunc(interp) < 0) { goto error; } - interp->import_func = Py_NewRef(import_func); assert(!_PyErr_Occurred(tstate)); return _PyStatus_OK(); @@ -918,11 +842,10 @@ pycore_interp_init(PyThreadState *tstate) } const PyConfig *config = _PyInterpreterState_GetConfig(interp); - if (config->_install_importlib) { - /* This call sets up builtin and frozen import support */ - if (init_importlib(tstate, sysmod) < 0) { - return _PyStatus_ERR("failed to initialize importlib"); - } + + status = _PyImport_InitCore(tstate, sysmod, config->_install_importlib); + if (_PyStatus_EXCEPTION(status)) { + goto done; } done: @@ -1172,7 +1095,7 @@ init_interp_main(PyThreadState *tstate) return _PyStatus_ERR("failed to update the Python config"); } - status = init_importlib_external(tstate); + status = _PyImport_InitExternal(tstate); if (_PyStatus_EXCEPTION(status)) { return status; } @@ -1379,8 +1302,11 @@ finalize_modules_delete_special(PyThreadState *tstate, int verbose) static const char * const sys_deletes[] = { "path", "argv", "ps1", "ps2", "last_type", "last_value", "last_traceback", - "path_hooks", "path_importer_cache", "meta_path", "__interactivehook__", + // path_hooks and path_importer_cache are cleared + // by _PyImport_FiniExternal(). + // XXX Clear meta_path in _PyImport_FiniCore(). + "meta_path", NULL }; @@ -1401,10 +1327,7 @@ finalize_modules_delete_special(PyThreadState *tstate, int verbose) const char * const *p; for (p = sys_deletes; *p != NULL; p++) { - if (verbose) { - PySys_WriteStderr("# clear sys.%s\n", *p); - } - if (PyDict_SetItemString(interp->sysdict, *p, Py_None) < 0) { + if (_PySys_ClearAttrString(interp, *p, verbose) < 0) { PyErr_WriteUnraisable(NULL); } } @@ -1576,11 +1499,12 @@ finalize_clear_sys_builtins_dict(PyInterpreterState *interp, int verbose) /* Clear modules, as good as we can */ +// XXX Move most of this to import.c. static void finalize_modules(PyThreadState *tstate) { PyInterpreterState *interp = tstate->interp; - PyObject *modules = interp->modules; + PyObject *modules = _PyImport_GetModules(interp); if (modules == NULL) { // Already done return; @@ -1645,12 +1569,12 @@ finalize_modules(PyThreadState *tstate) // clear PyInterpreterState.modules_by_index and // clear PyModuleDef.m_base.m_copy (of extensions not using the multi-phase // initialization API) - _PyInterpreterState_ClearModules(interp); + _PyImport_ClearModulesByIndex(interp); // Clear and delete the modules directory. Actual modules will // still be there only if imported during the execution of some // destructor. - Py_SETREF(interp->modules, NULL); + _PyImport_ClearModules(interp); // Collect garbage once more _PyGC_CollectNoFail(tstate); @@ -1861,6 +1785,8 @@ Py_FinalizeEx(void) runtime->initialized = 0; runtime->core_initialized = 0; + // XXX Call something like _PyImport_Disable() here? + /* Destroy the state of all threads of the interpreter, except of the current thread. In practice, only daemon threads should still be alive, except if wait_for_thread_shutdown() has been cancelled by CTRL+C. @@ -1910,6 +1836,7 @@ Py_FinalizeEx(void) PyGC_Collect(); /* Destroy all modules */ + _PyImport_FiniExternal(tstate->interp); finalize_modules(tstate); /* Print debug stats if any */ @@ -1943,7 +1870,9 @@ Py_FinalizeEx(void) so it is possible to use tracemalloc in objects destructor. */ _PyTraceMalloc_Fini(); - /* Destroy the database used by _PyImport_{Fixup,Find}Extension */ + /* Finalize any remaining import state */ + // XXX Move these up to where finalize_modules() is currently. + _PyImport_FiniCore(tstate->interp); _PyImport_Fini(); /* unload faulthandler module */ @@ -2183,7 +2112,11 @@ Py_EndInterpreter(PyThreadState *tstate) Py_FatalError("not the last thread"); } + // XXX Call something like _PyImport_Disable() here? + + _PyImport_FiniExternal(tstate->interp); finalize_modules(tstate); + _PyImport_FiniCore(tstate->interp); finalize_interp_clear(tstate); finalize_interp_delete(tstate->interp); @@ -2232,8 +2165,8 @@ add_main_module(PyInterpreterState *interp) if (PyErr_Occurred()) { return _PyStatus_ERR("Failed to test __main__.__loader__"); } - PyObject *loader = PyObject_GetAttrString(interp->importlib, - "BuiltinImporter"); + PyObject *loader = _PyImport_GetImportlibLoader(interp, + "BuiltinImporter"); if (loader == NULL) { return _PyStatus_ERR("Failed to retrieve BuiltinImporter"); } @@ -2739,7 +2672,7 @@ _Py_DumpExtensionModules(int fd, PyInterpreterState *interp) if (interp == NULL) { return; } - PyObject *modules = interp->modules; + PyObject *modules = _PyImport_GetModules(interp); if (modules == NULL || !PyDict_Check(modules)) { return; } diff --git a/Python/pystate.c b/Python/pystate.c index 1261092d1435fa..4770caaed0a363 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -772,11 +772,13 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) Py_CLEAR(interp->codec_search_path); Py_CLEAR(interp->codec_search_cache); Py_CLEAR(interp->codec_error_registry); - Py_CLEAR(interp->modules); - Py_CLEAR(interp->modules_by_index); + + assert(interp->imports.modules == NULL); + assert(interp->imports.modules_by_index == NULL); + assert(interp->imports.importlib == NULL); + assert(interp->imports.import_func == NULL); + Py_CLEAR(interp->builtins_copy); - Py_CLEAR(interp->importlib); - Py_CLEAR(interp->import_func); Py_CLEAR(interp->dict); #ifdef HAVE_FORK Py_CLEAR(interp->before_forkers); @@ -836,6 +838,7 @@ PyInterpreterState_Clear(PyInterpreterState *interp) // garbage. It can be different than the current Python thread state // of 'interp'. PyThreadState *current_tstate = current_fast_get(interp->runtime); + _PyImport_ClearCore(interp); interpreter_clear(interp, current_tstate); } @@ -843,6 +846,7 @@ PyInterpreterState_Clear(PyInterpreterState *interp) void _PyInterpreterState_Clear(PyThreadState *tstate) { + _PyImport_ClearCore(tstate->interp); interpreter_clear(tstate->interp, tstate); } @@ -945,36 +949,6 @@ _PyInterpreterState_DeleteExceptMain(_PyRuntimeState *runtime) #endif -// Used by finalize_modules() -void -_PyInterpreterState_ClearModules(PyInterpreterState *interp) -{ - if (!interp->modules_by_index) { - return; - } - - Py_ssize_t i; - for (i = 0; i < PyList_GET_SIZE(interp->modules_by_index); i++) { - PyObject *m = PyList_GET_ITEM(interp->modules_by_index, i); - if (PyModule_Check(m)) { - /* cleanup the saved copy of module dicts */ - PyModuleDef *md = PyModule_GetDef(m); - if (md) { - Py_CLEAR(md->m_base.m_copy); - } - } - } - - /* Setting modules_by_index to NULL could be dangerous, so we - clear the list instead. */ - if (PyList_SetSlice(interp->modules_by_index, - 0, PyList_GET_SIZE(interp->modules_by_index), - NULL)) { - PyErr_WriteUnraisable(interp->modules_by_index); - } -} - - //---------- // accessors //---------- @@ -1058,11 +1032,12 @@ _PyInterpreterState_RequireIDRef(PyInterpreterState *interp, int required) PyObject * _PyInterpreterState_GetMainModule(PyInterpreterState *interp) { - if (interp->modules == NULL) { + PyObject *modules = _PyImport_GetModules(interp); + if (modules == NULL) { PyErr_SetString(PyExc_RuntimeError, "interpreter not initialized"); return NULL; } - return PyMapping_GetItemString(interp->modules, "__main__"); + return PyMapping_GetItemString(modules, "__main__"); } PyObject * @@ -1922,110 +1897,6 @@ _PyThread_CurrentExceptions(void) } -/****************/ -/* module state */ -/****************/ - -PyObject* -PyState_FindModule(PyModuleDef* module) -{ - Py_ssize_t index = module->m_base.m_index; - PyInterpreterState *state = _PyInterpreterState_GET(); - PyObject *res; - if (module->m_slots) { - return NULL; - } - if (index == 0) - return NULL; - if (state->modules_by_index == NULL) - return NULL; - if (index >= PyList_GET_SIZE(state->modules_by_index)) - return NULL; - res = PyList_GET_ITEM(state->modules_by_index, index); - return res==Py_None ? NULL : res; -} - -int -_PyState_AddModule(PyThreadState *tstate, PyObject* module, PyModuleDef* def) -{ - if (!def) { - assert(_PyErr_Occurred(tstate)); - return -1; - } - if (def->m_slots) { - _PyErr_SetString(tstate, - PyExc_SystemError, - "PyState_AddModule called on module with slots"); - return -1; - } - - PyInterpreterState *interp = tstate->interp; - if (!interp->modules_by_index) { - interp->modules_by_index = PyList_New(0); - if (!interp->modules_by_index) { - return -1; - } - } - - while (PyList_GET_SIZE(interp->modules_by_index) <= def->m_base.m_index) { - if (PyList_Append(interp->modules_by_index, Py_None) < 0) { - return -1; - } - } - - return PyList_SetItem(interp->modules_by_index, - def->m_base.m_index, Py_NewRef(module)); -} - -int -PyState_AddModule(PyObject* module, PyModuleDef* def) -{ - if (!def) { - Py_FatalError("module definition is NULL"); - return -1; - } - - PyThreadState *tstate = current_fast_get(&_PyRuntime); - PyInterpreterState *interp = tstate->interp; - Py_ssize_t index = def->m_base.m_index; - if (interp->modules_by_index && - index < PyList_GET_SIZE(interp->modules_by_index) && - module == PyList_GET_ITEM(interp->modules_by_index, index)) - { - _Py_FatalErrorFormat(__func__, "module %p already added", module); - return -1; - } - return _PyState_AddModule(tstate, module, def); -} - -int -PyState_RemoveModule(PyModuleDef* def) -{ - PyThreadState *tstate = current_fast_get(&_PyRuntime); - PyInterpreterState *interp = tstate->interp; - - if (def->m_slots) { - _PyErr_SetString(tstate, - PyExc_SystemError, - "PyState_RemoveModule called on module with slots"); - return -1; - } - - Py_ssize_t index = def->m_base.m_index; - if (index == 0) { - Py_FatalError("invalid module index"); - } - if (interp->modules_by_index == NULL) { - Py_FatalError("Interpreters module-list not accessible."); - } - if (index > PyList_GET_SIZE(interp->modules_by_index)) { - Py_FatalError("Module index out of bounds."); - } - - return PyList_SetItem(interp->modules_by_index, index, Py_NewRef(Py_None)); -} - - /***********************************/ /* Python "auto thread state" API. */ /***********************************/ diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 6a4d593768690a..ce993ea8796cb7 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -350,14 +350,8 @@ static int set_main_loader(PyObject *d, PyObject *filename, const char *loader_name) { PyInterpreterState *interp = _PyInterpreterState_GET(); - PyObject *bootstrap = PyObject_GetAttrString(interp->importlib, - "_bootstrap_external"); - if (bootstrap == NULL) { - return -1; - } - - PyObject *loader_type = PyObject_GetAttrString(bootstrap, loader_name); - Py_DECREF(bootstrap); + PyObject *loader_type = _PyImport_GetImportlibExternalLoader(interp, + loader_name); if (loader_type == NULL) { return -1; } diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 6e81ef92b67f70..b69b803560924c 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -142,6 +142,20 @@ PySys_SetObject(const char *name, PyObject *v) return sys_set_object_str(interp, name, v); } +int +_PySys_ClearAttrString(PyInterpreterState *interp, + const char *name, int verbose) +{ + if (verbose) { + PySys_WriteStderr("# clear sys.%s\n", name); + } + /* To play it safe, we set the attr to None instead of deleting it. */ + if (PyDict_SetItemString(interp->sysdict, name, Py_None) < 0) { + return -1; + } + return 0; +} + static int should_audit(PyInterpreterState *interp) @@ -1650,7 +1664,7 @@ sys_setdlopenflags_impl(PyObject *module, int new_val) /*[clinic end generated code: output=ec918b7fe0a37281 input=4c838211e857a77f]*/ { PyInterpreterState *interp = _PyInterpreterState_GET(); - interp->dlopenflags = new_val; + _PyImport_SetDLOpenFlags(interp, new_val); Py_RETURN_NONE; } @@ -1668,7 +1682,8 @@ sys_getdlopenflags_impl(PyObject *module) /*[clinic end generated code: output=e92cd1bc5005da6e input=dc4ea0899c53b4b6]*/ { PyInterpreterState *interp = _PyInterpreterState_GET(); - return PyLong_FromLong(interp->dlopenflags); + return PyLong_FromLong( + _PyImport_GetDLOpenFlags(interp)); } #endif /* HAVE_DLOPEN */ @@ -2279,22 +2294,10 @@ static PyMethodDef sys_methods[] = { static PyObject * list_builtin_module_names(void) { - PyObject *list = PyList_New(0); + PyObject *list = _PyImport_GetBuiltinModuleNames(); if (list == NULL) { return NULL; } - struct _inittab *inittab = _PyRuntime.imports.inittab; - for (Py_ssize_t i = 0; inittab[i].name != NULL; i++) { - PyObject *name = PyUnicode_FromString(inittab[i].name); - if (name == NULL) { - goto error; - } - if (PyList_Append(list, name) < 0) { - Py_DECREF(name); - goto error; - } - Py_DECREF(name); - } if (PyList_Sort(list) != 0) { goto error; } @@ -3411,11 +3414,10 @@ _PySys_Create(PyThreadState *tstate, PyObject **sysmod_p) PyInterpreterState *interp = tstate->interp; - PyObject *modules = PyDict_New(); + PyObject *modules = _PyImport_InitModules(interp); if (modules == NULL) { goto error; } - interp->modules = modules; PyObject *sysmod = _PyModule_CreateInitialized(&sysmodule, PYTHON_API_VERSION); if (sysmod == NULL) { @@ -3428,7 +3430,7 @@ _PySys_Create(PyThreadState *tstate, PyObject **sysmod_p) } interp->sysdict = Py_NewRef(sysdict); - if (PyDict_SetItemString(sysdict, "modules", interp->modules) < 0) { + if (PyDict_SetItemString(sysdict, "modules", modules) < 0) { goto error; } @@ -3442,7 +3444,7 @@ _PySys_Create(PyThreadState *tstate, PyObject **sysmod_p) return status; } - if (_PyImport_FixupBuiltin(sysmod, "sys", interp->modules) < 0) { + if (_PyImport_FixupBuiltin(sysmod, "sys", modules) < 0) { goto error; } From b365d88465d9228ce4e9e0be20b88e9e4056ad88 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 15 Feb 2023 16:05:07 -0700 Subject: [PATCH 103/247] gh-101758: Add a Test For Single-Phase Init Modules in Multiple Interpreters (gh-101920) The test verifies the behavior of single-phase init modules when loaded in multiple interpreters. https://github.com/python/cpython/issues/101758 --- Include/internal/pycore_import.h | 3 ++ Lib/test/test_imp.py | 73 ++++++++++++++++++++++++++++++ Modules/_testinternalcapi.c | 15 +++++++ Modules/_testsinglephase.c | 49 +++++++++++++++++++- Python/import.c | 76 +++++++++++++++++++++++++++++++- 5 files changed, 212 insertions(+), 4 deletions(-) diff --git a/Include/internal/pycore_import.h b/Include/internal/pycore_import.h index da766253ef6b9c..6ee7356b41c021 100644 --- a/Include/internal/pycore_import.h +++ b/Include/internal/pycore_import.h @@ -153,6 +153,9 @@ PyAPI_DATA(const struct _frozen *) _PyImport_FrozenStdlib; PyAPI_DATA(const struct _frozen *) _PyImport_FrozenTest; extern const struct _module_alias * _PyImport_FrozenAliases; +// for testing +PyAPI_FUNC(int) _PyImport_ClearExtension(PyObject *name, PyObject *filename); + #ifdef __cplusplus } #endif diff --git a/Lib/test/test_imp.py b/Lib/test/test_imp.py index c85ab92307de78..e81eb6f0a86fe8 100644 --- a/Lib/test/test_imp.py +++ b/Lib/test/test_imp.py @@ -10,10 +10,13 @@ from test.support import os_helper from test.support import script_helper from test.support import warnings_helper +import textwrap import unittest import warnings imp = warnings_helper.import_deprecated('imp') import _imp +import _testinternalcapi +import _xxsubinterpreters as _interpreters OS_PATH_NAME = os.path.__name__ @@ -251,6 +254,71 @@ def test_issue16421_multiple_modules_in_one_dll(self): with self.assertRaises(ImportError): imp.load_dynamic('nonexistent', pathname) + @requires_load_dynamic + def test_singlephase_multiple_interpreters(self): + # Currently, for every single-phrase init module loaded + # in multiple interpreters, those interpreters share a + # PyModuleDef for that object, which can be a problem. + + # This single-phase module has global state, which is shared + # by the interpreters. + import _testsinglephase + name = _testsinglephase.__name__ + filename = _testsinglephase.__file__ + + del sys.modules[name] + _testsinglephase._clear_globals() + _testinternalcapi.clear_extension(name, filename) + init_count = _testsinglephase.initialized_count() + assert init_count == -1, (init_count,) + + def clean_up(): + _testsinglephase._clear_globals() + _testinternalcapi.clear_extension(name, filename) + self.addCleanup(clean_up) + + interp1 = _interpreters.create(isolated=False) + self.addCleanup(_interpreters.destroy, interp1) + interp2 = _interpreters.create(isolated=False) + self.addCleanup(_interpreters.destroy, interp2) + + script = textwrap.dedent(f''' + import _testsinglephase + + expected = %d + init_count = _testsinglephase.initialized_count() + if init_count != expected: + raise Exception(init_count) + + lookedup = _testsinglephase.look_up_self() + if lookedup is not _testsinglephase: + raise Exception((_testsinglephase, lookedup)) + + # Attrs set in the module init func are in m_copy. + _initialized = _testsinglephase._initialized + initialized = _testsinglephase.initialized() + if _initialized != initialized: + raise Exception((_initialized, initialized)) + + # Attrs set after loading are not in m_copy. + if hasattr(_testsinglephase, 'spam'): + raise Exception(_testsinglephase.spam) + _testsinglephase.spam = expected + ''') + + # Use an interpreter that gets destroyed right away. + ret = support.run_in_subinterp(script % 1) + self.assertEqual(ret, 0) + + # The module's init func gets run again. + # The module's globals did not get destroyed. + _interpreters.run_string(interp1, script % 2) + + # The module's init func is not run again. + # The second interpreter copies the module's m_copy. + # However, globals are still shared. + _interpreters.run_string(interp2, script % 2) + @requires_load_dynamic def test_singlephase_variants(self): '''Exercise the most meaningful variants described in Python/import.c.''' @@ -260,6 +328,11 @@ def test_singlephase_variants(self): fileobj, pathname, _ = imp.find_module(basename) fileobj.close() + def clean_up(): + import _testsinglephase + _testsinglephase._clear_globals() + self.addCleanup(clean_up) + modules = {} def load(name): assert name not in modules diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index ba57719d92096b..632fac2de0c419 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -671,6 +671,20 @@ get_interp_settings(PyObject *self, PyObject *args) } +static PyObject * +clear_extension(PyObject *self, PyObject *args) +{ + PyObject *name = NULL, *filename = NULL; + if (!PyArg_ParseTuple(args, "OO:clear_extension", &name, &filename)) { + return NULL; + } + if (_PyImport_ClearExtension(name, filename) < 0) { + return NULL; + } + Py_RETURN_NONE; +} + + static PyMethodDef module_functions[] = { {"get_configs", get_configs, METH_NOARGS}, {"get_recursion_depth", get_recursion_depth, METH_NOARGS}, @@ -692,6 +706,7 @@ static PyMethodDef module_functions[] = { _TESTINTERNALCAPI_COMPILER_CODEGEN_METHODDEF _TESTINTERNALCAPI_OPTIMIZE_CFG_METHODDEF {"get_interp_settings", get_interp_settings, METH_VARARGS, NULL}, + {"clear_extension", clear_extension, METH_VARARGS, NULL}, {NULL, NULL} /* sentinel */ }; diff --git a/Modules/_testsinglephase.c b/Modules/_testsinglephase.c index 9e8dd647ee761a..565221c887e5ae 100644 --- a/Modules/_testsinglephase.c +++ b/Modules/_testsinglephase.c @@ -17,12 +17,27 @@ typedef struct { PyObject *str_const; } module_state; + /* Process-global state is only used by _testsinglephase since it's the only one that does not support re-init. */ static struct { int initialized_count; module_state module; -} global_state = { .initialized_count = -1 }; +} global_state = { + +#define NOT_INITIALIZED -1 + .initialized_count = NOT_INITIALIZED, +}; + +static void clear_state(module_state *state); + +static void +clear_global_state(void) +{ + clear_state(&global_state.module); + global_state.initialized_count = NOT_INITIALIZED; +} + static inline module_state * get_module_state(PyObject *module) @@ -106,6 +121,7 @@ init_state(module_state *state) return -1; } + static int init_module(PyObject *module, module_state *state) { @@ -118,6 +134,16 @@ init_module(PyObject *module, module_state *state) if (PyModule_AddObjectRef(module, "str_const", state->str_const) != 0) { return -1; } + + double d = _PyTime_AsSecondsDouble(state->initialized); + PyObject *initialized = PyFloat_FromDouble(d); + if (initialized == NULL) { + return -1; + } + if (PyModule_AddObjectRef(module, "_initialized", initialized) != 0) { + return -1; + } + return 0; } @@ -198,10 +224,28 @@ basic_initialized_count(PyObject *self, PyObject *Py_UNUSED(ignored)) } #define INITIALIZED_COUNT_METHODDEF \ - {"initialized_count", basic_initialized_count, METH_VARARGS, \ + {"initialized_count", basic_initialized_count, METH_NOARGS, \ basic_initialized_count_doc} +PyDoc_STRVAR(basic__clear_globals_doc, +"_clear_globals()\n\ +\n\ +Free all global state and set it to uninitialized."); + +static PyObject * +basic__clear_globals(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + assert(PyModule_GetDef(self)->m_size == -1); + clear_global_state(); + Py_RETURN_NONE; +} + +#define _CLEAR_GLOBALS_METHODDEF \ + {"_clear_globals", basic__clear_globals, METH_NOARGS, \ + basic__clear_globals_doc} + + /*********************************************/ /* the _testsinglephase module (and aliases) */ /*********************************************/ @@ -223,6 +267,7 @@ static PyMethodDef TestMethods_Basic[] = { SUM_METHODDEF, INITIALIZED_METHODDEF, INITIALIZED_COUNT_METHODDEF, + _CLEAR_GLOBALS_METHODDEF, {NULL, NULL} /* sentinel */ }; diff --git a/Python/import.c b/Python/import.c index ae27aaf56848d6..87981668a30505 100644 --- a/Python/import.c +++ b/Python/import.c @@ -632,6 +632,28 @@ exec_builtin_or_dynamic(PyObject *mod) { } +static int clear_singlephase_extension(PyInterpreterState *interp, + PyObject *name, PyObject *filename); + +// Currently, this is only used for testing. +// (See _testinternalcapi.clear_extension().) +int +_PyImport_ClearExtension(PyObject *name, PyObject *filename) +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); + + /* Clearing a module's C globals is up to the module. */ + if (clear_singlephase_extension(interp, name, filename) < 0) { + return -1; + } + + // In the future we'll probably also make sure the extension's + // file handle (and DL handle) is closed (requires saving it). + + return 0; +} + + /*******************/ #if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) @@ -766,8 +788,30 @@ _extensions_cache_set(PyObject *filename, PyObject *name, PyModuleDef *def) return 0; } +static int +_extensions_cache_delete(PyObject *filename, PyObject *name) +{ + PyObject *extensions = EXTENSIONS; + if (extensions == NULL) { + return 0; + } + PyObject *key = PyTuple_Pack(2, filename, name); + if (key == NULL) { + return -1; + } + if (PyDict_DelItem(extensions, key) < 0) { + if (!PyErr_ExceptionMatches(PyExc_KeyError)) { + Py_DECREF(key); + return -1; + } + PyErr_Clear(); + } + Py_DECREF(key); + return 0; +} + static void -_extensions_cache_clear(void) +_extensions_cache_clear_all(void) { Py_CLEAR(EXTENSIONS); } @@ -890,6 +934,34 @@ import_find_extension(PyThreadState *tstate, PyObject *name, return mod; } +static int +clear_singlephase_extension(PyInterpreterState *interp, + PyObject *name, PyObject *filename) +{ + PyModuleDef *def = _extensions_cache_get(filename, name); + if (def == NULL) { + if (PyErr_Occurred()) { + return -1; + } + return 0; + } + + /* Clear data set when the module was initially loaded. */ + def->m_base.m_init = NULL; + Py_CLEAR(def->m_base.m_copy); + // We leave m_index alone since there's no reason to reset it. + + /* Clear the PyState_*Module() cache entry. */ + if (_modules_by_index_check(interp, def->m_base.m_index) == NULL) { + if (_modules_by_index_clear(interp, def) < 0) { + return -1; + } + } + + /* Clear the cached module def. */ + return _extensions_cache_delete(filename, name); +} + /*******************/ /* builtin modules */ @@ -2633,7 +2705,7 @@ void _PyImport_Fini(void) { /* Destroy the database used by _PyImport_{Fixup,Find}Extension */ - _extensions_cache_clear(); + _extensions_cache_clear_all(); if (import_lock != NULL) { PyThread_free_lock(import_lock); import_lock = NULL; From 3dea4ba6c1b9237893d23574f931f33c940b74e8 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 15 Feb 2023 17:54:05 -0700 Subject: [PATCH 104/247] gh-101758: Fix the wasm Buildbots (gh-101943) They were broken by gh-101920. https://github.com/python/cpython/issues/101758 --- Lib/test/test_imp.py | 12 +++++++++++- Python/pystate.c | 4 ++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_imp.py b/Lib/test/test_imp.py index e81eb6f0a86fe8..5997ffad8e1232 100644 --- a/Lib/test/test_imp.py +++ b/Lib/test/test_imp.py @@ -16,12 +16,21 @@ imp = warnings_helper.import_deprecated('imp') import _imp import _testinternalcapi -import _xxsubinterpreters as _interpreters +try: + import _xxsubinterpreters as _interpreters +except ModuleNotFoundError: + _interpreters = None OS_PATH_NAME = os.path.__name__ +def requires_subinterpreters(meth): + """Decorator to skip a test if subinterpreters are not supported.""" + return unittest.skipIf(_interpreters is None, + 'subinterpreters required')(meth) + + def requires_load_dynamic(meth): """Decorator to skip a test if not running under CPython or lacking imp.load_dynamic().""" @@ -254,6 +263,7 @@ def test_issue16421_multiple_modules_in_one_dll(self): with self.assertRaises(ImportError): imp.load_dynamic('nonexistent', pathname) + @requires_subinterpreters @requires_load_dynamic def test_singlephase_multiple_interpreters(self): # Currently, for every single-phrase init module loaded diff --git a/Python/pystate.c b/Python/pystate.c index 4770caaed0a363..32b17fd19e348f 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -197,6 +197,7 @@ gilstate_tss_clear(_PyRuntimeState *runtime) } +#ifndef NDEBUG static inline int tstate_is_alive(PyThreadState *tstate); static inline int @@ -204,6 +205,7 @@ tstate_is_bound(PyThreadState *tstate) { return tstate->_status.bound && !tstate->_status.unbound; } +#endif // !NDEBUG static void bind_gilstate_tstate(PyThreadState *); static void unbind_gilstate_tstate(PyThreadState *); @@ -1119,6 +1121,7 @@ _PyInterpreterState_LookUpID(int64_t requested_id) /* the per-thread runtime state */ /********************************/ +#ifndef NDEBUG static inline int tstate_is_alive(PyThreadState *tstate) { @@ -1127,6 +1130,7 @@ tstate_is_alive(PyThreadState *tstate) !tstate->_status.cleared && !tstate->_status.finalizing); } +#endif //---------- From 89ac665891dec1988bedec2ce9b2c4d016502a49 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 15 Feb 2023 18:16:00 -0700 Subject: [PATCH 105/247] gh-98627: Add an Optional Check for Extension Module Subinterpreter Compatibility (gh-99040) Enforcing (optionally) the restriction set by PEP 489 makes sense. Furthermore, this sets the stage for a potential restriction related to a per-interpreter GIL. This change includes the following: * add tests for extension module subinterpreter compatibility * add _PyInterpreterConfig.check_multi_interp_extensions * add Py_RTFLAGS_MULTI_INTERP_EXTENSIONS * add _PyImport_CheckSubinterpIncompatibleExtensionAllowed() * fail iff the module does not implement multi-phase init and the current interpreter is configured to check https://github.com/python/cpython/issues/98627 --- Include/cpython/initconfig.h | 3 + Include/cpython/pystate.h | 3 + Include/internal/pycore_import.h | 5 + Lib/test/support/import_helper.py | 18 ++ Lib/test/test_capi/check_config.py | 77 ++++++ Lib/test/test_capi/test_misc.py | 98 +++++++- Lib/test/test_embed.py | 4 +- Lib/test/test_import/__init__.py | 220 +++++++++++++++++- Lib/test/test_threading.py | 1 + ...2-11-02-20-23-47.gh-issue-98627.VJkdRM.rst | 5 + Modules/_testcapimodule.c | 12 +- Python/clinic/import.c.h | 33 ++- Python/import.c | 88 ++++++- Python/importdl.c | 5 + Python/pylifecycle.c | 4 + 15 files changed, 557 insertions(+), 19 deletions(-) create mode 100644 Lib/test/test_capi/check_config.py create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-11-02-20-23-47.gh-issue-98627.VJkdRM.rst diff --git a/Include/cpython/initconfig.h b/Include/cpython/initconfig.h index 6ce42b4c09502f..a070fa9ff3a038 100644 --- a/Include/cpython/initconfig.h +++ b/Include/cpython/initconfig.h @@ -248,6 +248,7 @@ typedef struct { int allow_exec; int allow_threads; int allow_daemon_threads; + int check_multi_interp_extensions; } _PyInterpreterConfig; #define _PyInterpreterConfig_INIT \ @@ -256,6 +257,7 @@ typedef struct { .allow_exec = 0, \ .allow_threads = 1, \ .allow_daemon_threads = 0, \ + .check_multi_interp_extensions = 1, \ } #define _PyInterpreterConfig_LEGACY_INIT \ @@ -264,6 +266,7 @@ typedef struct { .allow_exec = 1, \ .allow_threads = 1, \ .allow_daemon_threads = 1, \ + .check_multi_interp_extensions = 0, \ } /* --- Helper functions --------------------------------------- */ diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index be1fcb61fa2244..3efb241e8237e7 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -11,6 +11,9 @@ is available in a given context. For example, forking the process might not be allowed in the current interpreter (i.e. os.fork() would fail). */ +/* Set if import should check a module for subinterpreter support. */ +#define Py_RTFLAGS_MULTI_INTERP_EXTENSIONS (1UL << 8) + /* Set if threads are allowed. */ #define Py_RTFLAGS_THREADS (1UL << 10) diff --git a/Include/internal/pycore_import.h b/Include/internal/pycore_import.h index 6ee7356b41c021..b7ffe01c0c0e20 100644 --- a/Include/internal/pycore_import.h +++ b/Include/internal/pycore_import.h @@ -64,6 +64,7 @@ struct _import_state { /* override for config->use_frozen_modules (for tests) (-1: "off", 1: "on", 0: no override) */ int override_frozen_modules; + int override_multi_interp_extensions_check; #ifdef HAVE_DLOPEN int dlopenflags; #endif @@ -153,6 +154,10 @@ PyAPI_DATA(const struct _frozen *) _PyImport_FrozenStdlib; PyAPI_DATA(const struct _frozen *) _PyImport_FrozenTest; extern const struct _module_alias * _PyImport_FrozenAliases; +PyAPI_FUNC(int) _PyImport_CheckSubinterpIncompatibleExtensionAllowed( + const char *name); + + // for testing PyAPI_FUNC(int) _PyImport_ClearExtension(PyObject *name, PyObject *filename); diff --git a/Lib/test/support/import_helper.py b/Lib/test/support/import_helper.py index 63a8a7952db7a6..772c0987c2ebef 100644 --- a/Lib/test/support/import_helper.py +++ b/Lib/test/support/import_helper.py @@ -105,6 +105,24 @@ def frozen_modules(enabled=True): _imp._override_frozen_modules_for_tests(0) +@contextlib.contextmanager +def multi_interp_extensions_check(enabled=True): + """Force legacy modules to be allowed in subinterpreters (or not). + + ("legacy" == single-phase init) + + This only applies to modules that haven't been imported yet. + It overrides the PyInterpreterConfig.check_multi_interp_extensions + setting (see support.run_in_subinterp_with_config() and + _xxsubinterpreters.create()). + """ + old = _imp._override_multi_interp_extensions_check(1 if enabled else -1) + try: + yield + finally: + _imp._override_multi_interp_extensions_check(old) + + def import_fresh_module(name, fresh=(), blocked=(), *, deprecated=False, usefrozen=False, diff --git a/Lib/test/test_capi/check_config.py b/Lib/test/test_capi/check_config.py new file mode 100644 index 00000000000000..aaedd82f39af50 --- /dev/null +++ b/Lib/test/test_capi/check_config.py @@ -0,0 +1,77 @@ +# This script is used by test_misc. + +import _imp +import _testinternalcapi +import json +import os +import sys + + +def import_singlephase(): + assert '_testsinglephase' not in sys.modules + try: + import _testsinglephase + except ImportError: + sys.modules.pop('_testsinglephase') + return False + else: + del sys.modules['_testsinglephase'] + return True + + +def check_singlephase(override): + # Check using the default setting. + settings_initial = _testinternalcapi.get_interp_settings() + allowed_initial = import_singlephase() + assert(_testinternalcapi.get_interp_settings() == settings_initial) + + # Apply the override and check. + override_initial = _imp._override_multi_interp_extensions_check(override) + settings_after = _testinternalcapi.get_interp_settings() + allowed_after = import_singlephase() + + # Apply the override again and check. + noop = {} + override_after = _imp._override_multi_interp_extensions_check(override) + settings_noop = _testinternalcapi.get_interp_settings() + if settings_noop != settings_after: + noop['settings_noop'] = settings_noop + allowed_noop = import_singlephase() + if allowed_noop != allowed_after: + noop['allowed_noop'] = allowed_noop + + # Restore the original setting and check. + override_noop = _imp._override_multi_interp_extensions_check(override_initial) + if override_noop != override_after: + noop['override_noop'] = override_noop + settings_restored = _testinternalcapi.get_interp_settings() + allowed_restored = import_singlephase() + + # Restore the original setting again. + override_restored = _imp._override_multi_interp_extensions_check(override_initial) + assert(_testinternalcapi.get_interp_settings() == settings_restored) + + return dict({ + 'requested': override, + 'override__initial': override_initial, + 'override_after': override_after, + 'override_restored': override_restored, + 'settings__initial': settings_initial, + 'settings_after': settings_after, + 'settings_restored': settings_restored, + 'allowed__initial': allowed_initial, + 'allowed_after': allowed_after, + 'allowed_restored': allowed_restored, + }, **noop) + + +def run_singlephase_check(override, outfd): + with os.fdopen(outfd, 'w') as outfile: + sys.stdout = outfile + sys.stderr = outfile + try: + results = check_singlephase(override) + json.dump(results, outfile) + finally: + sys.stdout = sys.__stdout__ + sys.stderr = sys.__stderr__ diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 7612cddb1f6576..f26b4723d1e68b 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -31,6 +31,10 @@ import _testmultiphase except ImportError: _testmultiphase = None +try: + import _testsinglephase +except ImportError: + _testsinglephase = None # Skip this test if the _testcapi module isn't available. _testcapi = import_helper.import_module('_testcapi') @@ -1297,17 +1301,20 @@ def test_configured_settings(self): """ import json + EXTENSIONS = 1<<8 THREADS = 1<<10 DAEMON_THREADS = 1<<11 FORK = 1<<15 EXEC = 1<<16 - features = ['fork', 'exec', 'threads', 'daemon_threads'] + features = ['fork', 'exec', 'threads', 'daemon_threads', 'extensions'] kwlist = [f'allow_{n}' for n in features] + kwlist[-1] = 'check_multi_interp_extensions' for config, expected in { - (True, True, True, True): FORK | EXEC | THREADS | DAEMON_THREADS, - (False, False, False, False): 0, - (False, False, True, False): THREADS, + (True, True, True, True, True): + FORK | EXEC | THREADS | DAEMON_THREADS | EXTENSIONS, + (False, False, False, False, False): 0, + (False, False, True, False, True): THREADS | EXTENSIONS, }.items(): kwargs = dict(zip(kwlist, config)) expected = { @@ -1322,12 +1329,93 @@ def test_configured_settings(self): json.dump(settings, stdin) ''') with os.fdopen(r) as stdout: - support.run_in_subinterp_with_config(script, **kwargs) + ret = support.run_in_subinterp_with_config(script, **kwargs) + self.assertEqual(ret, 0) out = stdout.read() settings = json.loads(out) self.assertEqual(settings, expected) + @unittest.skipIf(_testsinglephase is None, "test requires _testsinglephase module") + @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") + def test_overridden_setting_extensions_subinterp_check(self): + """ + PyInterpreterConfig.check_multi_interp_extensions can be overridden + with PyInterpreterState.override_multi_interp_extensions_check. + This verifies that the override works but does not modify + the underlying setting. + """ + import json + + EXTENSIONS = 1<<8 + THREADS = 1<<10 + DAEMON_THREADS = 1<<11 + FORK = 1<<15 + EXEC = 1<<16 + BASE_FLAGS = FORK | EXEC | THREADS | DAEMON_THREADS + base_kwargs = { + 'allow_fork': True, + 'allow_exec': True, + 'allow_threads': True, + 'allow_daemon_threads': True, + } + + def check(enabled, override): + kwargs = dict( + base_kwargs, + check_multi_interp_extensions=enabled, + ) + flags = BASE_FLAGS | EXTENSIONS if enabled else BASE_FLAGS + settings = { + 'feature_flags': flags, + } + + expected = { + 'requested': override, + 'override__initial': 0, + 'override_after': override, + 'override_restored': 0, + # The override should not affect the config or settings. + 'settings__initial': settings, + 'settings_after': settings, + 'settings_restored': settings, + # These are the most likely values to be wrong. + 'allowed__initial': not enabled, + 'allowed_after': not ((override > 0) if override else enabled), + 'allowed_restored': not enabled, + } + + r, w = os.pipe() + script = textwrap.dedent(f''' + from test.test_capi.check_config import run_singlephase_check + run_singlephase_check({override}, {w}) + ''') + with os.fdopen(r) as stdout: + ret = support.run_in_subinterp_with_config(script, **kwargs) + self.assertEqual(ret, 0) + out = stdout.read() + results = json.loads(out) + + self.assertEqual(results, expected) + + self.maxDiff = None + + # setting: check disabled + with self.subTest('config: check disabled; override: disabled'): + check(False, -1) + with self.subTest('config: check disabled; override: use config'): + check(False, 0) + with self.subTest('config: check disabled; override: enabled'): + check(False, 1) + + # setting: check enabled + with self.subTest('config: check enabled; override: disabled'): + check(True, -1) + with self.subTest('config: check enabled; override: use config'): + check(True, 0) + with self.subTest('config: check enabled; override: enabled'): + check(True, 1) + def test_mutate_exception(self): """ Exceptions saved in global module state get shared between diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index 4d422da5b99f44..e56d0db8627e91 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -1656,13 +1656,15 @@ def test_init_use_frozen_modules(self): api=API_PYTHON, env=env) def test_init_main_interpreter_settings(self): + EXTENSIONS = 1<<8 THREADS = 1<<10 DAEMON_THREADS = 1<<11 FORK = 1<<15 EXEC = 1<<16 expected = { # All optional features should be enabled. - 'feature_flags': FORK | EXEC | THREADS | DAEMON_THREADS, + 'feature_flags': + FORK | EXEC | THREADS | DAEMON_THREADS, } out, err = self.run_embedded_interpreter( 'test_init_main_interpreter_settings', diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index 1e4429ed7efe13..96815b3f758a5b 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -21,7 +21,7 @@ from test.support import os_helper from test.support import ( STDLIB_DIR, swap_attr, swap_item, cpython_only, is_emscripten, - is_wasi) + is_wasi, run_in_subinterp_with_config) from test.support.import_helper import ( forget, make_legacy_pyc, unlink, unload, DirsOnSysPath, CleanImport) from test.support.os_helper import ( @@ -30,6 +30,14 @@ from test.support import threading_helper from test.test_importlib.util import uncache from types import ModuleType +try: + import _testsinglephase +except ImportError: + _testsinglephase = None +try: + import _testmultiphase +except ImportError: + _testmultiphase = None skip_if_dont_write_bytecode = unittest.skipIf( @@ -1392,6 +1400,216 @@ def test_unwritable_module(self): unwritable.x = 42 +class SubinterpImportTests(unittest.TestCase): + + RUN_KWARGS = dict( + allow_fork=False, + allow_exec=False, + allow_threads=True, + allow_daemon_threads=False, + ) + + @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") + def pipe(self): + r, w = os.pipe() + self.addCleanup(os.close, r) + self.addCleanup(os.close, w) + if hasattr(os, 'set_blocking'): + os.set_blocking(r, False) + return (r, w) + + def import_script(self, name, fd, check_override=None): + override_text = '' + if check_override is not None: + override_text = f''' + import _imp + _imp._override_multi_interp_extensions_check({check_override}) + ''' + return textwrap.dedent(f''' + import os, sys + {override_text} + try: + import {name} + except ImportError as exc: + text = 'ImportError: ' + str(exc) + else: + text = 'okay' + os.write({fd}, text.encode('utf-8')) + ''') + + def run_shared(self, name, *, + check_singlephase_setting=False, + check_singlephase_override=None, + ): + """ + Try importing the named module in a subinterpreter. + + The subinterpreter will be in the current process. + The module will have already been imported in the main interpreter. + Thus, for extension/builtin modules, the module definition will + have been loaded already and cached globally. + + "check_singlephase_setting" determines whether or not + the interpreter will be configured to check for modules + that are not compatible with use in multiple interpreters. + + This should always return "okay" for all modules if the + setting is False (with no override). + """ + __import__(name) + + kwargs = dict( + **self.RUN_KWARGS, + check_multi_interp_extensions=check_singlephase_setting, + ) + + r, w = self.pipe() + script = self.import_script(name, w, check_singlephase_override) + + ret = run_in_subinterp_with_config(script, **kwargs) + self.assertEqual(ret, 0) + return os.read(r, 100) + + def check_compatible_shared(self, name, *, strict=False): + # Verify that the named module may be imported in a subinterpreter. + # (See run_shared() for more info.) + out = self.run_shared(name, check_singlephase_setting=strict) + self.assertEqual(out, b'okay') + + def check_incompatible_shared(self, name): + # Differences from check_compatible_shared(): + # * verify that import fails + # * "strict" is always True + out = self.run_shared(name, check_singlephase_setting=True) + self.assertEqual( + out.decode('utf-8'), + f'ImportError: module {name} does not support loading in subinterpreters', + ) + + def check_compatible_isolated(self, name, *, strict=False): + # Differences from check_compatible_shared(): + # * subinterpreter in a new process + # * module has never been imported before in that process + # * this tests importing the module for the first time + _, out, err = script_helper.assert_python_ok('-c', textwrap.dedent(f''' + import _testcapi, sys + assert ( + {name!r} in sys.builtin_module_names or + {name!r} not in sys.modules + ), repr({name!r}) + ret = _testcapi.run_in_subinterp_with_config( + {self.import_script(name, "sys.stdout.fileno()")!r}, + **{self.RUN_KWARGS}, + check_multi_interp_extensions={strict}, + ) + assert ret == 0, ret + ''')) + self.assertEqual(err, b'') + self.assertEqual(out, b'okay') + + def check_incompatible_isolated(self, name): + # Differences from check_compatible_isolated(): + # * verify that import fails + # * "strict" is always True + _, out, err = script_helper.assert_python_ok('-c', textwrap.dedent(f''' + import _testcapi, sys + assert {name!r} not in sys.modules, {name!r} + ret = _testcapi.run_in_subinterp_with_config( + {self.import_script(name, "sys.stdout.fileno()")!r}, + **{self.RUN_KWARGS}, + check_multi_interp_extensions=True, + ) + assert ret == 0, ret + ''')) + self.assertEqual(err, b'') + self.assertEqual( + out.decode('utf-8'), + f'ImportError: module {name} does not support loading in subinterpreters', + ) + + def test_builtin_compat(self): + module = 'sys' + with self.subTest(f'{module}: not strict'): + self.check_compatible_shared(module, strict=False) + with self.subTest(f'{module}: strict, shared'): + self.check_compatible_shared(module, strict=True) + + @cpython_only + def test_frozen_compat(self): + module = '_frozen_importlib' + if __import__(module).__spec__.origin != 'frozen': + raise unittest.SkipTest(f'{module} is unexpectedly not frozen') + with self.subTest(f'{module}: not strict'): + self.check_compatible_shared(module, strict=False) + with self.subTest(f'{module}: strict, shared'): + self.check_compatible_shared(module, strict=True) + + @unittest.skipIf(_testsinglephase is None, "test requires _testsinglephase module") + def test_single_init_extension_compat(self): + module = '_testsinglephase' + with self.subTest(f'{module}: not strict'): + self.check_compatible_shared(module, strict=False) + with self.subTest(f'{module}: strict, shared'): + self.check_incompatible_shared(module) + with self.subTest(f'{module}: strict, isolated'): + self.check_incompatible_isolated(module) + + @unittest.skipIf(_testmultiphase is None, "test requires _testmultiphase module") + def test_multi_init_extension_compat(self): + module = '_testmultiphase' + with self.subTest(f'{module}: not strict'): + self.check_compatible_shared(module, strict=False) + with self.subTest(f'{module}: strict, shared'): + self.check_compatible_shared(module, strict=True) + with self.subTest(f'{module}: strict, isolated'): + self.check_compatible_isolated(module, strict=True) + + def test_python_compat(self): + module = 'threading' + if __import__(module).__spec__.origin == 'frozen': + raise unittest.SkipTest(f'{module} is unexpectedly frozen') + with self.subTest(f'{module}: not strict'): + self.check_compatible_shared(module, strict=False) + with self.subTest(f'{module}: strict, shared'): + self.check_compatible_shared(module, strict=True) + with self.subTest(f'{module}: strict, isolated'): + self.check_compatible_isolated(module, strict=True) + + @unittest.skipIf(_testsinglephase is None, "test requires _testsinglephase module") + def test_singlephase_check_with_setting_and_override(self): + module = '_testsinglephase' + + def check_compatible(setting, override): + out = self.run_shared( + module, + check_singlephase_setting=setting, + check_singlephase_override=override, + ) + self.assertEqual(out, b'okay') + + def check_incompatible(setting, override): + out = self.run_shared( + module, + check_singlephase_setting=setting, + check_singlephase_override=override, + ) + self.assertNotEqual(out, b'okay') + + with self.subTest('config: check enabled; override: enabled'): + check_incompatible(True, 1) + with self.subTest('config: check enabled; override: use config'): + check_incompatible(True, 0) + with self.subTest('config: check enabled; override: disabled'): + check_compatible(True, -1) + + with self.subTest('config: check disabled; override: enabled'): + check_incompatible(False, 1) + with self.subTest('config: check disabled; override: use config'): + check_compatible(False, 0) + with self.subTest('config: check disabled; override: disabled'): + check_compatible(False, -1) + + if __name__ == '__main__': # Test needs to be a package, so we can do relative imports. unittest.main() diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 31bf46311a80dc..7fea2d38673eff 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -1347,6 +1347,7 @@ def func(): allow_exec=True, allow_threads={allowed}, allow_daemon_threads={daemon_allowed}, + check_multi_interp_extensions=False, ) """) with test.support.SuppressCrashReport(): diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-11-02-20-23-47.gh-issue-98627.VJkdRM.rst b/Misc/NEWS.d/next/Core and Builtins/2022-11-02-20-23-47.gh-issue-98627.VJkdRM.rst new file mode 100644 index 00000000000000..3d2d6f6eb0c41f --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-11-02-20-23-47.gh-issue-98627.VJkdRM.rst @@ -0,0 +1,5 @@ +When an interpreter is configured to check (and only then), importing an +extension module will now fail when the extension does not support multiple +interpreters (i.e. doesn't implement PEP 489 multi-phase init). This does +not apply to the main interpreter, nor to subinterpreters created with +``Py_NewInterpreter()``. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 5610a7689136f6..0d8d1d73fb2390 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -1618,6 +1618,7 @@ run_in_subinterp_with_config(PyObject *self, PyObject *args, PyObject *kwargs) int allow_exec = -1; int allow_threads = -1; int allow_daemon_threads = -1; + int check_multi_interp_extensions = -1; int r; PyThreadState *substate, *mainstate; /* only initialise 'cflags.cf_flags' to test backwards compatibility */ @@ -1628,11 +1629,13 @@ run_in_subinterp_with_config(PyObject *self, PyObject *args, PyObject *kwargs) "allow_exec", "allow_threads", "allow_daemon_threads", + "check_multi_interp_extensions", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, - "s$pppp:run_in_subinterp_with_config", kwlist, + "s$ppppp:run_in_subinterp_with_config", kwlist, &code, &allow_fork, &allow_exec, - &allow_threads, &allow_daemon_threads)) { + &allow_threads, &allow_daemon_threads, + &check_multi_interp_extensions)) { return NULL; } if (allow_fork < 0) { @@ -1651,6 +1654,10 @@ run_in_subinterp_with_config(PyObject *self, PyObject *args, PyObject *kwargs) PyErr_SetString(PyExc_ValueError, "missing allow_daemon_threads"); return NULL; } + if (check_multi_interp_extensions < 0) { + PyErr_SetString(PyExc_ValueError, "missing check_multi_interp_extensions"); + return NULL; + } mainstate = PyThreadState_Get(); @@ -1661,6 +1668,7 @@ run_in_subinterp_with_config(PyObject *self, PyObject *args, PyObject *kwargs) .allow_exec = allow_exec, .allow_threads = allow_threads, .allow_daemon_threads = allow_daemon_threads, + .check_multi_interp_extensions = check_multi_interp_extensions, }; substate = _Py_NewInterpreterFromConfig(&config); if (substate == NULL) { diff --git a/Python/clinic/import.c.h b/Python/clinic/import.c.h index 819fb1c75c15c3..cb74be6a422124 100644 --- a/Python/clinic/import.c.h +++ b/Python/clinic/import.c.h @@ -442,6 +442,37 @@ _imp__override_frozen_modules_for_tests(PyObject *module, PyObject *arg) return return_value; } +PyDoc_STRVAR(_imp__override_multi_interp_extensions_check__doc__, +"_override_multi_interp_extensions_check($module, override, /)\n" +"--\n" +"\n" +"(internal-only) Override PyInterpreterConfig.check_multi_interp_extensions.\n" +"\n" +"(-1: \"never\", 1: \"always\", 0: no override)"); + +#define _IMP__OVERRIDE_MULTI_INTERP_EXTENSIONS_CHECK_METHODDEF \ + {"_override_multi_interp_extensions_check", (PyCFunction)_imp__override_multi_interp_extensions_check, METH_O, _imp__override_multi_interp_extensions_check__doc__}, + +static PyObject * +_imp__override_multi_interp_extensions_check_impl(PyObject *module, + int override); + +static PyObject * +_imp__override_multi_interp_extensions_check(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + int override; + + override = _PyLong_AsInt(arg); + if (override == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = _imp__override_multi_interp_extensions_check_impl(module, override); + +exit: + return return_value; +} + #if defined(HAVE_DYNAMIC_LOADING) PyDoc_STRVAR(_imp_create_dynamic__doc__, @@ -617,4 +648,4 @@ _imp_source_hash(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb #ifndef _IMP_EXEC_DYNAMIC_METHODDEF #define _IMP_EXEC_DYNAMIC_METHODDEF #endif /* !defined(_IMP_EXEC_DYNAMIC_METHODDEF) */ -/*[clinic end generated code: output=806352838c3f7008 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=b18d46e0036eff49 input=a9049054013a1b77]*/ diff --git a/Python/import.c b/Python/import.c index 87981668a30505..ec126f28b85816 100644 --- a/Python/import.c +++ b/Python/import.c @@ -74,6 +74,8 @@ static struct _inittab *inittab_copy = NULL; (interp)->imports.modules_by_index #define IMPORTLIB(interp) \ (interp)->imports.importlib +#define OVERRIDE_MULTI_INTERP_EXTENSIONS_CHECK(interp) \ + (interp)->imports.override_multi_interp_extensions_check #define OVERRIDE_FROZEN_MODULES(interp) \ (interp)->imports.override_frozen_modules #ifdef HAVE_DLOPEN @@ -816,6 +818,38 @@ _extensions_cache_clear_all(void) Py_CLEAR(EXTENSIONS); } + +static bool +check_multi_interp_extensions(PyInterpreterState *interp) +{ + int override = OVERRIDE_MULTI_INTERP_EXTENSIONS_CHECK(interp); + if (override < 0) { + return false; + } + else if (override > 0) { + return true; + } + else if (_PyInterpreterState_HasFeature( + interp, Py_RTFLAGS_MULTI_INTERP_EXTENSIONS)) { + return true; + } + return false; +} + +int +_PyImport_CheckSubinterpIncompatibleExtensionAllowed(const char *name) +{ + PyInterpreterState *interp = _PyInterpreterState_Get(); + if (check_multi_interp_extensions(interp)) { + assert(!_Py_IsMainInterpreter(interp)); + PyErr_Format(PyExc_ImportError, + "module %s does not support loading in subinterpreters", + name); + return -1; + } + return 0; +} + static int fix_up_extension(PyObject *mod, PyObject *name, PyObject *filename) { @@ -3297,6 +3331,34 @@ _imp__override_frozen_modules_for_tests_impl(PyObject *module, int override) Py_RETURN_NONE; } +/*[clinic input] +_imp._override_multi_interp_extensions_check + + override: int + / + +(internal-only) Override PyInterpreterConfig.check_multi_interp_extensions. + +(-1: "never", 1: "always", 0: no override) +[clinic start generated code]*/ + +static PyObject * +_imp__override_multi_interp_extensions_check_impl(PyObject *module, + int override) +/*[clinic end generated code: output=3ff043af52bbf280 input=e086a2ea181f92ae]*/ +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); + if (_Py_IsMainInterpreter(interp)) { + PyErr_SetString(PyExc_RuntimeError, + "_imp._override_multi_interp_extensions_check() " + "cannot be used in the main interpreter"); + return NULL; + } + int oldvalue = OVERRIDE_MULTI_INTERP_EXTENSIONS_CHECK(interp); + OVERRIDE_MULTI_INTERP_EXTENSIONS_CHECK(interp) = override; + return PyLong_FromLong(oldvalue); +} + #ifdef HAVE_DYNAMIC_LOADING /*[clinic input] @@ -3329,18 +3391,23 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) PyThreadState *tstate = _PyThreadState_GET(); mod = import_find_extension(tstate, name, path); - if (mod != NULL || PyErr_Occurred()) { - Py_DECREF(name); - Py_DECREF(path); - return mod; + if (mod != NULL) { + const char *name_buf = PyUnicode_AsUTF8(name); + assert(name_buf != NULL); + if (_PyImport_CheckSubinterpIncompatibleExtensionAllowed(name_buf) < 0) { + Py_DECREF(mod); + mod = NULL; + } + goto finally; + } + else if (PyErr_Occurred()) { + goto finally; } if (file != NULL) { fp = _Py_fopen_obj(path, "r"); if (fp == NULL) { - Py_DECREF(name); - Py_DECREF(path); - return NULL; + goto finally; } } else @@ -3348,10 +3415,12 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) mod = _PyImport_LoadDynamicModuleWithSpec(spec, fp); - Py_DECREF(name); - Py_DECREF(path); if (fp) fclose(fp); + +finally: + Py_DECREF(name); + Py_DECREF(path); return mod; } @@ -3436,6 +3505,7 @@ static PyMethodDef imp_methods[] = { _IMP_IS_FROZEN_METHODDEF _IMP__FROZEN_MODULE_NAMES_METHODDEF _IMP__OVERRIDE_FROZEN_MODULES_FOR_TESTS_METHODDEF + _IMP__OVERRIDE_MULTI_INTERP_EXTENSIONS_CHECK_METHODDEF _IMP_CREATE_DYNAMIC_METHODDEF _IMP_EXEC_DYNAMIC_METHODDEF _IMP_EXEC_BUILTIN_METHODDEF diff --git a/Python/importdl.c b/Python/importdl.c index 6dafb4541486e9..3a3a30ddbdcdb5 100644 --- a/Python/importdl.c +++ b/Python/importdl.c @@ -3,6 +3,7 @@ #include "Python.h" #include "pycore_call.h" +#include "pycore_import.h" #include "pycore_pystate.h" #include "pycore_runtime.h" @@ -203,6 +204,10 @@ _PyImport_LoadDynamicModuleWithSpec(PyObject *spec, FILE *fp) /* Fall back to single-phase init mechanism */ + if (_PyImport_CheckSubinterpIncompatibleExtensionAllowed(name_buf) < 0) { + goto error; + } + if (hook_prefix == nonascii_prefix) { /* don't allow legacy init for non-ASCII module names */ PyErr_Format( diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 281035dafa9577..e80dd30c89dfd0 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -565,6 +565,10 @@ init_interp_settings(PyInterpreterState *interp, const _PyInterpreterConfig *con if (config->allow_daemon_threads) { interp->feature_flags |= Py_RTFLAGS_DAEMON_THREADS; } + + if (config->check_multi_interp_extensions) { + interp->feature_flags |= Py_RTFLAGS_MULTI_INTERP_EXTENSIONS; + } } From 0b13575e74ff3321364a3389eda6b4e92792afe1 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Wed, 15 Feb 2023 22:08:20 -0800 Subject: [PATCH 106/247] gh-99108: Refactor _sha256 & _sha512 into _sha2. (#101924) This merges their code. They're backed by the same single HACL* static library, having them be a single module simplifies maintenance. This should unbreak the wasm enscripten builds that currently fail due to linking in --whole-archive mode and the HACL* library appearing twice. Long unnoticed error fixed: _sha512.SHA384Type was doubly assigned and was actually SHA512Type. Nobody depends on those internal names. Also rename LIBHACL_ make vars to LIBHACL_SHA2_ in preperation for other future HACL things. --- Lib/hashlib.py | 12 +- Lib/test/test_hashlib.py | 22 +- Makefile.pre.in | 15 +- ...3-02-15-01-54-06.gh-issue-99108.rjTSic.rst | 3 + Modules/Setup | 3 +- Modules/Setup.stdlib.in | 3 +- Modules/clinic/sha256module.c.h | 225 ----- Modules/clinic/sha2module.c.h | 440 ++++++++++ Modules/clinic/sha512module.c.h | 225 ----- Modules/sha256module.c | 465 ---------- Modules/sha2module.c | 805 ++++++++++++++++++ Modules/sha512module.c | 456 ---------- PC/config.c | 6 +- PCbuild/pythoncore.vcxproj | 3 +- PCbuild/pythoncore.vcxproj.filters | 5 +- Python/stdlib_module_names.h | 3 +- configure | 96 +-- configure.ac | 16 +- 18 files changed, 1310 insertions(+), 1493 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-02-15-01-54-06.gh-issue-99108.rjTSic.rst delete mode 100644 Modules/clinic/sha256module.c.h create mode 100644 Modules/clinic/sha2module.c.h delete mode 100644 Modules/clinic/sha512module.c.h delete mode 100644 Modules/sha256module.c create mode 100644 Modules/sha2module.c delete mode 100644 Modules/sha512module.c diff --git a/Lib/hashlib.py b/Lib/hashlib.py index 21b5e912f3c771..1b16441cb60ba7 100644 --- a/Lib/hashlib.py +++ b/Lib/hashlib.py @@ -92,13 +92,13 @@ def __get_builtin_constructor(name): import _md5 cache['MD5'] = cache['md5'] = _md5.md5 elif name in {'SHA256', 'sha256', 'SHA224', 'sha224'}: - import _sha256 - cache['SHA224'] = cache['sha224'] = _sha256.sha224 - cache['SHA256'] = cache['sha256'] = _sha256.sha256 + import _sha2 + cache['SHA224'] = cache['sha224'] = _sha2.sha224 + cache['SHA256'] = cache['sha256'] = _sha2.sha256 elif name in {'SHA512', 'sha512', 'SHA384', 'sha384'}: - import _sha512 - cache['SHA384'] = cache['sha384'] = _sha512.sha384 - cache['SHA512'] = cache['sha512'] = _sha512.sha512 + import _sha2 + cache['SHA384'] = cache['sha384'] = _sha2.sha384 + cache['SHA512'] = cache['sha512'] = _sha2.sha512 elif name in {'blake2b', 'blake2s'}: import _blake2 cache['blake2b'] = _blake2.blake2b diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py index 9c92b4e9c280dc..5ead8857943592 100644 --- a/Lib/test/test_hashlib.py +++ b/Lib/test/test_hashlib.py @@ -1,6 +1,4 @@ -# Test hashlib module -# -# $Id$ +# Test the hashlib module. # # Copyright (C) 2005-2010 Gregory P. Smith (greg@krypto.org) # Licensed to PSF under a Contributor Agreement. @@ -28,7 +26,6 @@ from http.client import HTTPException -# default builtin hash module default_builtin_hashes = {'md5', 'sha1', 'sha256', 'sha512', 'sha3', 'blake2'} # --with-builtin-hashlib-hashes override builtin_hashes = sysconfig.get_config_var("PY_BUILTIN_HASHLIB_HASHES") @@ -66,6 +63,7 @@ def get_fips_mode(): requires_blake2 = unittest.skipUnless(_blake2, 'requires _blake2') # bpo-46913: Don't test the _sha3 extension on a Python UBSAN build +# TODO(gh-99108): Revisit this after _sha3 uses HACL*. SKIP_SHA3 = support.check_sanitizer(ub=True) requires_sha3 = unittest.skipUnless(not SKIP_SHA3, 'requires _sha3') @@ -107,7 +105,7 @@ class HashLibTestCase(unittest.TestCase): shakes = {'shake_128', 'shake_256'} - # Issue #14693: fallback modules are always compiled under POSIX + # gh-58898: Fallback modules are always compiled under POSIX. _warn_on_extension_import = (os.name == 'posix' or support.Py_DEBUG) def _conditional_import_module(self, module_name): @@ -116,7 +114,7 @@ def _conditional_import_module(self, module_name): return importlib.import_module(module_name) except ModuleNotFoundError as error: if self._warn_on_extension_import and module_name in builtin_hashes: - warnings.warn('Did a C extension fail to compile? %s' % error) + warnings.warn(f'Did a C extension fail to compile? {error}') return None def __init__(self, *args, **kwargs): @@ -147,7 +145,7 @@ def _test_algorithm_via_hashlib_new(data=None, _alg=algorithm, **kwargs): _hashlib = self._conditional_import_module('_hashlib') self._hashlib = _hashlib if _hashlib: - # These two algorithms should always be present when this module + # These algorithms should always be present when this module # is compiled. If not, something was compiled wrong. self.assertTrue(hasattr(_hashlib, 'openssl_md5')) self.assertTrue(hasattr(_hashlib, 'openssl_sha1')) @@ -172,12 +170,10 @@ def add_builtin_constructor(name): _sha1 = self._conditional_import_module('_sha1') if _sha1: add_builtin_constructor('sha1') - _sha256 = self._conditional_import_module('_sha256') - if _sha256: + _sha2 = self._conditional_import_module('_sha2') + if _sha2: add_builtin_constructor('sha224') add_builtin_constructor('sha256') - _sha512 = self._conditional_import_module('_sha512') - if _sha512: add_builtin_constructor('sha384') add_builtin_constructor('sha512') if _blake2: @@ -460,9 +456,9 @@ def check_blocksize_name(self, name, block_size=0, digest_size=0, self.assertEqual(len(m.hexdigest()), 2*digest_size) self.assertEqual(m.name, name) # split for sha3_512 / _sha3.sha3 object - self.assertIn(name.split("_")[0], repr(m)) + self.assertIn(name.split("_")[0], repr(m).lower()) - def test_blocksize_name(self): + def test_blocksize_and_name(self): self.check_blocksize_name('md5', 64, 16) self.check_blocksize_name('sha1', 64, 20) self.check_blocksize_name('sha224', 64, 28) diff --git a/Makefile.pre.in b/Makefile.pre.in index ce3fed3d648536..490483a712014c 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -207,7 +207,7 @@ ENSUREPIP= @ENSUREPIP@ # Internal static libraries LIBMPDEC_A= Modules/_decimal/libmpdec/libmpdec.a LIBEXPAT_A= Modules/expat/libexpat.a -LIBHACL_A= Modules/_hacl/libHacl_Streaming_SHA2.a +LIBHACL_SHA2_A= Modules/_hacl/libHacl_Streaming_SHA2.a # Module state, compiler flags and linker flags # Empty CFLAGS and LDFLAGS are omitted. @@ -575,10 +575,10 @@ LIBEXPAT_HEADERS= \ ########################################################################## # hashlib's HACL* library -LIBHACL_OBJS= \ +LIBHACL_SHA2_OBJS= \ Modules/_hacl/Hacl_Streaming_SHA2.o -LIBHACL_HEADERS= \ +LIBHACL_SHA2_HEADERS= \ Modules/_hacl/Hacl_Streaming_SHA2.h \ Modules/_hacl/include/krml/FStar_UInt128_Verified.h \ Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h \ @@ -912,12 +912,12 @@ $(LIBEXPAT_A): $(LIBEXPAT_OBJS) # Build HACL* static libraries for hashlib: libHacl_Streaming_SHA2.a LIBHACL_CFLAGS=-I$(srcdir)/Modules/_hacl/include -D_BSD_SOURCE -D_DEFAULT_SOURCE $(PY_STDMODULE_CFLAGS) $(CCSHARED) -Modules/_hacl/Hacl_Streaming_SHA2.o: $(srcdir)/Modules/_hacl/Hacl_Streaming_SHA2.c $(LIBHACL_HEADERS) +Modules/_hacl/Hacl_Streaming_SHA2.o: $(srcdir)/Modules/_hacl/Hacl_Streaming_SHA2.c $(LIBHACL_SHA2_HEADERS) $(CC) -c $(LIBHACL_CFLAGS) -o $@ $(srcdir)/Modules/_hacl/Hacl_Streaming_SHA2.c -$(LIBHACL_A): $(LIBHACL_OBJS) +$(LIBHACL_SHA2_A): $(LIBHACL_SHA2_OBJS) -rm -f $@ - $(AR) $(ARFLAGS) $@ $(LIBHACL_OBJS) + $(AR) $(ARFLAGS) $@ $(LIBHACL_SHA2_OBJS) # create relative links from build/lib.platform/egg.so to Modules/egg.so # pybuilddir.txt is created too late. We cannot use it in Makefile @@ -2635,9 +2635,8 @@ MODULE__HASHLIB_DEPS=$(srcdir)/Modules/hashlib.h MODULE__IO_DEPS=$(srcdir)/Modules/_io/_iomodule.h MODULE__MD5_DEPS=$(srcdir)/Modules/hashlib.h MODULE__SHA1_DEPS=$(srcdir)/Modules/hashlib.h -MODULE__SHA256_DEPS=$(srcdir)/Modules/hashlib.h $(LIBHACL_HEADERS) $(LIBHACL_A) +MODULE__SHA2_DEPS=$(srcdir)/Modules/hashlib.h $(LIBHACL_SHA2_HEADERS) $(LIBHACL_SHA2_A) MODULE__SHA3_DEPS=$(srcdir)/Modules/_sha3/sha3.c $(srcdir)/Modules/_sha3/sha3.h $(srcdir)/Modules/hashlib.h -MODULE__SHA512_DEPS=$(srcdir)/Modules/hashlib.h $(LIBHACL_HEADERS) $(LIBHACL_A) MODULE__SOCKET_DEPS=$(srcdir)/Modules/socketmodule.h $(srcdir)/Modules/addrinfo.h $(srcdir)/Modules/getaddrinfo.c $(srcdir)/Modules/getnameinfo.c MODULE__SSL_DEPS=$(srcdir)/Modules/_ssl.h $(srcdir)/Modules/_ssl/cert.c $(srcdir)/Modules/_ssl/debughelpers.c $(srcdir)/Modules/_ssl/misc.c $(srcdir)/Modules/_ssl_data.h $(srcdir)/Modules/_ssl_data_111.h $(srcdir)/Modules/_ssl_data_300.h $(srcdir)/Modules/socketmodule.h MODULE__TESTCAPI_DEPS=$(srcdir)/Modules/_testcapi/testcapi_long.h $(srcdir)/Modules/_testcapi/parts.h diff --git a/Misc/NEWS.d/next/Library/2023-02-15-01-54-06.gh-issue-99108.rjTSic.rst b/Misc/NEWS.d/next/Library/2023-02-15-01-54-06.gh-issue-99108.rjTSic.rst new file mode 100644 index 00000000000000..1612c89c0ea6be --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-15-01-54-06.gh-issue-99108.rjTSic.rst @@ -0,0 +1,3 @@ +The built-in extension modules for :mod:`hashlib` SHA2 algorithms, used when +OpenSSL does not provide them, now live in a single internal ``_sha2`` module +instead of separate ``_sha256`` and ``_sha512`` modules. diff --git a/Modules/Setup b/Modules/Setup index 428be0a1bf8fa1..1d5183bc2df118 100644 --- a/Modules/Setup +++ b/Modules/Setup @@ -165,8 +165,7 @@ PYTHONPATH=$(COREPYTHONPATH) #_blake2 _blake2/blake2module.c _blake2/blake2b_impl.c _blake2/blake2s_impl.c #_md5 md5module.c #_sha1 sha1module.c -#_sha256 sha256module.c -#_sha512 sha512module.c +#_sha2 sha2module.c -I$(srcdir)/Modules/_hacl/include Modules/_hacl/libHacl_Streaming_SHA2.a #_sha3 _sha3/sha3module.c # text encodings and unicode diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index 22bcb423db233f..8f5e14a4e80e22 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -79,8 +79,7 @@ # hashing builtins, can be disabled with --without-builtin-hashlib-hashes @MODULE__MD5_TRUE@_md5 md5module.c @MODULE__SHA1_TRUE@_sha1 sha1module.c -@MODULE__SHA256_TRUE@_sha256 sha256module.c -I$(srcdir)/Modules/_hacl/include Modules/_hacl/libHacl_Streaming_SHA2.a -@MODULE__SHA512_TRUE@_sha512 sha512module.c -I$(srcdir)/Modules/_hacl/include Modules/_hacl/libHacl_Streaming_SHA2.a +@MODULE__SHA2_TRUE@_sha2 sha2module.c -I$(srcdir)/Modules/_hacl/include Modules/_hacl/libHacl_Streaming_SHA2.a @MODULE__SHA3_TRUE@_sha3 _sha3/sha3module.c @MODULE__BLAKE2_TRUE@_blake2 _blake2/blake2module.c _blake2/blake2b_impl.c _blake2/blake2s_impl.c diff --git a/Modules/clinic/sha256module.c.h b/Modules/clinic/sha256module.c.h deleted file mode 100644 index 10d09fac695fc4..00000000000000 --- a/Modules/clinic/sha256module.c.h +++ /dev/null @@ -1,225 +0,0 @@ -/*[clinic input] -preserve -[clinic start generated code]*/ - -#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) -# include "pycore_gc.h" // PyGC_Head -# include "pycore_runtime.h" // _Py_ID() -#endif - - -PyDoc_STRVAR(SHA256Type_copy__doc__, -"copy($self, /)\n" -"--\n" -"\n" -"Return a copy of the hash object."); - -#define SHA256TYPE_COPY_METHODDEF \ - {"copy", _PyCFunction_CAST(SHA256Type_copy), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, SHA256Type_copy__doc__}, - -static PyObject * -SHA256Type_copy_impl(SHAobject *self, PyTypeObject *cls); - -static PyObject * -SHA256Type_copy(SHAobject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) -{ - if (nargs) { - PyErr_SetString(PyExc_TypeError, "copy() takes no arguments"); - return NULL; - } - return SHA256Type_copy_impl(self, cls); -} - -PyDoc_STRVAR(SHA256Type_digest__doc__, -"digest($self, /)\n" -"--\n" -"\n" -"Return the digest value as a bytes object."); - -#define SHA256TYPE_DIGEST_METHODDEF \ - {"digest", (PyCFunction)SHA256Type_digest, METH_NOARGS, SHA256Type_digest__doc__}, - -static PyObject * -SHA256Type_digest_impl(SHAobject *self); - -static PyObject * -SHA256Type_digest(SHAobject *self, PyObject *Py_UNUSED(ignored)) -{ - return SHA256Type_digest_impl(self); -} - -PyDoc_STRVAR(SHA256Type_hexdigest__doc__, -"hexdigest($self, /)\n" -"--\n" -"\n" -"Return the digest value as a string of hexadecimal digits."); - -#define SHA256TYPE_HEXDIGEST_METHODDEF \ - {"hexdigest", (PyCFunction)SHA256Type_hexdigest, METH_NOARGS, SHA256Type_hexdigest__doc__}, - -static PyObject * -SHA256Type_hexdigest_impl(SHAobject *self); - -static PyObject * -SHA256Type_hexdigest(SHAobject *self, PyObject *Py_UNUSED(ignored)) -{ - return SHA256Type_hexdigest_impl(self); -} - -PyDoc_STRVAR(SHA256Type_update__doc__, -"update($self, obj, /)\n" -"--\n" -"\n" -"Update this hash object\'s state with the provided string."); - -#define SHA256TYPE_UPDATE_METHODDEF \ - {"update", (PyCFunction)SHA256Type_update, METH_O, SHA256Type_update__doc__}, - -PyDoc_STRVAR(_sha256_sha256__doc__, -"sha256($module, /, string=b\'\', *, usedforsecurity=True)\n" -"--\n" -"\n" -"Return a new SHA-256 hash object; optionally initialized with a string."); - -#define _SHA256_SHA256_METHODDEF \ - {"sha256", _PyCFunction_CAST(_sha256_sha256), METH_FASTCALL|METH_KEYWORDS, _sha256_sha256__doc__}, - -static PyObject * -_sha256_sha256_impl(PyObject *module, PyObject *string, int usedforsecurity); - -static PyObject * -_sha256_sha256(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) -{ - PyObject *return_value = NULL; - #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) - - #define NUM_KEYWORDS 2 - static struct { - PyGC_Head _this_is_not_used; - PyObject_VAR_HEAD - PyObject *ob_item[NUM_KEYWORDS]; - } _kwtuple = { - .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) - .ob_item = { &_Py_ID(string), &_Py_ID(usedforsecurity), }, - }; - #undef NUM_KEYWORDS - #define KWTUPLE (&_kwtuple.ob_base.ob_base) - - #else // !Py_BUILD_CORE - # define KWTUPLE NULL - #endif // !Py_BUILD_CORE - - static const char * const _keywords[] = {"string", "usedforsecurity", NULL}; - static _PyArg_Parser _parser = { - .keywords = _keywords, - .fname = "sha256", - .kwtuple = KWTUPLE, - }; - #undef KWTUPLE - PyObject *argsbuf[2]; - Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; - PyObject *string = NULL; - int usedforsecurity = 1; - - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); - if (!args) { - goto exit; - } - if (!noptargs) { - goto skip_optional_pos; - } - if (args[0]) { - string = args[0]; - if (!--noptargs) { - goto skip_optional_pos; - } - } -skip_optional_pos: - if (!noptargs) { - goto skip_optional_kwonly; - } - usedforsecurity = PyObject_IsTrue(args[1]); - if (usedforsecurity < 0) { - goto exit; - } -skip_optional_kwonly: - return_value = _sha256_sha256_impl(module, string, usedforsecurity); - -exit: - return return_value; -} - -PyDoc_STRVAR(_sha256_sha224__doc__, -"sha224($module, /, string=b\'\', *, usedforsecurity=True)\n" -"--\n" -"\n" -"Return a new SHA-224 hash object; optionally initialized with a string."); - -#define _SHA256_SHA224_METHODDEF \ - {"sha224", _PyCFunction_CAST(_sha256_sha224), METH_FASTCALL|METH_KEYWORDS, _sha256_sha224__doc__}, - -static PyObject * -_sha256_sha224_impl(PyObject *module, PyObject *string, int usedforsecurity); - -static PyObject * -_sha256_sha224(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) -{ - PyObject *return_value = NULL; - #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) - - #define NUM_KEYWORDS 2 - static struct { - PyGC_Head _this_is_not_used; - PyObject_VAR_HEAD - PyObject *ob_item[NUM_KEYWORDS]; - } _kwtuple = { - .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) - .ob_item = { &_Py_ID(string), &_Py_ID(usedforsecurity), }, - }; - #undef NUM_KEYWORDS - #define KWTUPLE (&_kwtuple.ob_base.ob_base) - - #else // !Py_BUILD_CORE - # define KWTUPLE NULL - #endif // !Py_BUILD_CORE - - static const char * const _keywords[] = {"string", "usedforsecurity", NULL}; - static _PyArg_Parser _parser = { - .keywords = _keywords, - .fname = "sha224", - .kwtuple = KWTUPLE, - }; - #undef KWTUPLE - PyObject *argsbuf[2]; - Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; - PyObject *string = NULL; - int usedforsecurity = 1; - - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); - if (!args) { - goto exit; - } - if (!noptargs) { - goto skip_optional_pos; - } - if (args[0]) { - string = args[0]; - if (!--noptargs) { - goto skip_optional_pos; - } - } -skip_optional_pos: - if (!noptargs) { - goto skip_optional_kwonly; - } - usedforsecurity = PyObject_IsTrue(args[1]); - if (usedforsecurity < 0) { - goto exit; - } -skip_optional_kwonly: - return_value = _sha256_sha224_impl(module, string, usedforsecurity); - -exit: - return return_value; -} -/*[clinic end generated code: output=ae926f7ec85e7c97 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/sha2module.c.h b/Modules/clinic/sha2module.c.h new file mode 100644 index 00000000000000..8f855ca345e47a --- /dev/null +++ b/Modules/clinic/sha2module.c.h @@ -0,0 +1,440 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) +# include "pycore_gc.h" // PyGC_Head +# include "pycore_runtime.h" // _Py_ID() +#endif + + +PyDoc_STRVAR(SHA256Type_copy__doc__, +"copy($self, /)\n" +"--\n" +"\n" +"Return a copy of the hash object."); + +#define SHA256TYPE_COPY_METHODDEF \ + {"copy", _PyCFunction_CAST(SHA256Type_copy), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, SHA256Type_copy__doc__}, + +static PyObject * +SHA256Type_copy_impl(SHA256object *self, PyTypeObject *cls); + +static PyObject * +SHA256Type_copy(SHA256object *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + if (nargs) { + PyErr_SetString(PyExc_TypeError, "copy() takes no arguments"); + return NULL; + } + return SHA256Type_copy_impl(self, cls); +} + +PyDoc_STRVAR(SHA512Type_copy__doc__, +"copy($self, /)\n" +"--\n" +"\n" +"Return a copy of the hash object."); + +#define SHA512TYPE_COPY_METHODDEF \ + {"copy", _PyCFunction_CAST(SHA512Type_copy), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, SHA512Type_copy__doc__}, + +static PyObject * +SHA512Type_copy_impl(SHA512object *self, PyTypeObject *cls); + +static PyObject * +SHA512Type_copy(SHA512object *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + if (nargs) { + PyErr_SetString(PyExc_TypeError, "copy() takes no arguments"); + return NULL; + } + return SHA512Type_copy_impl(self, cls); +} + +PyDoc_STRVAR(SHA256Type_digest__doc__, +"digest($self, /)\n" +"--\n" +"\n" +"Return the digest value as a bytes object."); + +#define SHA256TYPE_DIGEST_METHODDEF \ + {"digest", (PyCFunction)SHA256Type_digest, METH_NOARGS, SHA256Type_digest__doc__}, + +static PyObject * +SHA256Type_digest_impl(SHA256object *self); + +static PyObject * +SHA256Type_digest(SHA256object *self, PyObject *Py_UNUSED(ignored)) +{ + return SHA256Type_digest_impl(self); +} + +PyDoc_STRVAR(SHA512Type_digest__doc__, +"digest($self, /)\n" +"--\n" +"\n" +"Return the digest value as a bytes object."); + +#define SHA512TYPE_DIGEST_METHODDEF \ + {"digest", (PyCFunction)SHA512Type_digest, METH_NOARGS, SHA512Type_digest__doc__}, + +static PyObject * +SHA512Type_digest_impl(SHA512object *self); + +static PyObject * +SHA512Type_digest(SHA512object *self, PyObject *Py_UNUSED(ignored)) +{ + return SHA512Type_digest_impl(self); +} + +PyDoc_STRVAR(SHA256Type_hexdigest__doc__, +"hexdigest($self, /)\n" +"--\n" +"\n" +"Return the digest value as a string of hexadecimal digits."); + +#define SHA256TYPE_HEXDIGEST_METHODDEF \ + {"hexdigest", (PyCFunction)SHA256Type_hexdigest, METH_NOARGS, SHA256Type_hexdigest__doc__}, + +static PyObject * +SHA256Type_hexdigest_impl(SHA256object *self); + +static PyObject * +SHA256Type_hexdigest(SHA256object *self, PyObject *Py_UNUSED(ignored)) +{ + return SHA256Type_hexdigest_impl(self); +} + +PyDoc_STRVAR(SHA512Type_hexdigest__doc__, +"hexdigest($self, /)\n" +"--\n" +"\n" +"Return the digest value as a string of hexadecimal digits."); + +#define SHA512TYPE_HEXDIGEST_METHODDEF \ + {"hexdigest", (PyCFunction)SHA512Type_hexdigest, METH_NOARGS, SHA512Type_hexdigest__doc__}, + +static PyObject * +SHA512Type_hexdigest_impl(SHA512object *self); + +static PyObject * +SHA512Type_hexdigest(SHA512object *self, PyObject *Py_UNUSED(ignored)) +{ + return SHA512Type_hexdigest_impl(self); +} + +PyDoc_STRVAR(SHA256Type_update__doc__, +"update($self, obj, /)\n" +"--\n" +"\n" +"Update this hash object\'s state with the provided string."); + +#define SHA256TYPE_UPDATE_METHODDEF \ + {"update", (PyCFunction)SHA256Type_update, METH_O, SHA256Type_update__doc__}, + +PyDoc_STRVAR(SHA512Type_update__doc__, +"update($self, obj, /)\n" +"--\n" +"\n" +"Update this hash object\'s state with the provided string."); + +#define SHA512TYPE_UPDATE_METHODDEF \ + {"update", (PyCFunction)SHA512Type_update, METH_O, SHA512Type_update__doc__}, + +PyDoc_STRVAR(_sha2_sha256__doc__, +"sha256($module, /, string=b\'\', *, usedforsecurity=True)\n" +"--\n" +"\n" +"Return a new SHA-256 hash object; optionally initialized with a string."); + +#define _SHA2_SHA256_METHODDEF \ + {"sha256", _PyCFunction_CAST(_sha2_sha256), METH_FASTCALL|METH_KEYWORDS, _sha2_sha256__doc__}, + +static PyObject * +_sha2_sha256_impl(PyObject *module, PyObject *string, int usedforsecurity); + +static PyObject * +_sha2_sha256(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(string), &_Py_ID(usedforsecurity), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"string", "usedforsecurity", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "sha256", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *string = NULL; + int usedforsecurity = 1; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + string = args[0]; + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: + if (!noptargs) { + goto skip_optional_kwonly; + } + usedforsecurity = PyObject_IsTrue(args[1]); + if (usedforsecurity < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _sha2_sha256_impl(module, string, usedforsecurity); + +exit: + return return_value; +} + +PyDoc_STRVAR(_sha2_sha224__doc__, +"sha224($module, /, string=b\'\', *, usedforsecurity=True)\n" +"--\n" +"\n" +"Return a new SHA-224 hash object; optionally initialized with a string."); + +#define _SHA2_SHA224_METHODDEF \ + {"sha224", _PyCFunction_CAST(_sha2_sha224), METH_FASTCALL|METH_KEYWORDS, _sha2_sha224__doc__}, + +static PyObject * +_sha2_sha224_impl(PyObject *module, PyObject *string, int usedforsecurity); + +static PyObject * +_sha2_sha224(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(string), &_Py_ID(usedforsecurity), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"string", "usedforsecurity", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "sha224", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *string = NULL; + int usedforsecurity = 1; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + string = args[0]; + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: + if (!noptargs) { + goto skip_optional_kwonly; + } + usedforsecurity = PyObject_IsTrue(args[1]); + if (usedforsecurity < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _sha2_sha224_impl(module, string, usedforsecurity); + +exit: + return return_value; +} + +PyDoc_STRVAR(_sha2_sha512__doc__, +"sha512($module, /, string=b\'\', *, usedforsecurity=True)\n" +"--\n" +"\n" +"Return a new SHA-512 hash object; optionally initialized with a string."); + +#define _SHA2_SHA512_METHODDEF \ + {"sha512", _PyCFunction_CAST(_sha2_sha512), METH_FASTCALL|METH_KEYWORDS, _sha2_sha512__doc__}, + +static PyObject * +_sha2_sha512_impl(PyObject *module, PyObject *string, int usedforsecurity); + +static PyObject * +_sha2_sha512(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(string), &_Py_ID(usedforsecurity), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"string", "usedforsecurity", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "sha512", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *string = NULL; + int usedforsecurity = 1; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + string = args[0]; + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: + if (!noptargs) { + goto skip_optional_kwonly; + } + usedforsecurity = PyObject_IsTrue(args[1]); + if (usedforsecurity < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _sha2_sha512_impl(module, string, usedforsecurity); + +exit: + return return_value; +} + +PyDoc_STRVAR(_sha2_sha384__doc__, +"sha384($module, /, string=b\'\', *, usedforsecurity=True)\n" +"--\n" +"\n" +"Return a new SHA-384 hash object; optionally initialized with a string."); + +#define _SHA2_SHA384_METHODDEF \ + {"sha384", _PyCFunction_CAST(_sha2_sha384), METH_FASTCALL|METH_KEYWORDS, _sha2_sha384__doc__}, + +static PyObject * +_sha2_sha384_impl(PyObject *module, PyObject *string, int usedforsecurity); + +static PyObject * +_sha2_sha384(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(string), &_Py_ID(usedforsecurity), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"string", "usedforsecurity", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "sha384", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *string = NULL; + int usedforsecurity = 1; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + string = args[0]; + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: + if (!noptargs) { + goto skip_optional_kwonly; + } + usedforsecurity = PyObject_IsTrue(args[1]); + if (usedforsecurity < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _sha2_sha384_impl(module, string, usedforsecurity); + +exit: + return return_value; +} +/*[clinic end generated code: output=f81dacb48f3fee72 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/sha512module.c.h b/Modules/clinic/sha512module.c.h deleted file mode 100644 index f8d326363c398e..00000000000000 --- a/Modules/clinic/sha512module.c.h +++ /dev/null @@ -1,225 +0,0 @@ -/*[clinic input] -preserve -[clinic start generated code]*/ - -#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) -# include "pycore_gc.h" // PyGC_Head -# include "pycore_runtime.h" // _Py_ID() -#endif - - -PyDoc_STRVAR(SHA512Type_copy__doc__, -"copy($self, /)\n" -"--\n" -"\n" -"Return a copy of the hash object."); - -#define SHA512TYPE_COPY_METHODDEF \ - {"copy", _PyCFunction_CAST(SHA512Type_copy), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, SHA512Type_copy__doc__}, - -static PyObject * -SHA512Type_copy_impl(SHAobject *self, PyTypeObject *cls); - -static PyObject * -SHA512Type_copy(SHAobject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) -{ - if (nargs) { - PyErr_SetString(PyExc_TypeError, "copy() takes no arguments"); - return NULL; - } - return SHA512Type_copy_impl(self, cls); -} - -PyDoc_STRVAR(SHA512Type_digest__doc__, -"digest($self, /)\n" -"--\n" -"\n" -"Return the digest value as a bytes object."); - -#define SHA512TYPE_DIGEST_METHODDEF \ - {"digest", (PyCFunction)SHA512Type_digest, METH_NOARGS, SHA512Type_digest__doc__}, - -static PyObject * -SHA512Type_digest_impl(SHAobject *self); - -static PyObject * -SHA512Type_digest(SHAobject *self, PyObject *Py_UNUSED(ignored)) -{ - return SHA512Type_digest_impl(self); -} - -PyDoc_STRVAR(SHA512Type_hexdigest__doc__, -"hexdigest($self, /)\n" -"--\n" -"\n" -"Return the digest value as a string of hexadecimal digits."); - -#define SHA512TYPE_HEXDIGEST_METHODDEF \ - {"hexdigest", (PyCFunction)SHA512Type_hexdigest, METH_NOARGS, SHA512Type_hexdigest__doc__}, - -static PyObject * -SHA512Type_hexdigest_impl(SHAobject *self); - -static PyObject * -SHA512Type_hexdigest(SHAobject *self, PyObject *Py_UNUSED(ignored)) -{ - return SHA512Type_hexdigest_impl(self); -} - -PyDoc_STRVAR(SHA512Type_update__doc__, -"update($self, obj, /)\n" -"--\n" -"\n" -"Update this hash object\'s state with the provided string."); - -#define SHA512TYPE_UPDATE_METHODDEF \ - {"update", (PyCFunction)SHA512Type_update, METH_O, SHA512Type_update__doc__}, - -PyDoc_STRVAR(_sha512_sha512__doc__, -"sha512($module, /, string=b\'\', *, usedforsecurity=True)\n" -"--\n" -"\n" -"Return a new SHA-512 hash object; optionally initialized with a string."); - -#define _SHA512_SHA512_METHODDEF \ - {"sha512", _PyCFunction_CAST(_sha512_sha512), METH_FASTCALL|METH_KEYWORDS, _sha512_sha512__doc__}, - -static PyObject * -_sha512_sha512_impl(PyObject *module, PyObject *string, int usedforsecurity); - -static PyObject * -_sha512_sha512(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) -{ - PyObject *return_value = NULL; - #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) - - #define NUM_KEYWORDS 2 - static struct { - PyGC_Head _this_is_not_used; - PyObject_VAR_HEAD - PyObject *ob_item[NUM_KEYWORDS]; - } _kwtuple = { - .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) - .ob_item = { &_Py_ID(string), &_Py_ID(usedforsecurity), }, - }; - #undef NUM_KEYWORDS - #define KWTUPLE (&_kwtuple.ob_base.ob_base) - - #else // !Py_BUILD_CORE - # define KWTUPLE NULL - #endif // !Py_BUILD_CORE - - static const char * const _keywords[] = {"string", "usedforsecurity", NULL}; - static _PyArg_Parser _parser = { - .keywords = _keywords, - .fname = "sha512", - .kwtuple = KWTUPLE, - }; - #undef KWTUPLE - PyObject *argsbuf[2]; - Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; - PyObject *string = NULL; - int usedforsecurity = 1; - - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); - if (!args) { - goto exit; - } - if (!noptargs) { - goto skip_optional_pos; - } - if (args[0]) { - string = args[0]; - if (!--noptargs) { - goto skip_optional_pos; - } - } -skip_optional_pos: - if (!noptargs) { - goto skip_optional_kwonly; - } - usedforsecurity = PyObject_IsTrue(args[1]); - if (usedforsecurity < 0) { - goto exit; - } -skip_optional_kwonly: - return_value = _sha512_sha512_impl(module, string, usedforsecurity); - -exit: - return return_value; -} - -PyDoc_STRVAR(_sha512_sha384__doc__, -"sha384($module, /, string=b\'\', *, usedforsecurity=True)\n" -"--\n" -"\n" -"Return a new SHA-384 hash object; optionally initialized with a string."); - -#define _SHA512_SHA384_METHODDEF \ - {"sha384", _PyCFunction_CAST(_sha512_sha384), METH_FASTCALL|METH_KEYWORDS, _sha512_sha384__doc__}, - -static PyObject * -_sha512_sha384_impl(PyObject *module, PyObject *string, int usedforsecurity); - -static PyObject * -_sha512_sha384(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) -{ - PyObject *return_value = NULL; - #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) - - #define NUM_KEYWORDS 2 - static struct { - PyGC_Head _this_is_not_used; - PyObject_VAR_HEAD - PyObject *ob_item[NUM_KEYWORDS]; - } _kwtuple = { - .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) - .ob_item = { &_Py_ID(string), &_Py_ID(usedforsecurity), }, - }; - #undef NUM_KEYWORDS - #define KWTUPLE (&_kwtuple.ob_base.ob_base) - - #else // !Py_BUILD_CORE - # define KWTUPLE NULL - #endif // !Py_BUILD_CORE - - static const char * const _keywords[] = {"string", "usedforsecurity", NULL}; - static _PyArg_Parser _parser = { - .keywords = _keywords, - .fname = "sha384", - .kwtuple = KWTUPLE, - }; - #undef KWTUPLE - PyObject *argsbuf[2]; - Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; - PyObject *string = NULL; - int usedforsecurity = 1; - - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); - if (!args) { - goto exit; - } - if (!noptargs) { - goto skip_optional_pos; - } - if (args[0]) { - string = args[0]; - if (!--noptargs) { - goto skip_optional_pos; - } - } -skip_optional_pos: - if (!noptargs) { - goto skip_optional_kwonly; - } - usedforsecurity = PyObject_IsTrue(args[1]); - if (usedforsecurity < 0) { - goto exit; - } -skip_optional_kwonly: - return_value = _sha512_sha384_impl(module, string, usedforsecurity); - -exit: - return return_value; -} -/*[clinic end generated code: output=dd168f3f21097afe input=a9049054013a1b77]*/ diff --git a/Modules/sha256module.c b/Modules/sha256module.c deleted file mode 100644 index 301c9837bb6720..00000000000000 --- a/Modules/sha256module.c +++ /dev/null @@ -1,465 +0,0 @@ -/* SHA256 module */ - -/* This module provides an interface to NIST's SHA-256 and SHA-224 Algorithms */ - -/* See below for information about the original code this module was - based upon. Additional work performed by: - - Andrew Kuchling (amk@amk.ca) - Greg Stein (gstein@lyra.org) - Trevor Perrin (trevp@trevp.net) - Jonathan Protzenko (jonathan@protzenko.fr) - - Copyright (C) 2005-2007 Gregory P. Smith (greg@krypto.org) - Licensed to PSF under a Contributor Agreement. - -*/ - -/* SHA objects */ -#ifndef Py_BUILD_CORE_BUILTIN -# define Py_BUILD_CORE_MODULE 1 -#endif - -#include "Python.h" -#include "pycore_bitutils.h" // _Py_bswap32() -#include "pycore_strhex.h" // _Py_strhex() -#include "structmember.h" // PyMemberDef -#include "hashlib.h" - -/*[clinic input] -module _sha256 -class SHA256Type "SHAobject *" "&PyType_Type" -[clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=71a39174d4f0a744]*/ - - -/* The SHA block size and maximum message digest sizes, in bytes */ - -#define SHA_BLOCKSIZE 64 -#define SHA_DIGESTSIZE 32 - -/* The SHA2-224 and SHA2-256 implementations defer to the HACL* verified - * library. */ - -#include "_hacl/Hacl_Streaming_SHA2.h" - -typedef struct { - PyObject_HEAD - // Even though one could conceivably perform run-type checks to tell apart a - // sha224_type from a sha256_type (and thus deduce the digest size), we must - // keep this field because it's exposed as a member field on the underlying - // python object. - // TODO: could we transform this into a getter and get rid of the redundant - // field? - int digestsize; - Hacl_Streaming_SHA2_state_sha2_256 *state; -} SHAobject; - -#include "clinic/sha256module.c.h" - -/* We shall use run-time type information in the remainder of this module to - * tell apart SHA2-224 and SHA2-256 */ -typedef struct { - PyTypeObject* sha224_type; - PyTypeObject* sha256_type; -} _sha256_state; - -static inline _sha256_state* -_sha256_get_state(PyObject *module) -{ - void *state = PyModule_GetState(module); - assert(state != NULL); - return (_sha256_state *)state; -} - -static void SHAcopy(SHAobject *src, SHAobject *dest) -{ - dest->digestsize = src->digestsize; - dest->state = Hacl_Streaming_SHA2_copy_256(src->state); -} - -static SHAobject * -newSHA224object(_sha256_state *state) -{ - SHAobject *sha = (SHAobject *)PyObject_GC_New(SHAobject, - state->sha224_type); - PyObject_GC_Track(sha); - return sha; -} - -static SHAobject * -newSHA256object(_sha256_state *state) -{ - SHAobject *sha = (SHAobject *)PyObject_GC_New(SHAobject, - state->sha256_type); - PyObject_GC_Track(sha); - return sha; -} - -/* Internal methods for a hash object */ -static int -SHA_traverse(PyObject *ptr, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(ptr)); - return 0; -} - -static void -SHA_dealloc(SHAobject *ptr) -{ - Hacl_Streaming_SHA2_free_256(ptr->state); - PyTypeObject *tp = Py_TYPE(ptr); - PyObject_GC_UnTrack(ptr); - PyObject_GC_Del(ptr); - Py_DECREF(tp); -} - -/* HACL* takes a uint32_t for the length of its parameter, but Py_ssize_t can be - * 64 bits. */ -static void update_256(Hacl_Streaming_SHA2_state_sha2_256 *state, uint8_t *buf, Py_ssize_t len) { - /* Note: we explicitly ignore the error code on the basis that it would take > - * 1 billion years to overflow the maximum admissible length for SHA2-256 - * (namely, 2^61-1 bytes). */ - while (len > UINT32_MAX) { - Hacl_Streaming_SHA2_update_256(state, buf, UINT32_MAX); - len -= UINT32_MAX; - buf += UINT32_MAX; - } - /* Cast to uint32_t is safe: upon exiting the loop, len <= UINT32_MAX, and - * therefore fits in a uint32_t */ - Hacl_Streaming_SHA2_update_256(state, buf, (uint32_t) len); -} - - -/* External methods for a hash object */ - -/*[clinic input] -SHA256Type.copy - - cls:defining_class - -Return a copy of the hash object. -[clinic start generated code]*/ - -static PyObject * -SHA256Type_copy_impl(SHAobject *self, PyTypeObject *cls) -/*[clinic end generated code: output=9273f92c382be12f input=3137146fcb88e212]*/ -{ - SHAobject *newobj; - _sha256_state *state = PyType_GetModuleState(cls); - if (Py_IS_TYPE(self, state->sha256_type)) { - if ( (newobj = newSHA256object(state)) == NULL) { - return NULL; - } - } else { - if ( (newobj = newSHA224object(state))==NULL) { - return NULL; - } - } - - SHAcopy(self, newobj); - return (PyObject *)newobj; -} - -/*[clinic input] -SHA256Type.digest - -Return the digest value as a bytes object. -[clinic start generated code]*/ - -static PyObject * -SHA256Type_digest_impl(SHAobject *self) -/*[clinic end generated code: output=46616a5e909fbc3d input=f1f4cfea5cbde35c]*/ -{ - uint8_t digest[SHA_DIGESTSIZE]; - // HACL performs copies under the hood so that self->state remains valid - // after this call. - Hacl_Streaming_SHA2_finish_256(self->state, digest); - return PyBytes_FromStringAndSize((const char *)digest, self->digestsize); -} - -/*[clinic input] -SHA256Type.hexdigest - -Return the digest value as a string of hexadecimal digits. -[clinic start generated code]*/ - -static PyObject * -SHA256Type_hexdigest_impl(SHAobject *self) -/*[clinic end generated code: output=725f8a7041ae97f3 input=0cc4c714693010d1]*/ -{ - uint8_t digest[SHA_DIGESTSIZE]; - Hacl_Streaming_SHA2_finish_256(self->state, digest); - return _Py_strhex((const char *)digest, self->digestsize); -} - -/*[clinic input] -SHA256Type.update - - obj: object - / - -Update this hash object's state with the provided string. -[clinic start generated code]*/ - -static PyObject * -SHA256Type_update(SHAobject *self, PyObject *obj) -/*[clinic end generated code: output=0967fb2860c66af7 input=b2d449d5b30f0f5a]*/ -{ - Py_buffer buf; - - GET_BUFFER_VIEW_OR_ERROUT(obj, &buf); - - update_256(self->state, buf.buf, buf.len); - - PyBuffer_Release(&buf); - Py_RETURN_NONE; -} - -static PyMethodDef SHA_methods[] = { - SHA256TYPE_COPY_METHODDEF - SHA256TYPE_DIGEST_METHODDEF - SHA256TYPE_HEXDIGEST_METHODDEF - SHA256TYPE_UPDATE_METHODDEF - {NULL, NULL} /* sentinel */ -}; - -static PyObject * -SHA256_get_block_size(PyObject *self, void *closure) -{ - return PyLong_FromLong(SHA_BLOCKSIZE); -} - -static PyObject * -SHA256_get_name(SHAobject *self, void *closure) -{ - if (self->digestsize == 28) { - return PyUnicode_FromStringAndSize("sha224", 6); - } - return PyUnicode_FromStringAndSize("sha256", 6); -} - -static PyGetSetDef SHA_getseters[] = { - {"block_size", - (getter)SHA256_get_block_size, NULL, - NULL, - NULL}, - {"name", - (getter)SHA256_get_name, NULL, - NULL, - NULL}, - {NULL} /* Sentinel */ -}; - -static PyMemberDef SHA_members[] = { - {"digest_size", T_INT, offsetof(SHAobject, digestsize), READONLY, NULL}, - {NULL} /* Sentinel */ -}; - -static PyType_Slot sha256_types_slots[] = { - {Py_tp_dealloc, SHA_dealloc}, - {Py_tp_methods, SHA_methods}, - {Py_tp_members, SHA_members}, - {Py_tp_getset, SHA_getseters}, - {Py_tp_traverse, SHA_traverse}, - {0,0} -}; - -static PyType_Spec sha224_type_spec = { - .name = "_sha256.sha224", - .basicsize = sizeof(SHAobject), - .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | - Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), - .slots = sha256_types_slots -}; - -static PyType_Spec sha256_type_spec = { - .name = "_sha256.sha256", - .basicsize = sizeof(SHAobject), - .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | - Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), - .slots = sha256_types_slots -}; - -/* The single module-level function: new() */ - -/*[clinic input] -_sha256.sha256 - - string: object(c_default="NULL") = b'' - * - usedforsecurity: bool = True - -Return a new SHA-256 hash object; optionally initialized with a string. -[clinic start generated code]*/ - -static PyObject * -_sha256_sha256_impl(PyObject *module, PyObject *string, int usedforsecurity) -/*[clinic end generated code: output=a1de327e8e1185cf input=9be86301aeb14ea5]*/ -{ - Py_buffer buf; - - if (string) { - GET_BUFFER_VIEW_OR_ERROUT(string, &buf); - } - - _sha256_state *state = PyModule_GetState(module); - - SHAobject *new; - if ((new = newSHA256object(state)) == NULL) { - if (string) { - PyBuffer_Release(&buf); - } - return NULL; - } - - new->state = Hacl_Streaming_SHA2_create_in_256(); - new->digestsize = 32; - - if (PyErr_Occurred()) { - Py_DECREF(new); - if (string) { - PyBuffer_Release(&buf); - } - return NULL; - } - if (string) { - update_256(new->state, buf.buf, buf.len); - PyBuffer_Release(&buf); - } - - return (PyObject *)new; -} - -/*[clinic input] -_sha256.sha224 - - string: object(c_default="NULL") = b'' - * - usedforsecurity: bool = True - -Return a new SHA-224 hash object; optionally initialized with a string. -[clinic start generated code]*/ - -static PyObject * -_sha256_sha224_impl(PyObject *module, PyObject *string, int usedforsecurity) -/*[clinic end generated code: output=08be6b36569bc69c input=9fcfb46e460860ac]*/ -{ - Py_buffer buf; - if (string) { - GET_BUFFER_VIEW_OR_ERROUT(string, &buf); - } - - _sha256_state *state = PyModule_GetState(module); - SHAobject *new; - if ((new = newSHA224object(state)) == NULL) { - if (string) { - PyBuffer_Release(&buf); - } - return NULL; - } - - new->state = Hacl_Streaming_SHA2_create_in_224(); - new->digestsize = 28; - - if (PyErr_Occurred()) { - Py_DECREF(new); - if (string) { - PyBuffer_Release(&buf); - } - return NULL; - } - if (string) { - update_256(new->state, buf.buf, buf.len); - PyBuffer_Release(&buf); - } - - return (PyObject *)new; -} - - -/* List of functions exported by this module */ - -static struct PyMethodDef SHA_functions[] = { - _SHA256_SHA256_METHODDEF - _SHA256_SHA224_METHODDEF - {NULL, NULL} /* Sentinel */ -}; - -static int -_sha256_traverse(PyObject *module, visitproc visit, void *arg) -{ - _sha256_state *state = _sha256_get_state(module); - Py_VISIT(state->sha224_type); - Py_VISIT(state->sha256_type); - return 0; -} - -static int -_sha256_clear(PyObject *module) -{ - _sha256_state *state = _sha256_get_state(module); - Py_CLEAR(state->sha224_type); - Py_CLEAR(state->sha256_type); - return 0; -} - -static void -_sha256_free(void *module) -{ - _sha256_clear((PyObject *)module); -} - -static int sha256_exec(PyObject *module) -{ - _sha256_state *state = _sha256_get_state(module); - - state->sha224_type = (PyTypeObject *)PyType_FromModuleAndSpec( - module, &sha224_type_spec, NULL); - - if (state->sha224_type == NULL) { - return -1; - } - - state->sha256_type = (PyTypeObject *)PyType_FromModuleAndSpec( - module, &sha256_type_spec, NULL); - - if (state->sha256_type == NULL) { - return -1; - } - - Py_INCREF((PyObject *)state->sha224_type); - if (PyModule_AddObject(module, "SHA224Type", (PyObject *)state->sha224_type) < 0) { - Py_DECREF((PyObject *)state->sha224_type); - return -1; - } - Py_INCREF((PyObject *)state->sha256_type); - if (PyModule_AddObject(module, "SHA256Type", (PyObject *)state->sha256_type) < 0) { - Py_DECREF((PyObject *)state->sha256_type); - return -1; - } - return 0; -} - -static PyModuleDef_Slot _sha256_slots[] = { - {Py_mod_exec, sha256_exec}, - {0, NULL} -}; - -static struct PyModuleDef _sha256module = { - PyModuleDef_HEAD_INIT, - .m_name = "_sha256", - .m_size = sizeof(_sha256_state), - .m_methods = SHA_functions, - .m_slots = _sha256_slots, - .m_traverse = _sha256_traverse, - .m_clear = _sha256_clear, - .m_free = _sha256_free -}; - -/* Initialize this module. */ -PyMODINIT_FUNC -PyInit__sha256(void) -{ - return PyModuleDef_Init(&_sha256module); -} diff --git a/Modules/sha2module.c b/Modules/sha2module.c new file mode 100644 index 00000000000000..9999f255cd578a --- /dev/null +++ b/Modules/sha2module.c @@ -0,0 +1,805 @@ +/* SHA2 module */ + +/* This provides an interface to NIST's SHA2 224, 256, 384, & 512 Algorithms */ + +/* See below for information about the original code this module was + based upon. Additional work performed by: + + Andrew Kuchling (amk@amk.ca) + Greg Stein (gstein@lyra.org) + Trevor Perrin (trevp@trevp.net) + Jonathan Protzenko (jonathan@protzenko.fr) + + Copyright (C) 2005-2007 Gregory P. Smith (greg@krypto.org) + Licensed to PSF under a Contributor Agreement. + +*/ + +/* SHA objects */ +#ifndef Py_BUILD_CORE_BUILTIN +# define Py_BUILD_CORE_MODULE 1 +#endif + +#include "Python.h" +#include "pycore_bitutils.h" // _Py_bswap32() +#include "pycore_moduleobject.h" // _PyModule_GetState() +#include "pycore_strhex.h" // _Py_strhex() +#include "structmember.h" // PyMemberDef +#include "hashlib.h" + +/*[clinic input] +module _sha2 +class SHA256Type "SHA256object *" "&PyType_Type" +class SHA512Type "SHA512object *" "&PyType_Type" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=b5315a7b611c9afc]*/ + + +/* The SHA block sizes and maximum message digest sizes, in bytes */ + +#define SHA256_BLOCKSIZE 64 +#define SHA256_DIGESTSIZE 32 +#define SHA512_BLOCKSIZE 128 +#define SHA512_DIGESTSIZE 64 + +/* Our SHA2 implementations defer to the HACL* verified library. */ + +#include "_hacl/Hacl_Streaming_SHA2.h" + +// TODO: Get rid of int digestsize in favor of Hacl state info? + +typedef struct { + PyObject_HEAD + int digestsize; + Hacl_Streaming_SHA2_state_sha2_256 *state; +} SHA256object; + +typedef struct { + PyObject_HEAD + int digestsize; + Hacl_Streaming_SHA2_state_sha2_512 *state; +} SHA512object; + +#include "clinic/sha2module.c.h" + +/* We shall use run-time type information in the remainder of this module to + * tell apart SHA2-224 and SHA2-256 */ +typedef struct { + PyTypeObject* sha224_type; + PyTypeObject* sha256_type; + PyTypeObject* sha384_type; + PyTypeObject* sha512_type; +} sha2_state; + +static inline sha2_state* +sha2_get_state(PyObject *module) +{ + void *state = _PyModule_GetState(module); + assert(state != NULL); + return (sha2_state *)state; +} + +static void SHA256copy(SHA256object *src, SHA256object *dest) +{ + dest->digestsize = src->digestsize; + dest->state = Hacl_Streaming_SHA2_copy_256(src->state); +} + +static void SHA512copy(SHA512object *src, SHA512object *dest) +{ + dest->digestsize = src->digestsize; + dest->state = Hacl_Streaming_SHA2_copy_512(src->state); +} + +static SHA256object * +newSHA224object(sha2_state *state) +{ + SHA256object *sha = (SHA256object *)PyObject_GC_New( + SHA256object, state->sha224_type); + if (!sha) { + return NULL; + } + PyObject_GC_Track(sha); + return sha; +} + +static SHA256object * +newSHA256object(sha2_state *state) +{ + SHA256object *sha = (SHA256object *)PyObject_GC_New( + SHA256object, state->sha256_type); + if (!sha) { + return NULL; + } + PyObject_GC_Track(sha); + return sha; +} + +static SHA512object * +newSHA384object(sha2_state *state) +{ + SHA512object *sha = (SHA512object *)PyObject_GC_New( + SHA512object, state->sha384_type); + if (!sha) { + return NULL; + } + PyObject_GC_Track(sha); + return sha; +} + +static SHA512object * +newSHA512object(sha2_state *state) +{ + SHA512object *sha = (SHA512object *)PyObject_GC_New( + SHA512object, state->sha512_type); + if (!sha) { + return NULL; + } + PyObject_GC_Track(sha); + return sha; +} + +/* Internal methods for our hash objects. */ + +static int +SHA2_traverse(PyObject *ptr, visitproc visit, void *arg) +{ + Py_VISIT(Py_TYPE(ptr)); + return 0; +} + +static void +SHA256_dealloc(SHA256object *ptr) +{ + Hacl_Streaming_SHA2_free_256(ptr->state); + PyTypeObject *tp = Py_TYPE(ptr); + PyObject_GC_UnTrack(ptr); + PyObject_GC_Del(ptr); + Py_DECREF(tp); +} + +static void +SHA512_dealloc(SHA512object *ptr) +{ + Hacl_Streaming_SHA2_free_512(ptr->state); + PyTypeObject *tp = Py_TYPE(ptr); + PyObject_GC_UnTrack(ptr); + PyObject_GC_Del(ptr); + Py_DECREF(tp); +} + +/* HACL* takes a uint32_t for the length of its parameter, but Py_ssize_t can be + * 64 bits so we loop in <4gig chunks when needed. */ + +static void update_256(Hacl_Streaming_SHA2_state_sha2_256 *state, uint8_t *buf, Py_ssize_t len) { + /* Note: we explicitly ignore the error code on the basis that it would take > + * 1 billion years to overflow the maximum admissible length for SHA2-256 + * (namely, 2^61-1 bytes). */ +#if PY_SSIZE_T_MAX > UINT32_MAX + while (len > UINT32_MAX) { + Hacl_Streaming_SHA2_update_256(state, buf, UINT32_MAX); + len -= UINT32_MAX; + buf += UINT32_MAX; + } +#endif + /* Cast to uint32_t is safe: len <= UINT32_MAX at this point. */ + Hacl_Streaming_SHA2_update_256(state, buf, (uint32_t) len); +} + +static void update_512(Hacl_Streaming_SHA2_state_sha2_512 *state, uint8_t *buf, Py_ssize_t len) { + /* Note: we explicitly ignore the error code on the basis that it would take > + * 1 billion years to overflow the maximum admissible length for this API + * (namely, 2^64-1 bytes). */ +#if PY_SSIZE_T_MAX > UINT32_MAX + while (len > UINT32_MAX) { + Hacl_Streaming_SHA2_update_512(state, buf, UINT32_MAX); + len -= UINT32_MAX; + buf += UINT32_MAX; + } +#endif + /* Cast to uint32_t is safe: len <= UINT32_MAX at this point. */ + Hacl_Streaming_SHA2_update_512(state, buf, (uint32_t) len); +} + + +/* External methods for our hash objects */ + +/*[clinic input] +SHA256Type.copy + + cls:defining_class + +Return a copy of the hash object. +[clinic start generated code]*/ + +static PyObject * +SHA256Type_copy_impl(SHA256object *self, PyTypeObject *cls) +/*[clinic end generated code: output=fabd515577805cd3 input=3137146fcb88e212]*/ +{ + SHA256object *newobj; + sha2_state *state = PyType_GetModuleState(cls); + if (Py_IS_TYPE(self, state->sha256_type)) { + if ((newobj = newSHA256object(state)) == NULL) { + return NULL; + } + } else { + if ((newobj = newSHA224object(state)) == NULL) { + return NULL; + } + } + + SHA256copy(self, newobj); + return (PyObject *)newobj; +} + +/*[clinic input] +SHA512Type.copy + + cls: defining_class + +Return a copy of the hash object. +[clinic start generated code]*/ + +static PyObject * +SHA512Type_copy_impl(SHA512object *self, PyTypeObject *cls) +/*[clinic end generated code: output=66d2a8ef20de8302 input=f673a18f66527c90]*/ +{ + SHA512object *newobj; + sha2_state *state = PyType_GetModuleState(cls); + + if (Py_IS_TYPE((PyObject*)self, state->sha512_type)) { + if ((newobj = newSHA512object(state)) == NULL) { + return NULL; + } + } + else { + if ((newobj = newSHA384object(state)) == NULL) { + return NULL; + } + } + + SHA512copy(self, newobj); + return (PyObject *)newobj; +} + +/*[clinic input] +SHA256Type.digest + +Return the digest value as a bytes object. +[clinic start generated code]*/ + +static PyObject * +SHA256Type_digest_impl(SHA256object *self) +/*[clinic end generated code: output=3a2e3997a98ee792 input=f1f4cfea5cbde35c]*/ +{ + uint8_t digest[SHA256_DIGESTSIZE]; + assert(self->digestsize <= SHA256_DIGESTSIZE); + // HACL* performs copies under the hood so that self->state remains valid + // after this call. + Hacl_Streaming_SHA2_finish_256(self->state, digest); + return PyBytes_FromStringAndSize((const char *)digest, self->digestsize); +} + +/*[clinic input] +SHA512Type.digest + +Return the digest value as a bytes object. +[clinic start generated code]*/ + +static PyObject * +SHA512Type_digest_impl(SHA512object *self) +/*[clinic end generated code: output=dd8c6320070458e0 input=f6470dd359071f4b]*/ +{ + uint8_t digest[SHA512_DIGESTSIZE]; + assert(self->digestsize <= SHA512_DIGESTSIZE); + // HACL* performs copies under the hood so that self->state remains valid + // after this call. + Hacl_Streaming_SHA2_finish_512(self->state, digest); + return PyBytes_FromStringAndSize((const char *)digest, self->digestsize); +} + +/*[clinic input] +SHA256Type.hexdigest + +Return the digest value as a string of hexadecimal digits. +[clinic start generated code]*/ + +static PyObject * +SHA256Type_hexdigest_impl(SHA256object *self) +/*[clinic end generated code: output=96cb68996a780ab3 input=0cc4c714693010d1]*/ +{ + uint8_t digest[SHA256_DIGESTSIZE]; + assert(self->digestsize <= SHA256_DIGESTSIZE); + Hacl_Streaming_SHA2_finish_256(self->state, digest); + return _Py_strhex((const char *)digest, self->digestsize); +} + +/*[clinic input] +SHA512Type.hexdigest + +Return the digest value as a string of hexadecimal digits. +[clinic start generated code]*/ + +static PyObject * +SHA512Type_hexdigest_impl(SHA512object *self) +/*[clinic end generated code: output=cbd6f844aba1fe7c input=498b877b25cbe0a2]*/ +{ + uint8_t digest[SHA512_DIGESTSIZE]; + assert(self->digestsize <= SHA512_DIGESTSIZE); + Hacl_Streaming_SHA2_finish_512(self->state, digest); + return _Py_strhex((const char *)digest, self->digestsize); +} + +/*[clinic input] +SHA256Type.update + + obj: object + / + +Update this hash object's state with the provided string. +[clinic start generated code]*/ + +static PyObject * +SHA256Type_update(SHA256object *self, PyObject *obj) +/*[clinic end generated code: output=1b240f965ddbd8c6 input=b2d449d5b30f0f5a]*/ +{ + Py_buffer buf; + + GET_BUFFER_VIEW_OR_ERROUT(obj, &buf); + + update_256(self->state, buf.buf, buf.len); + + PyBuffer_Release(&buf); + Py_RETURN_NONE; +} + +/*[clinic input] +SHA512Type.update + + obj: object + / + +Update this hash object's state with the provided string. +[clinic start generated code]*/ + +static PyObject * +SHA512Type_update(SHA512object *self, PyObject *obj) +/*[clinic end generated code: output=745f51057a985884 input=ded2b46656566283]*/ +{ + Py_buffer buf; + + GET_BUFFER_VIEW_OR_ERROUT(obj, &buf); + + update_512(self->state, buf.buf, buf.len); + + PyBuffer_Release(&buf); + Py_RETURN_NONE; +} + +static PyMethodDef SHA256_methods[] = { + SHA256TYPE_COPY_METHODDEF + SHA256TYPE_DIGEST_METHODDEF + SHA256TYPE_HEXDIGEST_METHODDEF + SHA256TYPE_UPDATE_METHODDEF + {NULL, NULL} /* sentinel */ +}; + +static PyMethodDef SHA512_methods[] = { + SHA512TYPE_COPY_METHODDEF + SHA512TYPE_DIGEST_METHODDEF + SHA512TYPE_HEXDIGEST_METHODDEF + SHA512TYPE_UPDATE_METHODDEF + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +SHA256_get_block_size(PyObject *self, void *closure) +{ + return PyLong_FromLong(SHA256_BLOCKSIZE); +} + +static PyObject * +SHA512_get_block_size(PyObject *self, void *closure) +{ + return PyLong_FromLong(SHA512_BLOCKSIZE); +} + +static PyObject * +SHA256_get_digest_size(SHA256object *self, void *closure) +{ + return PyLong_FromLong(self->digestsize); +} + +static PyObject * +SHA512_get_digest_size(SHA512object *self, void *closure) +{ + return PyLong_FromLong(self->digestsize); +} + +static PyObject * +SHA256_get_name(SHA256object *self, void *closure) +{ + if (self->digestsize == 28) { + return PyUnicode_FromStringAndSize("sha224", 6); + } + return PyUnicode_FromStringAndSize("sha256", 6); +} + +static PyObject * +SHA512_get_name(SHA512object *self, void *closure) +{ + if (self->digestsize == 64) { + return PyUnicode_FromStringAndSize("sha512", 6); + } + return PyUnicode_FromStringAndSize("sha384", 6); +} + +static PyGetSetDef SHA256_getseters[] = { + {"block_size", + (getter)SHA256_get_block_size, NULL, + NULL, + NULL}, + {"name", + (getter)SHA256_get_name, NULL, + NULL, + NULL}, + {"digest_size", + (getter)SHA256_get_digest_size, NULL, + NULL, + NULL}, + {NULL} /* Sentinel */ +}; + +static PyGetSetDef SHA512_getseters[] = { + {"block_size", + (getter)SHA512_get_block_size, NULL, + NULL, + NULL}, + {"name", + (getter)SHA512_get_name, NULL, + NULL, + NULL}, + {"digest_size", + (getter)SHA512_get_digest_size, NULL, + NULL, + NULL}, + {NULL} /* Sentinel */ +}; + +static PyType_Slot sha256_types_slots[] = { + {Py_tp_dealloc, SHA256_dealloc}, + {Py_tp_methods, SHA256_methods}, + {Py_tp_getset, SHA256_getseters}, + {Py_tp_traverse, SHA2_traverse}, + {0,0} +}; + +static PyType_Slot sha512_type_slots[] = { + {Py_tp_dealloc, SHA512_dealloc}, + {Py_tp_methods, SHA512_methods}, + {Py_tp_getset, SHA512_getseters}, + {Py_tp_traverse, SHA2_traverse}, + {0,0} +}; + +// Using PyType_GetModuleState() on these types is safe since they +// cannot be subclassed: they don't have the Py_TPFLAGS_BASETYPE flag. +static PyType_Spec sha224_type_spec = { + .name = "_sha2.SHA224Type", + .basicsize = sizeof(SHA256object), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | + Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), + .slots = sha256_types_slots +}; + +static PyType_Spec sha256_type_spec = { + .name = "_sha2.SHA256Type", + .basicsize = sizeof(SHA256object), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | + Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), + .slots = sha256_types_slots +}; + +static PyType_Spec sha384_type_spec = { + .name = "_sha2.SHA384Type", + .basicsize = sizeof(SHA512object), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | + Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), + .slots = sha512_type_slots +}; + +static PyType_Spec sha512_type_spec = { + .name = "_sha2.SHA512Type", + .basicsize = sizeof(SHA512object), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | + Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), + .slots = sha512_type_slots +}; + +/* The module-level constructors. */ + +/*[clinic input] +_sha2.sha256 + + string: object(c_default="NULL") = b'' + * + usedforsecurity: bool = True + +Return a new SHA-256 hash object; optionally initialized with a string. +[clinic start generated code]*/ + +static PyObject * +_sha2_sha256_impl(PyObject *module, PyObject *string, int usedforsecurity) +/*[clinic end generated code: output=243c9dd289931f87 input=6249da1de607280a]*/ +{ + Py_buffer buf; + + if (string) { + GET_BUFFER_VIEW_OR_ERROUT(string, &buf); + } + + sha2_state *state = sha2_get_state(module); + + SHA256object *new; + if ((new = newSHA256object(state)) == NULL) { + if (string) { + PyBuffer_Release(&buf); + } + return NULL; + } + + new->state = Hacl_Streaming_SHA2_create_in_256(); + new->digestsize = 32; + + if (PyErr_Occurred()) { + Py_DECREF(new); + if (string) { + PyBuffer_Release(&buf); + } + return NULL; + } + if (string) { + update_256(new->state, buf.buf, buf.len); + PyBuffer_Release(&buf); + } + + return (PyObject *)new; +} + +/*[clinic input] +_sha2.sha224 + + string: object(c_default="NULL") = b'' + * + usedforsecurity: bool = True + +Return a new SHA-224 hash object; optionally initialized with a string. +[clinic start generated code]*/ + +static PyObject * +_sha2_sha224_impl(PyObject *module, PyObject *string, int usedforsecurity) +/*[clinic end generated code: output=68191f232e4a3843 input=c42bcba47fd7d2b7]*/ +{ + Py_buffer buf; + if (string) { + GET_BUFFER_VIEW_OR_ERROUT(string, &buf); + } + + sha2_state *state = sha2_get_state(module); + SHA256object *new; + if ((new = newSHA224object(state)) == NULL) { + if (string) { + PyBuffer_Release(&buf); + } + return NULL; + } + + new->state = Hacl_Streaming_SHA2_create_in_224(); + new->digestsize = 28; + + if (PyErr_Occurred()) { + Py_DECREF(new); + if (string) { + PyBuffer_Release(&buf); + } + return NULL; + } + if (string) { + update_256(new->state, buf.buf, buf.len); + PyBuffer_Release(&buf); + } + + return (PyObject *)new; +} + +/*[clinic input] +_sha2.sha512 + + string: object(c_default="NULL") = b'' + * + usedforsecurity: bool = True + +Return a new SHA-512 hash object; optionally initialized with a string. +[clinic start generated code]*/ + +static PyObject * +_sha2_sha512_impl(PyObject *module, PyObject *string, int usedforsecurity) +/*[clinic end generated code: output=d55c8996eca214d7 input=0576ae2a6ebfad25]*/ +{ + SHA512object *new; + Py_buffer buf; + + sha2_state *state = sha2_get_state(module); + + if (string) + GET_BUFFER_VIEW_OR_ERROUT(string, &buf); + + if ((new = newSHA512object(state)) == NULL) { + if (string) + PyBuffer_Release(&buf); + return NULL; + } + + new->state = Hacl_Streaming_SHA2_create_in_512(); + new->digestsize = 64; + + if (PyErr_Occurred()) { + Py_DECREF(new); + if (string) + PyBuffer_Release(&buf); + return NULL; + } + if (string) { + update_512(new->state, buf.buf, buf.len); + PyBuffer_Release(&buf); + } + + return (PyObject *)new; +} + +/*[clinic input] +_sha2.sha384 + + string: object(c_default="NULL") = b'' + * + usedforsecurity: bool = True + +Return a new SHA-384 hash object; optionally initialized with a string. +[clinic start generated code]*/ + +static PyObject * +_sha2_sha384_impl(PyObject *module, PyObject *string, int usedforsecurity) +/*[clinic end generated code: output=b29a0d81d51d1368 input=4e9199d8de0d2f9b]*/ +{ + SHA512object *new; + Py_buffer buf; + + sha2_state *state = sha2_get_state(module); + + if (string) + GET_BUFFER_VIEW_OR_ERROUT(string, &buf); + + if ((new = newSHA384object(state)) == NULL) { + if (string) + PyBuffer_Release(&buf); + return NULL; + } + + new->state = Hacl_Streaming_SHA2_create_in_384(); + new->digestsize = 48; + + if (PyErr_Occurred()) { + Py_DECREF(new); + if (string) + PyBuffer_Release(&buf); + return NULL; + } + if (string) { + update_512(new->state, buf.buf, buf.len); + PyBuffer_Release(&buf); + } + + return (PyObject *)new; +} + +/* List of functions exported by this module */ + +static struct PyMethodDef SHA2_functions[] = { + _SHA2_SHA256_METHODDEF + _SHA2_SHA224_METHODDEF + _SHA2_SHA512_METHODDEF + _SHA2_SHA384_METHODDEF + {NULL, NULL} /* Sentinel */ +}; + +static int +_sha2_traverse(PyObject *module, visitproc visit, void *arg) +{ + sha2_state *state = sha2_get_state(module); + Py_VISIT(state->sha224_type); + Py_VISIT(state->sha256_type); + Py_VISIT(state->sha384_type); + Py_VISIT(state->sha512_type); + return 0; +} + +static int +_sha2_clear(PyObject *module) +{ + sha2_state *state = sha2_get_state(module); + Py_CLEAR(state->sha224_type); + Py_CLEAR(state->sha256_type); + Py_CLEAR(state->sha384_type); + Py_CLEAR(state->sha512_type); + return 0; +} + +static void +_sha2_free(void *module) +{ + _sha2_clear((PyObject *)module); +} + +/* Initialize this module. */ +static int sha2_exec(PyObject *module) +{ + sha2_state *state = sha2_get_state(module); + + state->sha224_type = (PyTypeObject *)PyType_FromModuleAndSpec( + module, &sha224_type_spec, NULL); + if (state->sha224_type == NULL) { + return -1; + } + state->sha256_type = (PyTypeObject *)PyType_FromModuleAndSpec( + module, &sha256_type_spec, NULL); + if (state->sha256_type == NULL) { + return -1; + } + state->sha384_type = (PyTypeObject *)PyType_FromModuleAndSpec( + module, &sha384_type_spec, NULL); + if (state->sha384_type == NULL) { + return -1; + } + state->sha512_type = (PyTypeObject *)PyType_FromModuleAndSpec( + module, &sha512_type_spec, NULL); + if (state->sha512_type == NULL) { + return -1; + } + + if (PyModule_AddType(module, state->sha224_type) < 0) { + return -1; + } + if (PyModule_AddType(module, state->sha256_type) < 0) { + return -1; + } + if (PyModule_AddType(module, state->sha384_type) < 0) { + return -1; + } + if (PyModule_AddType(module, state->sha512_type) < 0) { + return -1; + } + + return 0; +} + +static PyModuleDef_Slot _sha2_slots[] = { + {Py_mod_exec, sha2_exec}, + {0, NULL} +}; + +static struct PyModuleDef _sha2module = { + PyModuleDef_HEAD_INIT, + .m_name = "_sha2", + .m_size = sizeof(sha2_state), + .m_methods = SHA2_functions, + .m_slots = _sha2_slots, + .m_traverse = _sha2_traverse, + .m_clear = _sha2_clear, + .m_free = _sha2_free +}; + +PyMODINIT_FUNC +PyInit__sha2(void) +{ + return PyModuleDef_Init(&_sha2module); +} diff --git a/Modules/sha512module.c b/Modules/sha512module.c deleted file mode 100644 index d7dfed4e5db03a..00000000000000 --- a/Modules/sha512module.c +++ /dev/null @@ -1,456 +0,0 @@ -/* SHA512 module */ - -/* This module provides an interface to NIST's SHA-512 and SHA-384 Algorithms */ - -/* See below for information about the original code this module was - based upon. Additional work performed by: - - Andrew Kuchling (amk@amk.ca) - Greg Stein (gstein@lyra.org) - Trevor Perrin (trevp@trevp.net) - Jonathan Protzenko (jonathan@protzenko.fr) - - Copyright (C) 2005-2007 Gregory P. Smith (greg@krypto.org) - Licensed to PSF under a Contributor Agreement. - -*/ - -/* SHA objects */ -#ifndef Py_BUILD_CORE_BUILTIN -# define Py_BUILD_CORE_MODULE 1 -#endif - -#include "Python.h" -#include "pycore_bitutils.h" // _Py_bswap64() -#include "pycore_strhex.h" // _Py_strhex() -#include "structmember.h" // PyMemberDef -#include "hashlib.h" - -/*[clinic input] -module _sha512 -class SHA512Type "SHAobject *" "&PyType_Type" -[clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=81a3ccde92bcfe8d]*/ - - -/* The SHA block size and message digest sizes, in bytes */ - -#define SHA_BLOCKSIZE 128 -#define SHA_DIGESTSIZE 64 - -/* The SHA2-384 and SHA2-512 implementations defer to the HACL* verified - * library. */ - -#include "_hacl/Hacl_Streaming_SHA2.h" - -typedef struct { - PyObject_HEAD - int digestsize; - Hacl_Streaming_SHA2_state_sha2_512 *state; -} SHAobject; - -#include "clinic/sha512module.c.h" - - -static void SHAcopy(SHAobject *src, SHAobject *dest) -{ - dest->digestsize = src->digestsize; - dest->state = Hacl_Streaming_SHA2_copy_512(src->state); -} - -typedef struct { - PyTypeObject* sha384_type; - PyTypeObject* sha512_type; -} SHA512State; - -static inline SHA512State* -sha512_get_state(PyObject *module) -{ - void *state = PyModule_GetState(module); - assert(state != NULL); - return (SHA512State *)state; -} - -static SHAobject * -newSHA384object(SHA512State *st) -{ - SHAobject *sha = (SHAobject *)PyObject_GC_New(SHAobject, st->sha384_type); - PyObject_GC_Track(sha); - return sha; -} - -static SHAobject * -newSHA512object(SHA512State *st) -{ - SHAobject *sha = (SHAobject *)PyObject_GC_New(SHAobject, st->sha512_type); - PyObject_GC_Track(sha); - return sha; -} - -/* Internal methods for a hash object */ -static int -SHA_traverse(PyObject *ptr, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(ptr)); - return 0; -} - -static void -SHA512_dealloc(SHAobject *ptr) -{ - Hacl_Streaming_SHA2_free_512(ptr->state); - PyTypeObject *tp = Py_TYPE(ptr); - PyObject_GC_UnTrack(ptr); - PyObject_GC_Del(ptr); - Py_DECREF(tp); -} - -/* HACL* takes a uint32_t for the length of its parameter, but Py_ssize_t can be - * 64 bits. */ -static void update_512(Hacl_Streaming_SHA2_state_sha2_512 *state, uint8_t *buf, Py_ssize_t len) { - /* Note: we explicitly ignore the error code on the basis that it would take > - * 1 billion years to overflow the maximum admissible length for this API - * (namely, 2^64-1 bytes). */ - while (len > UINT32_MAX) { - Hacl_Streaming_SHA2_update_512(state, buf, UINT32_MAX); - len -= UINT32_MAX; - buf += UINT32_MAX; - } - /* Cast to uint32_t is safe: upon exiting the loop, len <= UINT32_MAX, and - * therefore fits in a uint32_t */ - Hacl_Streaming_SHA2_update_512(state, buf, (uint32_t) len); -} - - -/* External methods for a hash object */ - -/*[clinic input] -SHA512Type.copy - - cls: defining_class - -Return a copy of the hash object. -[clinic start generated code]*/ - -static PyObject * -SHA512Type_copy_impl(SHAobject *self, PyTypeObject *cls) -/*[clinic end generated code: output=85ea5b47837a08e6 input=f673a18f66527c90]*/ -{ - SHAobject *newobj; - SHA512State *st = PyType_GetModuleState(cls); - - if (Py_IS_TYPE((PyObject*)self, st->sha512_type)) { - if ( (newobj = newSHA512object(st))==NULL) { - return NULL; - } - } - else { - if ( (newobj = newSHA384object(st))==NULL) { - return NULL; - } - } - - SHAcopy(self, newobj); - return (PyObject *)newobj; -} - -/*[clinic input] -SHA512Type.digest - -Return the digest value as a bytes object. -[clinic start generated code]*/ - -static PyObject * -SHA512Type_digest_impl(SHAobject *self) -/*[clinic end generated code: output=1080bbeeef7dde1b input=f6470dd359071f4b]*/ -{ - uint8_t digest[SHA_DIGESTSIZE]; - // HACL performs copies under the hood so that self->state remains valid - // after this call. - Hacl_Streaming_SHA2_finish_512(self->state, digest); - return PyBytes_FromStringAndSize((const char *)digest, self->digestsize); -} - -/*[clinic input] -SHA512Type.hexdigest - -Return the digest value as a string of hexadecimal digits. -[clinic start generated code]*/ - -static PyObject * -SHA512Type_hexdigest_impl(SHAobject *self) -/*[clinic end generated code: output=7373305b8601e18b input=498b877b25cbe0a2]*/ -{ - uint8_t digest[SHA_DIGESTSIZE]; - Hacl_Streaming_SHA2_finish_512(self->state, digest); - return _Py_strhex((const char *)digest, self->digestsize); -} - -/*[clinic input] -SHA512Type.update - - obj: object - / - -Update this hash object's state with the provided string. -[clinic start generated code]*/ - -static PyObject * -SHA512Type_update(SHAobject *self, PyObject *obj) -/*[clinic end generated code: output=1cf333e73995a79e input=ded2b46656566283]*/ -{ - Py_buffer buf; - - GET_BUFFER_VIEW_OR_ERROUT(obj, &buf); - - update_512(self->state, buf.buf, buf.len); - - PyBuffer_Release(&buf); - Py_RETURN_NONE; -} - -static PyMethodDef SHA_methods[] = { - SHA512TYPE_COPY_METHODDEF - SHA512TYPE_DIGEST_METHODDEF - SHA512TYPE_HEXDIGEST_METHODDEF - SHA512TYPE_UPDATE_METHODDEF - {NULL, NULL} /* sentinel */ -}; - -static PyObject * -SHA512_get_block_size(PyObject *self, void *closure) -{ - return PyLong_FromLong(SHA_BLOCKSIZE); -} - -static PyObject * -SHA512_get_name(PyObject *self, void *closure) -{ - if (((SHAobject *)self)->digestsize == 64) - return PyUnicode_FromStringAndSize("sha512", 6); - else - return PyUnicode_FromStringAndSize("sha384", 6); -} - -static PyGetSetDef SHA_getseters[] = { - {"block_size", - (getter)SHA512_get_block_size, NULL, - NULL, - NULL}, - {"name", - (getter)SHA512_get_name, NULL, - NULL, - NULL}, - {NULL} /* Sentinel */ -}; - -static PyMemberDef SHA_members[] = { - {"digest_size", T_INT, offsetof(SHAobject, digestsize), READONLY, NULL}, - {NULL} /* Sentinel */ -}; - -static PyType_Slot sha512_sha384_type_slots[] = { - {Py_tp_dealloc, SHA512_dealloc}, - {Py_tp_methods, SHA_methods}, - {Py_tp_members, SHA_members}, - {Py_tp_getset, SHA_getseters}, - {Py_tp_traverse, SHA_traverse}, - {0,0} -}; - -static PyType_Spec sha512_sha384_type_spec = { - .name = "_sha512.sha384", - .basicsize = sizeof(SHAobject), - .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | - Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), - .slots = sha512_sha384_type_slots -}; - -// Using PyType_GetModuleState() on this type is safe since -// it cannot be subclassed: it does not have the Py_TPFLAGS_BASETYPE flag. -static PyType_Spec sha512_sha512_type_spec = { - .name = "_sha512.sha512", - .basicsize = sizeof(SHAobject), - .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | - Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), - .slots = sha512_sha384_type_slots -}; - -/* The single module-level function: new() */ - -/*[clinic input] -_sha512.sha512 - - string: object(c_default="NULL") = b'' - * - usedforsecurity: bool = True - -Return a new SHA-512 hash object; optionally initialized with a string. -[clinic start generated code]*/ - -static PyObject * -_sha512_sha512_impl(PyObject *module, PyObject *string, int usedforsecurity) -/*[clinic end generated code: output=a8d9e5f9e6a0831c input=23b4daebc2ebb9c9]*/ -{ - SHAobject *new; - Py_buffer buf; - - SHA512State *st = sha512_get_state(module); - - if (string) - GET_BUFFER_VIEW_OR_ERROUT(string, &buf); - - if ((new = newSHA512object(st)) == NULL) { - if (string) - PyBuffer_Release(&buf); - return NULL; - } - - new->state = Hacl_Streaming_SHA2_create_in_512(); - new->digestsize = 64; - - if (PyErr_Occurred()) { - Py_DECREF(new); - if (string) - PyBuffer_Release(&buf); - return NULL; - } - if (string) { - update_512(new->state, buf.buf, buf.len); - PyBuffer_Release(&buf); - } - - return (PyObject *)new; -} - -/*[clinic input] -_sha512.sha384 - - string: object(c_default="NULL") = b'' - * - usedforsecurity: bool = True - -Return a new SHA-384 hash object; optionally initialized with a string. -[clinic start generated code]*/ - -static PyObject * -_sha512_sha384_impl(PyObject *module, PyObject *string, int usedforsecurity) -/*[clinic end generated code: output=da7d594a08027ac3 input=59ef72f039a6b431]*/ -{ - SHAobject *new; - Py_buffer buf; - - SHA512State *st = sha512_get_state(module); - - if (string) - GET_BUFFER_VIEW_OR_ERROUT(string, &buf); - - if ((new = newSHA384object(st)) == NULL) { - if (string) - PyBuffer_Release(&buf); - return NULL; - } - - new->state = Hacl_Streaming_SHA2_create_in_384(); - new->digestsize = 48; - - if (PyErr_Occurred()) { - Py_DECREF(new); - if (string) - PyBuffer_Release(&buf); - return NULL; - } - if (string) { - update_512(new->state, buf.buf, buf.len); - PyBuffer_Release(&buf); - } - - return (PyObject *)new; -} - - -/* List of functions exported by this module */ - -static struct PyMethodDef SHA_functions[] = { - _SHA512_SHA512_METHODDEF - _SHA512_SHA384_METHODDEF - {NULL, NULL} /* Sentinel */ -}; - -static int -_sha512_traverse(PyObject *module, visitproc visit, void *arg) -{ - SHA512State *state = sha512_get_state(module); - Py_VISIT(state->sha384_type); - Py_VISIT(state->sha512_type); - return 0; -} - -static int -_sha512_clear(PyObject *module) -{ - SHA512State *state = sha512_get_state(module); - Py_CLEAR(state->sha384_type); - Py_CLEAR(state->sha512_type); - return 0; -} - -static void -_sha512_free(void *module) -{ - _sha512_clear((PyObject *)module); -} - - -/* Initialize this module. */ -static int -_sha512_exec(PyObject *m) -{ - SHA512State* st = sha512_get_state(m); - - st->sha384_type = (PyTypeObject *)PyType_FromModuleAndSpec( - m, &sha512_sha384_type_spec, NULL); - - st->sha512_type = (PyTypeObject *)PyType_FromModuleAndSpec( - m, &sha512_sha512_type_spec, NULL); - - if (st->sha384_type == NULL || st->sha512_type == NULL) { - return -1; - } - - Py_INCREF(st->sha384_type); - if (PyModule_AddObject(m, "SHA384Type", (PyObject *)st->sha384_type) < 0) { - Py_DECREF(st->sha384_type); - return -1; - } - - Py_INCREF(st->sha512_type); - if (PyModule_AddObject(m, "SHA384Type", (PyObject *)st->sha512_type) < 0) { - Py_DECREF(st->sha512_type); - return -1; - } - - return 0; -} - -static PyModuleDef_Slot _sha512_slots[] = { - {Py_mod_exec, _sha512_exec}, - {0, NULL} -}; - -static struct PyModuleDef _sha512module = { - PyModuleDef_HEAD_INIT, - .m_name = "_sha512", - .m_size = sizeof(SHA512State), - .m_methods = SHA_functions, - .m_slots = _sha512_slots, - .m_traverse = _sha512_traverse, - .m_clear = _sha512_clear, - .m_free = _sha512_free -}; - -PyMODINIT_FUNC -PyInit__sha512(void) -{ - return PyModuleDef_Init(&_sha512module); -} diff --git a/PC/config.c b/PC/config.c index cdb5db23c4ae49..b1481d79e6508d 100644 --- a/PC/config.c +++ b/PC/config.c @@ -20,8 +20,7 @@ extern PyObject* PyInit_nt(void); extern PyObject* PyInit__operator(void); extern PyObject* PyInit__signal(void); extern PyObject* PyInit__sha1(void); -extern PyObject* PyInit__sha256(void); -extern PyObject* PyInit__sha512(void); +extern PyObject* PyInit__sha2(void); extern PyObject* PyInit__sha3(void); extern PyObject* PyInit__statistics(void); extern PyObject* PyInit__typing(void); @@ -98,8 +97,7 @@ struct _inittab _PyImport_Inittab[] = { {"_signal", PyInit__signal}, {"_md5", PyInit__md5}, {"_sha1", PyInit__sha1}, - {"_sha256", PyInit__sha256}, - {"_sha512", PyInit__sha512}, + {"_sha2", PyInit__sha2}, {"_sha3", PyInit__sha3}, {"_blake2", PyInit__blake2}, {"time", PyInit_time}, diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index e8e9ff01e306bc..222963bc42d17c 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -408,8 +408,7 @@ - - + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index 4820db6f2c32dc..efb96222043ac2 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -869,10 +869,7 @@ Modules - - Modules - - + Modules diff --git a/Python/stdlib_module_names.h b/Python/stdlib_module_names.h index 4e7dfb14d19dec..e9f0061a59d3ba 100644 --- a/Python/stdlib_module_names.h +++ b/Python/stdlib_module_names.h @@ -63,9 +63,8 @@ static const char* _Py_stdlib_module_names[] = { "_random", "_scproxy", "_sha1", -"_sha256", +"_sha2", "_sha3", -"_sha512", "_signal", "_sitebuiltins", "_socket", diff --git a/configure b/configure index c00a1e1d2ec986..7c4254f3cb176f 100755 --- a/configure +++ b/configure @@ -686,10 +686,8 @@ MODULE__BLAKE2_FALSE MODULE__BLAKE2_TRUE MODULE__SHA3_FALSE MODULE__SHA3_TRUE -MODULE__SHA512_FALSE -MODULE__SHA512_TRUE -MODULE__SHA256_FALSE -MODULE__SHA256_TRUE +MODULE__SHA2_FALSE +MODULE__SHA2_TRUE MODULE__SHA1_FALSE MODULE__SHA1_TRUE MODULE__MD5_FALSE @@ -1891,9 +1889,9 @@ Optional Packages: leave OpenSSL's defaults untouched, STRING: use a custom string, python and STRING also set TLS 1.2 as minimum TLS version - --with-builtin-hashlib-hashes=md5,sha1,sha256,sha512,sha3,blake2 - builtin hash modules, md5, sha1, sha256, sha512, - sha3 (with shake), blake2 + --with-builtin-hashlib-hashes=md5,sha1,sha2,sha3,blake2 + builtin hash modules, md5, sha1, sha2, sha3 (with + shake), blake2 Some influential environment variables: PKG_CONFIG path to pkg-config utility @@ -25346,7 +25344,7 @@ fi # builtin hash modules -default_hashlib_hashes="md5,sha1,sha256,sha512,sha3,blake2" +default_hashlib_hashes="md5,sha1,sha2,sha3,blake2" $as_echo "#define PY_BUILTIN_HASHLIB_HASHES /**/" >>confdefs.h @@ -25386,10 +25384,8 @@ for builtin_hash in $with_builtin_hashlib_hashes; do with_builtin_md5=yes ;; #( sha1) : with_builtin_sha1=yes ;; #( - sha256) : - with_builtin_sha256=yes ;; #( - sha512) : - with_builtin_sha512=yes ;; #( + sha2) : + with_builtin_sha2=yes ;; #( sha3) : with_builtin_sha3=yes ;; #( blake2) : @@ -26898,72 +26894,38 @@ fi $as_echo "$py_cv_module__sha1" >&6; } - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdlib extension module _sha256" >&5 -$as_echo_n "checking for stdlib extension module _sha256... " >&6; } - if test "$py_cv_module__sha256" != "n/a"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdlib extension module _sha2" >&5 +$as_echo_n "checking for stdlib extension module _sha2... " >&6; } + if test "$py_cv_module__sha2" != "n/a"; then : - if test "$with_builtin_sha256" = yes; then : + if test "$with_builtin_sha2" = yes; then : if true; then : - py_cv_module__sha256=yes + py_cv_module__sha2=yes else - py_cv_module__sha256=missing + py_cv_module__sha2=missing fi else - py_cv_module__sha256=disabled + py_cv_module__sha2=disabled fi fi - as_fn_append MODULE_BLOCK "MODULE__SHA256_STATE=$py_cv_module__sha256$as_nl" - if test "x$py_cv_module__sha256" = xyes; then : + as_fn_append MODULE_BLOCK "MODULE__SHA2_STATE=$py_cv_module__sha2$as_nl" + if test "x$py_cv_module__sha2" = xyes; then : - as_fn_append MODULE_BLOCK "MODULE__SHA256_CFLAGS=-I\$(srcdir)/Modules/_hacl/include -I\$(srcdir)/Modules/_hacl/internal -D_BSD_SOURCE -D_DEFAULT_SOURCE$as_nl" + as_fn_append MODULE_BLOCK "MODULE__SHA2_CFLAGS=-I\$(srcdir)/Modules/_hacl/include -I\$(srcdir)/Modules/_hacl/internal -D_BSD_SOURCE -D_DEFAULT_SOURCE$as_nl" fi - if test "$py_cv_module__sha256" = yes; then - MODULE__SHA256_TRUE= - MODULE__SHA256_FALSE='#' + if test "$py_cv_module__sha2" = yes; then + MODULE__SHA2_TRUE= + MODULE__SHA2_FALSE='#' else - MODULE__SHA256_TRUE='#' - MODULE__SHA256_FALSE= + MODULE__SHA2_TRUE='#' + MODULE__SHA2_FALSE= fi - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $py_cv_module__sha256" >&5 -$as_echo "$py_cv_module__sha256" >&6; } - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdlib extension module _sha512" >&5 -$as_echo_n "checking for stdlib extension module _sha512... " >&6; } - if test "$py_cv_module__sha512" != "n/a"; then : - - if test "$with_builtin_sha512" = yes; then : - if true; then : - py_cv_module__sha512=yes -else - py_cv_module__sha512=missing -fi -else - py_cv_module__sha512=disabled -fi - -fi - as_fn_append MODULE_BLOCK "MODULE__SHA512_STATE=$py_cv_module__sha512$as_nl" - if test "x$py_cv_module__sha512" = xyes; then : - - as_fn_append MODULE_BLOCK "MODULE__SHA512_CFLAGS=-I\$(srcdir)/Modules/_hacl/include -I\$(srcdir)/Modules/_hacl/internal -D_BSD_SOURCE -D_DEFAULT_SOURCE$as_nl" - - -fi - if test "$py_cv_module__sha512" = yes; then - MODULE__SHA512_TRUE= - MODULE__SHA512_FALSE='#' -else - MODULE__SHA512_TRUE='#' - MODULE__SHA512_FALSE= -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $py_cv_module__sha512" >&5 -$as_echo "$py_cv_module__sha512" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $py_cv_module__sha2" >&5 +$as_echo "$py_cv_module__sha2" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdlib extension module _sha3" >&5 @@ -28337,12 +28299,8 @@ if test -z "${MODULE__SHA1_TRUE}" && test -z "${MODULE__SHA1_FALSE}"; then as_fn_error $? "conditional \"MODULE__SHA1\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi -if test -z "${MODULE__SHA256_TRUE}" && test -z "${MODULE__SHA256_FALSE}"; then - as_fn_error $? "conditional \"MODULE__SHA256\" was never defined. -Usually this means the macro was only invoked conditionally." "$LINENO" 5 -fi -if test -z "${MODULE__SHA512_TRUE}" && test -z "${MODULE__SHA512_FALSE}"; then - as_fn_error $? "conditional \"MODULE__SHA512\" was never defined. +if test -z "${MODULE__SHA2_TRUE}" && test -z "${MODULE__SHA2_FALSE}"; then + as_fn_error $? "conditional \"MODULE__SHA2\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${MODULE__SHA3_TRUE}" && test -z "${MODULE__SHA3_FALSE}"; then diff --git a/configure.ac b/configure.ac index 92a05c011026f2..370bbe07c57634 100644 --- a/configure.ac +++ b/configure.ac @@ -6928,14 +6928,14 @@ AC_DEFINE(PY_SSL_DEFAULT_CIPHERS, 1) ]) # builtin hash modules -default_hashlib_hashes="md5,sha1,sha256,sha512,sha3,blake2" +default_hashlib_hashes="md5,sha1,sha2,sha3,blake2" AC_DEFINE([PY_BUILTIN_HASHLIB_HASHES], [], [enabled builtin hash modules] ) AC_MSG_CHECKING(for --with-builtin-hashlib-hashes) AC_ARG_WITH(builtin-hashlib-hashes, - AS_HELP_STRING([--with-builtin-hashlib-hashes=md5,sha1,sha256,sha512,sha3,blake2], + AS_HELP_STRING([--with-builtin-hashlib-hashes=md5,sha1,sha2,sha3,blake2], [builtin hash modules, - md5, sha1, sha256, sha512, sha3 (with shake), blake2]), + md5, sha1, sha2, sha3 (with shake), blake2]), [ AS_CASE([$with_builtin_hashlib_hashes], [yes], [with_builtin_hashlib_hashes=$default_hashlib_hashes], @@ -6952,8 +6952,7 @@ for builtin_hash in $with_builtin_hashlib_hashes; do AS_CASE($builtin_hash, [md5], [with_builtin_md5=yes], [sha1], [with_builtin_sha1=yes], - [sha256], [with_builtin_sha256=yes], - [sha512], [with_builtin_sha512=yes], + [sha2], [with_builtin_sha2=yes], [sha3], [with_builtin_sha3=yes], [blake2], [with_builtin_blake2=yes] ) @@ -7197,11 +7196,8 @@ dnl By default we always compile these even when OpenSSL is available dnl (issue #14693). The modules are small. PY_STDLIB_MOD([_md5], [test "$with_builtin_md5" = yes]) PY_STDLIB_MOD([_sha1], [test "$with_builtin_sha1" = yes]) -PY_STDLIB_MOD([_sha256], - [test "$with_builtin_sha256" = yes], [], - [-I\$(srcdir)/Modules/_hacl/include -I\$(srcdir)/Modules/_hacl/internal -D_BSD_SOURCE -D_DEFAULT_SOURCE]) -PY_STDLIB_MOD([_sha512], - [test "$with_builtin_sha512" = yes], [], +PY_STDLIB_MOD([_sha2], + [test "$with_builtin_sha2" = yes], [], [-I\$(srcdir)/Modules/_hacl/include -I\$(srcdir)/Modules/_hacl/internal -D_BSD_SOURCE -D_DEFAULT_SOURCE]) PY_STDLIB_MOD([_sha3], [test "$with_builtin_sha3" = yes]) PY_STDLIB_MOD([_blake2], From df7ccf6138b1a2ce0b82ff06aa3497ca4d38c90d Mon Sep 17 00:00:00 2001 From: penguin_wwy <940375606@qq.com> Date: Thu, 16 Feb 2023 19:31:41 +0800 Subject: [PATCH 107/247] gh-101928: fix crash in compiler on multi-line lambda in function call (#101933) --- Lib/test/test_compile.py | 11 +++++++++++ Python/compile.c | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 90b067bcf30912..a77742c0cfa6fc 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -1155,6 +1155,17 @@ def test_if_expression_expression_empty_block(self): with self.subTest(expr=expr): compile(expr, "", "exec") + def test_multi_line_lambda_as_argument(self): + # See gh-101928 + compile(""" +def foo(param, lambda_exp): + pass + +foo(param=0, + lambda_exp=lambda: + 1) + """, "", "exec") + @requires_debug_ranges() class TestSourcePositions(unittest.TestCase): diff --git a/Python/compile.c b/Python/compile.c index 0534b536e3d12e..c3b344c7af2a7f 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -9085,8 +9085,8 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts) Py_DECREF(cnt); break; case RETURN_VALUE: - INSTR_SET_OP1(inst, RETURN_CONST, oparg); - INSTR_SET_OP0(&bb->b_instr[i + 1], NOP); + INSTR_SET_OP0(inst, NOP); + INSTR_SET_OP1(&bb->b_instr[++i], RETURN_CONST, oparg); break; } break; From 36b139af638cdeb671cb6b8b0315b254148688f7 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Thu, 16 Feb 2023 12:31:59 +0000 Subject: [PATCH 108/247] gh-101951: use textwrap.dedent in compiler tests to make them more readable (GH-101950) Fixes #101951. Automerge-Triggered-By: GH:iritkatriel --- Lib/test/test_compile.py | 288 ++++++++++++++++++++------------------- 1 file changed, 145 insertions(+), 143 deletions(-) diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index a77742c0cfa6fc..fe775779c50f50 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -115,24 +115,24 @@ def test_extended_arg(self): repeat = 2000 longexpr = 'x = x or ' + '-x' * repeat g = {} - code = ''' -def f(x): - %s - %s - %s - %s - %s - %s - %s - %s - %s - %s - # the expressions above have no effect, x == argument - while x: - x -= 1 - # EXTENDED_ARG/JUMP_ABSOLUTE here - return x -''' % ((longexpr,)*10) + code = textwrap.dedent(''' + def f(x): + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + # the expressions above have no effect, x == argument + while x: + x -= 1 + # EXTENDED_ARG/JUMP_ABSOLUTE here + return x + ''' % ((longexpr,)*10)) exec(code, g) self.assertEqual(g['f'](5), 0) @@ -148,10 +148,11 @@ def test_float_literals(self): def test_indentation(self): # testing compile() of indented block w/o trailing newline" - s = """ -if 1: - if 2: - pass""" + s = textwrap.dedent(""" + if 1: + if 2: + pass + """) compile(s, "", "exec") # This test is probably specific to CPython and may not generalize @@ -1157,14 +1158,15 @@ def test_if_expression_expression_empty_block(self): def test_multi_line_lambda_as_argument(self): # See gh-101928 - compile(""" -def foo(param, lambda_exp): - pass + code = textwrap.dedent(""" + def foo(param, lambda_exp): + pass -foo(param=0, - lambda_exp=lambda: - 1) - """, "", "exec") + foo(param=0, + lambda_exp=lambda: + 1) + """) + compile(code, "", "exec") @requires_debug_ranges() @@ -1252,24 +1254,24 @@ def test_compiles_to_extended_op_arg(self): column=2, end_column=9, occurrence=2) def test_multiline_expression(self): - snippet = """\ -f( - 1, 2, 3, 4 -) -""" + snippet = textwrap.dedent("""\ + f( + 1, 2, 3, 4 + ) + """) compiled_code, _ = self.check_positions_against_ast(snippet) self.assertOpcodeSourcePositionIs(compiled_code, 'CALL', line=1, end_line=3, column=0, end_column=1) @requires_specialization def test_multiline_boolean_expression(self): - snippet = """\ -if (a or - (b and not c) or - not ( - d > 0)): - x = 42 -""" + snippet = textwrap.dedent("""\ + if (a or + (b and not c) or + not ( + d > 0)): + x = 42 + """) compiled_code, _ = self.check_positions_against_ast(snippet) # jump if a is true: self.assertOpcodeSourcePositionIs(compiled_code, 'POP_JUMP_IF_TRUE', @@ -1288,11 +1290,11 @@ def test_multiline_boolean_expression(self): line=4, end_line=4, column=8, end_column=13, occurrence=2) def test_multiline_assert(self): - snippet = """\ -assert (a > 0 and - bb > 0 and - ccc == 4), "error msg" -""" + snippet = textwrap.dedent("""\ + assert (a > 0 and + bb > 0 and + ccc == 4), "error msg" + """) compiled_code, _ = self.check_positions_against_ast(snippet) self.assertOpcodeSourcePositionIs(compiled_code, 'LOAD_ASSERTION_ERROR', line=1, end_line=3, column=0, end_column=30, occurrence=1) @@ -1305,14 +1307,14 @@ def test_multiline_assert(self): line=1, end_line=3, column=0, end_column=30, occurrence=1) def test_multiline_generator_expression(self): - snippet = """\ -((x, - 2*x) - for x - in [1,2,3] if (x > 0 - and x < 100 - and x != 50)) -""" + snippet = textwrap.dedent("""\ + ((x, + 2*x) + for x + in [1,2,3] if (x > 0 + and x < 100 + and x != 50)) + """) compiled_code, _ = self.check_positions_against_ast(snippet) compiled_code = compiled_code.co_consts[0] self.assertIsInstance(compiled_code, types.CodeType) @@ -1324,14 +1326,14 @@ def test_multiline_generator_expression(self): line=1, end_line=6, column=0, end_column=32, occurrence=1) def test_multiline_async_generator_expression(self): - snippet = """\ -((x, - 2*x) - async for x - in [1,2,3] if (x > 0 - and x < 100 - and x != 50)) -""" + snippet = textwrap.dedent("""\ + ((x, + 2*x) + async for x + in [1,2,3] if (x > 0 + and x < 100 + and x != 50)) + """) compiled_code, _ = self.check_positions_against_ast(snippet) compiled_code = compiled_code.co_consts[0] self.assertIsInstance(compiled_code, types.CodeType) @@ -1341,14 +1343,14 @@ def test_multiline_async_generator_expression(self): line=1, end_line=6, column=0, end_column=32, occurrence=1) def test_multiline_list_comprehension(self): - snippet = """\ -[(x, - 2*x) - for x - in [1,2,3] if (x > 0 - and x < 100 - and x != 50)] -""" + snippet = textwrap.dedent("""\ + [(x, + 2*x) + for x + in [1,2,3] if (x > 0 + and x < 100 + and x != 50)] + """) compiled_code, _ = self.check_positions_against_ast(snippet) compiled_code = compiled_code.co_consts[0] self.assertIsInstance(compiled_code, types.CodeType) @@ -1360,15 +1362,15 @@ def test_multiline_list_comprehension(self): line=1, end_line=6, column=0, end_column=32, occurrence=1) def test_multiline_async_list_comprehension(self): - snippet = """\ -async def f(): - [(x, - 2*x) - async for x - in [1,2,3] if (x > 0 - and x < 100 - and x != 50)] -""" + snippet = textwrap.dedent("""\ + async def f(): + [(x, + 2*x) + async for x + in [1,2,3] if (x > 0 + and x < 100 + and x != 50)] + """) compiled_code, _ = self.check_positions_against_ast(snippet) g = {} eval(compiled_code, g) @@ -1382,14 +1384,14 @@ async def f(): line=2, end_line=7, column=4, end_column=36, occurrence=1) def test_multiline_set_comprehension(self): - snippet = """\ -{(x, - 2*x) - for x - in [1,2,3] if (x > 0 - and x < 100 - and x != 50)} -""" + snippet = textwrap.dedent("""\ + {(x, + 2*x) + for x + in [1,2,3] if (x > 0 + and x < 100 + and x != 50)} + """) compiled_code, _ = self.check_positions_against_ast(snippet) compiled_code = compiled_code.co_consts[0] self.assertIsInstance(compiled_code, types.CodeType) @@ -1401,15 +1403,15 @@ def test_multiline_set_comprehension(self): line=1, end_line=6, column=0, end_column=32, occurrence=1) def test_multiline_async_set_comprehension(self): - snippet = """\ -async def f(): - {(x, - 2*x) - async for x - in [1,2,3] if (x > 0 - and x < 100 - and x != 50)} -""" + snippet = textwrap.dedent("""\ + async def f(): + {(x, + 2*x) + async for x + in [1,2,3] if (x > 0 + and x < 100 + and x != 50)} + """) compiled_code, _ = self.check_positions_against_ast(snippet) g = {} eval(compiled_code, g) @@ -1423,14 +1425,14 @@ async def f(): line=2, end_line=7, column=4, end_column=36, occurrence=1) def test_multiline_dict_comprehension(self): - snippet = """\ -{x: - 2*x - for x - in [1,2,3] if (x > 0 - and x < 100 - and x != 50)} -""" + snippet = textwrap.dedent("""\ + {x: + 2*x + for x + in [1,2,3] if (x > 0 + and x < 100 + and x != 50)} + """) compiled_code, _ = self.check_positions_against_ast(snippet) compiled_code = compiled_code.co_consts[0] self.assertIsInstance(compiled_code, types.CodeType) @@ -1442,15 +1444,15 @@ def test_multiline_dict_comprehension(self): line=1, end_line=6, column=0, end_column=32, occurrence=1) def test_multiline_async_dict_comprehension(self): - snippet = """\ -async def f(): - {x: - 2*x - async for x - in [1,2,3] if (x > 0 - and x < 100 - and x != 50)} -""" + snippet = textwrap.dedent("""\ + async def f(): + {x: + 2*x + async for x + in [1,2,3] if (x > 0 + and x < 100 + and x != 50)} + """) compiled_code, _ = self.check_positions_against_ast(snippet) g = {} eval(compiled_code, g) @@ -1464,11 +1466,11 @@ async def f(): line=2, end_line=7, column=4, end_column=36, occurrence=1) def test_matchcase_sequence(self): - snippet = """\ -match x: - case a, b: - pass -""" + snippet = textwrap.dedent("""\ + match x: + case a, b: + pass + """) compiled_code, _ = self.check_positions_against_ast(snippet) self.assertOpcodeSourcePositionIs(compiled_code, 'MATCH_SEQUENCE', line=2, end_line=2, column=9, end_column=13, occurrence=1) @@ -1480,11 +1482,11 @@ def test_matchcase_sequence(self): line=2, end_line=2, column=9, end_column=13, occurrence=2) def test_matchcase_sequence_wildcard(self): - snippet = """\ -match x: - case a, *b, c: - pass -""" + snippet = textwrap.dedent("""\ + match x: + case a, *b, c: + pass + """) compiled_code, _ = self.check_positions_against_ast(snippet) self.assertOpcodeSourcePositionIs(compiled_code, 'MATCH_SEQUENCE', line=2, end_line=2, column=9, end_column=17, occurrence=1) @@ -1498,11 +1500,11 @@ def test_matchcase_sequence_wildcard(self): line=2, end_line=2, column=9, end_column=17, occurrence=3) def test_matchcase_mapping(self): - snippet = """\ -match x: - case {"a" : a, "b": b}: - pass -""" + snippet = textwrap.dedent("""\ + match x: + case {"a" : a, "b": b}: + pass + """) compiled_code, _ = self.check_positions_against_ast(snippet) self.assertOpcodeSourcePositionIs(compiled_code, 'MATCH_MAPPING', line=2, end_line=2, column=9, end_column=26, occurrence=1) @@ -1514,11 +1516,11 @@ def test_matchcase_mapping(self): line=2, end_line=2, column=9, end_column=26, occurrence=2) def test_matchcase_mapping_wildcard(self): - snippet = """\ -match x: - case {"a" : a, "b": b, **c}: - pass -""" + snippet = textwrap.dedent("""\ + match x: + case {"a" : a, "b": b, **c}: + pass + """) compiled_code, _ = self.check_positions_against_ast(snippet) self.assertOpcodeSourcePositionIs(compiled_code, 'MATCH_MAPPING', line=2, end_line=2, column=9, end_column=31, occurrence=1) @@ -1530,11 +1532,11 @@ def test_matchcase_mapping_wildcard(self): line=2, end_line=2, column=9, end_column=31, occurrence=2) def test_matchcase_class(self): - snippet = """\ -match x: - case C(a, b): - pass -""" + snippet = textwrap.dedent("""\ + match x: + case C(a, b): + pass + """) compiled_code, _ = self.check_positions_against_ast(snippet) self.assertOpcodeSourcePositionIs(compiled_code, 'MATCH_CLASS', line=2, end_line=2, column=9, end_column=16, occurrence=1) @@ -1546,11 +1548,11 @@ def test_matchcase_class(self): line=2, end_line=2, column=9, end_column=16, occurrence=2) def test_matchcase_or(self): - snippet = """\ -match x: - case C(1) | C(2): - pass -""" + snippet = textwrap.dedent("""\ + match x: + case C(1) | C(2): + pass + """) compiled_code, _ = self.check_positions_against_ast(snippet) self.assertOpcodeSourcePositionIs(compiled_code, 'MATCH_CLASS', line=2, end_line=2, column=9, end_column=13, occurrence=1) From 739c026f4488bd2e37d500a2c3d948aaf929b641 Mon Sep 17 00:00:00 2001 From: Rayyan Ansari Date: Thu, 16 Feb 2023 14:52:24 +0000 Subject: [PATCH 109/247] gh-101881: Support (non-)blocking read/write functions on Windows pipes (GH-101882) * fileutils: handle non-blocking pipe IO on Windows Handle erroring operations on non-blocking pipes by reading the _doserrno code. Limit writes on non-blocking pipes that are too large. * Support blocking functions on Windows Use the GetNamedPipeHandleState and SetNamedPipeHandleState Win32 API functions to add support for os.get_blocking and os.set_blocking. --- Doc/library/os.rst | 12 ++- Include/internal/pycore_fileutils.h | 4 +- Lib/test/test_os.py | 1 + ...-02-13-18-05-49.gh-issue-101881._TnHzN.rst | 1 + ...-02-15-11-08-10.gh-issue-101881.fScr3m.rst | 1 + Modules/clinic/posixmodule.c.h | 18 +--- Modules/posixmodule.c | 2 - Python/fileutils.c | 93 ++++++++++++++++++- 8 files changed, 107 insertions(+), 25 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2023-02-13-18-05-49.gh-issue-101881._TnHzN.rst create mode 100644 Misc/NEWS.d/next/Windows/2023-02-15-11-08-10.gh-issue-101881.fScr3m.rst diff --git a/Doc/library/os.rst b/Doc/library/os.rst index fb091176767f7a..85924d0e48366b 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -1091,13 +1091,17 @@ as internal buffering of data. See also :func:`set_blocking` and :meth:`socket.socket.setblocking`. - .. availability:: Unix. + .. availability:: Unix, Windows. The function is limited on Emscripten and WASI, see :ref:`wasm-availability` for more information. + On Windows, this function is limited to pipes. + .. versionadded:: 3.5 + .. versionchanged:: 3.12 + Added support for pipes on Windows. .. function:: isatty(fd, /) @@ -1565,13 +1569,17 @@ or `the MSDN `_ on Windo See also :func:`get_blocking` and :meth:`socket.socket.setblocking`. - .. availability:: Unix. + .. availability:: Unix, Windows. The function is limited on Emscripten and WASI, see :ref:`wasm-availability` for more information. + On Windows, this function is limited to pipes. + .. versionadded:: 3.5 + .. versionchanged:: 3.12 + Added support for pipes on Windows. .. data:: SF_NODISKIO SF_MNOWAIT diff --git a/Include/internal/pycore_fileutils.h b/Include/internal/pycore_fileutils.h index ac89c43d569c07..f8e2bf22590888 100644 --- a/Include/internal/pycore_fileutils.h +++ b/Include/internal/pycore_fileutils.h @@ -160,11 +160,11 @@ PyAPI_FUNC(int) _Py_set_inheritable_async_safe(int fd, int inheritable, PyAPI_FUNC(int) _Py_dup(int fd); -#ifndef MS_WINDOWS PyAPI_FUNC(int) _Py_get_blocking(int fd); PyAPI_FUNC(int) _Py_set_blocking(int fd, int blocking); -#else /* MS_WINDOWS */ + +#ifdef MS_WINDOWS PyAPI_FUNC(void*) _Py_get_osfhandle_noraise(int fd); PyAPI_FUNC(void*) _Py_get_osfhandle(int fd); diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 387d2581c06fc6..deea207bfdadd9 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -4099,6 +4099,7 @@ def test_path_t_converter_and_custom_class(self): @unittest.skipUnless(hasattr(os, 'get_blocking'), 'needs os.get_blocking() and os.set_blocking()') @unittest.skipIf(support.is_emscripten, "Cannot unset blocking flag") +@unittest.skipIf(sys.platform == 'win32', 'Windows only supports blocking on pipes') class BlockingTests(unittest.TestCase): def test_blocking(self): fd = os.open(__file__, os.O_RDONLY) diff --git a/Misc/NEWS.d/next/Windows/2023-02-13-18-05-49.gh-issue-101881._TnHzN.rst b/Misc/NEWS.d/next/Windows/2023-02-13-18-05-49.gh-issue-101881._TnHzN.rst new file mode 100644 index 00000000000000..ba58dd4f5cb450 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-02-13-18-05-49.gh-issue-101881._TnHzN.rst @@ -0,0 +1 @@ +Add support for the os.get_blocking() and os.set_blocking() functions on Windows. diff --git a/Misc/NEWS.d/next/Windows/2023-02-15-11-08-10.gh-issue-101881.fScr3m.rst b/Misc/NEWS.d/next/Windows/2023-02-15-11-08-10.gh-issue-101881.fScr3m.rst new file mode 100644 index 00000000000000..099b2c1c07a665 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-02-15-11-08-10.gh-issue-101881.fScr3m.rst @@ -0,0 +1 @@ +Handle read and write operations on non-blocking pipes properly on Windows. diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index 5e04507ddd6917..dcd25c28370c93 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -10402,8 +10402,6 @@ os_set_handle_inheritable(PyObject *module, PyObject *const *args, Py_ssize_t na #endif /* defined(MS_WINDOWS) */ -#if !defined(MS_WINDOWS) - PyDoc_STRVAR(os_get_blocking__doc__, "get_blocking($module, fd, /)\n" "--\n" @@ -10439,10 +10437,6 @@ os_get_blocking(PyObject *module, PyObject *arg) return return_value; } -#endif /* !defined(MS_WINDOWS) */ - -#if !defined(MS_WINDOWS) - PyDoc_STRVAR(os_set_blocking__doc__, "set_blocking($module, fd, blocking, /)\n" "--\n" @@ -10482,8 +10476,6 @@ os_set_blocking(PyObject *module, PyObject *const *args, Py_ssize_t nargs) return return_value; } -#endif /* !defined(MS_WINDOWS) */ - PyDoc_STRVAR(os_DirEntry_is_symlink__doc__, "is_symlink($self, /)\n" "--\n" @@ -11789,14 +11781,6 @@ os_waitstatus_to_exitcode(PyObject *module, PyObject *const *args, Py_ssize_t na #define OS_SET_HANDLE_INHERITABLE_METHODDEF #endif /* !defined(OS_SET_HANDLE_INHERITABLE_METHODDEF) */ -#ifndef OS_GET_BLOCKING_METHODDEF - #define OS_GET_BLOCKING_METHODDEF -#endif /* !defined(OS_GET_BLOCKING_METHODDEF) */ - -#ifndef OS_SET_BLOCKING_METHODDEF - #define OS_SET_BLOCKING_METHODDEF -#endif /* !defined(OS_SET_BLOCKING_METHODDEF) */ - #ifndef OS_GETRANDOM_METHODDEF #define OS_GETRANDOM_METHODDEF #endif /* !defined(OS_GETRANDOM_METHODDEF) */ @@ -11812,4 +11796,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=a3f76228b549e8ec input=a9049054013a1b77]*/ +/*[clinic end generated code: output=1b0eb6a76b1a0e28 input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index d9e93473aeadaa..524dc7eb1ccc97 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -13930,7 +13930,6 @@ os_set_handle_inheritable_impl(PyObject *module, intptr_t handle, } #endif /* MS_WINDOWS */ -#ifndef MS_WINDOWS /*[clinic input] os.get_blocking -> bool fd: int @@ -13978,7 +13977,6 @@ os_set_blocking_impl(PyObject *module, int fd, int blocking) return NULL; Py_RETURN_NONE; } -#endif /* !MS_WINDOWS */ /*[clinic input] diff --git a/Python/fileutils.c b/Python/fileutils.c index 22b2257a56d0ec..897c2f9f4ea160 100644 --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -1750,7 +1750,15 @@ _Py_read(int fd, void *buf, size_t count) Py_BEGIN_ALLOW_THREADS errno = 0; #ifdef MS_WINDOWS + _doserrno = 0; n = read(fd, buf, (int)count); + // read() on a non-blocking empty pipe fails with EINVAL, which is + // mapped from the Windows error code ERROR_NO_DATA. + if (n < 0 && errno == EINVAL) { + if (_doserrno == ERROR_NO_DATA) { + errno = EAGAIN; + } + } #else n = read(fd, buf, count); #endif @@ -1804,6 +1812,7 @@ _Py_write_impl(int fd, const void *buf, size_t count, int gil_held) } } } + #endif if (count > _PY_WRITE_MAX) { count = _PY_WRITE_MAX; @@ -1814,7 +1823,18 @@ _Py_write_impl(int fd, const void *buf, size_t count, int gil_held) Py_BEGIN_ALLOW_THREADS errno = 0; #ifdef MS_WINDOWS - n = write(fd, buf, (int)count); + // write() on a non-blocking pipe fails with ENOSPC on Windows if + // the pipe lacks available space for the entire buffer. + int c = (int)count; + do { + _doserrno = 0; + n = write(fd, buf, c); + if (n >= 0 || errno != ENOSPC || _doserrno != 0) { + break; + } + errno = EAGAIN; + c /= 2; + } while (c > 0); #else n = write(fd, buf, count); #endif @@ -1829,7 +1849,18 @@ _Py_write_impl(int fd, const void *buf, size_t count, int gil_held) do { errno = 0; #ifdef MS_WINDOWS - n = write(fd, buf, (int)count); + // write() on a non-blocking pipe fails with ENOSPC on Windows if + // the pipe lacks available space for the entire buffer. + int c = (int)count; + do { + _doserrno = 0; + n = write(fd, buf, c); + if (n >= 0 || errno != ENOSPC || _doserrno != 0) { + break; + } + errno = EAGAIN; + c /= 2; + } while (c > 0); #else n = write(fd, buf, count); #endif @@ -2450,6 +2481,64 @@ _Py_set_blocking(int fd, int blocking) return -1; } #else /* MS_WINDOWS */ +int +_Py_get_blocking(int fd) +{ + HANDLE handle; + DWORD mode; + BOOL success; + + handle = _Py_get_osfhandle(fd); + if (handle == INVALID_HANDLE_VALUE) { + return -1; + } + + Py_BEGIN_ALLOW_THREADS + success = GetNamedPipeHandleStateW(handle, &mode, + NULL, NULL, NULL, NULL, 0); + Py_END_ALLOW_THREADS + + if (!success) { + PyErr_SetFromWindowsErr(0); + return -1; + } + + return !(mode & PIPE_NOWAIT); +} + +int +_Py_set_blocking(int fd, int blocking) +{ + HANDLE handle; + DWORD mode; + BOOL success; + + handle = _Py_get_osfhandle(fd); + if (handle == INVALID_HANDLE_VALUE) { + return -1; + } + + Py_BEGIN_ALLOW_THREADS + success = GetNamedPipeHandleStateW(handle, &mode, + NULL, NULL, NULL, NULL, 0); + if (success) { + if (blocking) { + mode &= ~PIPE_NOWAIT; + } + else { + mode |= PIPE_NOWAIT; + } + success = SetNamedPipeHandleState(handle, &mode, NULL, NULL); + } + Py_END_ALLOW_THREADS + + if (!success) { + PyErr_SetFromWindowsErr(0); + return -1; + } + return 0; +} + void* _Py_get_osfhandle_noraise(int fd) { From 924a3bfa28578802eb9ca77a66fb5d4762a62f14 Mon Sep 17 00:00:00 2001 From: sblondon Date: Thu, 16 Feb 2023 16:13:21 +0100 Subject: [PATCH 110/247] gh-93573: Replace wrong example domains in configparser doc (GH-93574) * Replace bitbucket.org domain by forge.example * Update example to python.org * Use explicitly invalid domain topsecret.server.com domain is not controled by PSF. It's replaced by invalid topsecret.server.example domain. It follows RFC 2606, which advise .example as TLD for documentation. --- Doc/library/configparser.rst | 46 ++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/Doc/library/configparser.rst b/Doc/library/configparser.rst index a925a3dd4fb9c2..a7f75fd6e84f4c 100644 --- a/Doc/library/configparser.rst +++ b/Doc/library/configparser.rst @@ -69,10 +69,10 @@ Let's take a very basic configuration file that looks like this: CompressionLevel = 9 ForwardX11 = yes - [bitbucket.org] + [forge.example] User = hg - [topsecret.server.com] + [topsecret.server.example] Port = 50022 ForwardX11 = no @@ -89,10 +89,10 @@ creating the above configuration file programmatically. >>> config['DEFAULT'] = {'ServerAliveInterval': '45', ... 'Compression': 'yes', ... 'CompressionLevel': '9'} - >>> config['bitbucket.org'] = {} - >>> config['bitbucket.org']['User'] = 'hg' - >>> config['topsecret.server.com'] = {} - >>> topsecret = config['topsecret.server.com'] + >>> config['forge.example'] = {} + >>> config['forge.example']['User'] = 'hg' + >>> config['topsecret.server.example'] = {} + >>> topsecret = config['topsecret.server.example'] >>> topsecret['Port'] = '50022' # mutates the parser >>> topsecret['ForwardX11'] = 'no' # same here >>> config['DEFAULT']['ForwardX11'] = 'yes' @@ -115,28 +115,28 @@ back and explore the data it holds. >>> config.read('example.ini') ['example.ini'] >>> config.sections() - ['bitbucket.org', 'topsecret.server.com'] - >>> 'bitbucket.org' in config + ['forge.example', 'topsecret.server.example'] + >>> 'forge.example' in config True - >>> 'bytebong.com' in config + >>> 'python.org' in config False - >>> config['bitbucket.org']['User'] + >>> config['forge.example']['User'] 'hg' >>> config['DEFAULT']['Compression'] 'yes' - >>> topsecret = config['topsecret.server.com'] + >>> topsecret = config['topsecret.server.example'] >>> topsecret['ForwardX11'] 'no' >>> topsecret['Port'] '50022' - >>> for key in config['bitbucket.org']: # doctest: +SKIP + >>> for key in config['forge.example']: # doctest: +SKIP ... print(key) user compressionlevel serveraliveinterval compression forwardx11 - >>> config['bitbucket.org']['ForwardX11'] + >>> config['forge.example']['ForwardX11'] 'yes' As we can see above, the API is pretty straightforward. The only bit of magic @@ -154,15 +154,15 @@ configuration while the previously existing keys are retained. >>> another_config = configparser.ConfigParser() >>> another_config.read('example.ini') ['example.ini'] - >>> another_config['topsecret.server.com']['Port'] + >>> another_config['topsecret.server.example']['Port'] '50022' - >>> another_config.read_string("[topsecret.server.com]\nPort=48484") - >>> another_config['topsecret.server.com']['Port'] + >>> another_config.read_string("[topsecret.server.example]\nPort=48484") + >>> another_config['topsecret.server.example']['Port'] '48484' - >>> another_config.read_dict({"topsecret.server.com": {"Port": 21212}}) - >>> another_config['topsecret.server.com']['Port'] + >>> another_config.read_dict({"topsecret.server.example": {"Port": 21212}}) + >>> another_config['topsecret.server.example']['Port'] '21212' - >>> another_config['topsecret.server.com']['ForwardX11'] + >>> another_config['topsecret.server.example']['ForwardX11'] 'no' This behaviour is equivalent to a :meth:`ConfigParser.read` call with several @@ -195,9 +195,9 @@ recognizes Boolean values from ``'yes'``/``'no'``, ``'on'``/``'off'``, >>> topsecret.getboolean('ForwardX11') False - >>> config['bitbucket.org'].getboolean('ForwardX11') + >>> config['forge.example'].getboolean('ForwardX11') True - >>> config.getboolean('bitbucket.org', 'Compression') + >>> config.getboolean('forge.example', 'Compression') True Apart from :meth:`~ConfigParser.getboolean`, config parsers also @@ -224,7 +224,7 @@ provide fallback values: Please note that default values have precedence over fallback values. For instance, in our example the ``'CompressionLevel'`` key was specified only in the ``'DEFAULT'`` section. If we try to get it from -the section ``'topsecret.server.com'``, we will always get the default, +the section ``'topsecret.server.example'``, we will always get the default, even if we specify a fallback: .. doctest:: @@ -239,7 +239,7 @@ the ``fallback`` keyword-only argument: .. doctest:: - >>> config.get('bitbucket.org', 'monster', + >>> config.get('forge.example', 'monster', ... fallback='No such things as monsters') 'No such things as monsters' From 68bd8c5e2efab64ff9d38a214775164182179431 Mon Sep 17 00:00:00 2001 From: Eclips4 <80244920+Eclips4@users.noreply.github.com> Date: Thu, 16 Feb 2023 20:46:43 +0300 Subject: [PATCH 111/247] gh-101952: Fix possible segfault in `BUILD_SET` opcode (#101958) --- .../2023-02-16-16-57-23.gh-issue-101952.Zo1dlq.rst | 1 + Python/bytecodes.c | 2 ++ Python/generated_cases.c.h | 2 ++ 3 files changed, 5 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-16-16-57-23.gh-issue-101952.Zo1dlq.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-16-16-57-23.gh-issue-101952.Zo1dlq.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-16-16-57-23.gh-issue-101952.Zo1dlq.rst new file mode 100644 index 00000000000000..3902c988c8bf9f --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-16-16-57-23.gh-issue-101952.Zo1dlq.rst @@ -0,0 +1 @@ +Fix possible segfault in ``BUILD_SET`` opcode, when new set created. diff --git a/Python/bytecodes.c b/Python/bytecodes.c index d5d5034cbfbf74..84747f1758e06c 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1302,6 +1302,8 @@ dummy_func( inst(BUILD_SET, (values[oparg] -- set)) { set = PySet_New(NULL); + if (set == NULL) + goto error; int err = 0; for (int i = 0; i < oparg; i++) { PyObject *item = values[i]; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 8b8a7161ad898e..730dfb7426acbf 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -1649,6 +1649,8 @@ PyObject **values = &PEEK(oparg); PyObject *set; set = PySet_New(NULL); + if (set == NULL) + goto error; int err = 0; for (int i = 0; i < oparg; i++) { PyObject *item = values[i]; From 226484e47599a93f5bf033ac47198e68ff401432 Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Thu, 16 Feb 2023 12:57:59 -0500 Subject: [PATCH 112/247] gh-99942: correct the pkg-config/python-config flags for cygwin/android --- .../next/Build/2023-01-12-00-49-16.gh-issue-99942.DUR8b4.rst | 2 ++ configure | 2 +- configure.ac | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2023-01-12-00-49-16.gh-issue-99942.DUR8b4.rst diff --git a/Misc/NEWS.d/next/Build/2023-01-12-00-49-16.gh-issue-99942.DUR8b4.rst b/Misc/NEWS.d/next/Build/2023-01-12-00-49-16.gh-issue-99942.DUR8b4.rst new file mode 100644 index 00000000000000..5b692c3cc458c5 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2023-01-12-00-49-16.gh-issue-99942.DUR8b4.rst @@ -0,0 +1,2 @@ +On Android, in a static build, python-config in embed mode no longer +incorrectly reports a library to link to. diff --git a/configure b/configure index 7c4254f3cb176f..17dc62fb63de3b 100755 --- a/configure +++ b/configure @@ -21496,7 +21496,7 @@ $as_echo "$LDVERSION" >&6; } # On Android and Cygwin the shared libraries must be linked with libpython. -if test -n "$ANDROID_API_LEVEL" -o "$MACHDEP" = "cygwin"; then +if test "$PY_ENABLE_SHARED" = "1" && ( test -n "$ANDROID_API_LEVEL" || test "$MACHDEP" = "cygwin"); then LIBPYTHON="-lpython${VERSION}${ABIFLAGS}" else LIBPYTHON='' diff --git a/configure.ac b/configure.ac index 370bbe07c57634..bc288b86cfa590 100644 --- a/configure.ac +++ b/configure.ac @@ -5759,7 +5759,7 @@ AC_MSG_RESULT($LDVERSION) # On Android and Cygwin the shared libraries must be linked with libpython. AC_SUBST(LIBPYTHON) -if test -n "$ANDROID_API_LEVEL" -o "$MACHDEP" = "cygwin"; then +if test "$PY_ENABLE_SHARED" = "1" && ( test -n "$ANDROID_API_LEVEL" || test "$MACHDEP" = "cygwin"); then LIBPYTHON="-lpython${VERSION}${ABIFLAGS}" else LIBPYTHON='' From a5024a261a75dafa4fb6613298dcb64a9603d9c7 Mon Sep 17 00:00:00 2001 From: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> Date: Fri, 17 Feb 2023 00:18:21 +0530 Subject: [PATCH 113/247] GH-96764: rewrite `asyncio.wait_for` to use `asyncio.timeout` (#98518) Changes `asyncio.wait_for` to use `asyncio.timeout` as its underlying implementation. --- Lib/asyncio/tasks.py | 77 ++++------- Lib/test/test_asyncio/test_futures2.py | 7 +- Lib/test/test_asyncio/test_waitfor.py | 127 ++++++++++++++---- ...2-10-22-09-26-43.gh-issue-96764.Dh9Y5L.rst | 1 + 4 files changed, 133 insertions(+), 79 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-10-22-09-26-43.gh-issue-96764.Dh9Y5L.rst diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py index e78719de216fd0..a2e06d5ef72f42 100644 --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -24,6 +24,7 @@ from . import events from . import exceptions from . import futures +from . import timeouts from .coroutines import _is_coroutine # Helper to generate new task names @@ -437,65 +438,44 @@ async def wait_for(fut, timeout): If the wait is cancelled, the task is also cancelled. + If the task supresses the cancellation and returns a value instead, + that value is returned. + This function is a coroutine. """ - loop = events.get_running_loop() + # The special case for timeout <= 0 is for the following case: + # + # async def test_waitfor(): + # func_started = False + # + # async def func(): + # nonlocal func_started + # func_started = True + # + # try: + # await asyncio.wait_for(func(), 0) + # except asyncio.TimeoutError: + # assert not func_started + # else: + # assert False + # + # asyncio.run(test_waitfor()) - if timeout is None: - return await fut - if timeout <= 0: - fut = ensure_future(fut, loop=loop) + if timeout is not None and timeout <= 0: + fut = ensure_future(fut) if fut.done(): return fut.result() - await _cancel_and_wait(fut, loop=loop) + await _cancel_and_wait(fut) try: return fut.result() except exceptions.CancelledError as exc: - raise exceptions.TimeoutError() from exc - - waiter = loop.create_future() - timeout_handle = loop.call_later(timeout, _release_waiter, waiter) - cb = functools.partial(_release_waiter, waiter) - - fut = ensure_future(fut, loop=loop) - fut.add_done_callback(cb) - - try: - # wait until the future completes or the timeout - try: - await waiter - except exceptions.CancelledError: - if fut.done(): - return fut.result() - else: - fut.remove_done_callback(cb) - # We must ensure that the task is not running - # after wait_for() returns. - # See https://bugs.python.org/issue32751 - await _cancel_and_wait(fut, loop=loop) - raise - - if fut.done(): - return fut.result() - else: - fut.remove_done_callback(cb) - # We must ensure that the task is not running - # after wait_for() returns. - # See https://bugs.python.org/issue32751 - await _cancel_and_wait(fut, loop=loop) - # In case task cancellation failed with some - # exception, we should re-raise it - # See https://bugs.python.org/issue40607 - try: - return fut.result() - except exceptions.CancelledError as exc: - raise exceptions.TimeoutError() from exc - finally: - timeout_handle.cancel() + raise TimeoutError from exc + async with timeouts.timeout(timeout): + return await fut async def _wait(fs, timeout, return_when, loop): """Internal helper for wait(). @@ -541,9 +521,10 @@ def _on_completion(f): return done, pending -async def _cancel_and_wait(fut, loop): +async def _cancel_and_wait(fut): """Cancel the *fut* future or task and wait until it completes.""" + loop = events.get_running_loop() waiter = loop.create_future() cb = functools.partial(_release_waiter, waiter) fut.add_done_callback(cb) diff --git a/Lib/test/test_asyncio/test_futures2.py b/Lib/test/test_asyncio/test_futures2.py index 9e7a5775a70383..b7cfffb76bd8f1 100644 --- a/Lib/test/test_asyncio/test_futures2.py +++ b/Lib/test/test_asyncio/test_futures2.py @@ -86,10 +86,9 @@ async def test_recursive_repr_for_pending_tasks(self): async def func(): return asyncio.all_tasks() - # The repr() call should not raise RecursiveError at first. - # The check for returned string is not very reliable but - # exact comparison for the whole string is even weaker. - self.assertIn('...', repr(await asyncio.wait_for(func(), timeout=10))) + # The repr() call should not raise RecursionError at first. + waiter = await asyncio.wait_for(asyncio.Task(func()),timeout=10) + self.assertIn('...', repr(waiter)) if __name__ == '__main__': diff --git a/Lib/test/test_asyncio/test_waitfor.py b/Lib/test/test_asyncio/test_waitfor.py index 45498fa097f6bc..ed80540b2b3852 100644 --- a/Lib/test/test_asyncio/test_waitfor.py +++ b/Lib/test/test_asyncio/test_waitfor.py @@ -237,33 +237,6 @@ async def inner(): with self.assertRaises(FooException): await foo() - async def test_wait_for_self_cancellation(self): - async def inner(): - try: - await asyncio.sleep(0.3) - except asyncio.CancelledError: - try: - await asyncio.sleep(0.3) - except asyncio.CancelledError: - await asyncio.sleep(0.3) - - return 42 - - inner_task = asyncio.create_task(inner()) - - wait = asyncio.wait_for(inner_task, timeout=0.1) - - # Test that wait_for itself is properly cancellable - # even when the initial task holds up the initial cancellation. - task = asyncio.create_task(wait) - await asyncio.sleep(0.2) - task.cancel() - - with self.assertRaises(asyncio.CancelledError): - await task - - self.assertEqual(await inner_task, 42) - async def _test_cancel_wait_for(self, timeout): loop = asyncio.get_running_loop() @@ -289,6 +262,106 @@ async def test_cancel_blocking_wait_for(self): async def test_cancel_wait_for(self): await self._test_cancel_wait_for(60.0) + async def test_wait_for_cancel_suppressed(self): + # GH-86296: Supressing CancelledError is discouraged + # but if a task subpresses CancelledError and returns a value, + # `wait_for` should return the value instead of raising CancelledError. + # This is the same behavior as `asyncio.timeout`. + + async def return_42(): + try: + await asyncio.sleep(10) + except asyncio.CancelledError: + return 42 + + res = await asyncio.wait_for(return_42(), timeout=0.1) + self.assertEqual(res, 42) + + + async def test_wait_for_issue86296(self): + # GH-86296: The task should get cancelled and not run to completion. + # inner completes in one cycle of the event loop so it + # completes before the task is cancelled. + + async def inner(): + return 'done' + + inner_task = asyncio.create_task(inner()) + reached_end = False + + async def wait_for_coro(): + await asyncio.wait_for(inner_task, timeout=100) + await asyncio.sleep(1) + nonlocal reached_end + reached_end = True + + task = asyncio.create_task(wait_for_coro()) + self.assertFalse(task.done()) + # Run the task + await asyncio.sleep(0) + task.cancel() + with self.assertRaises(asyncio.CancelledError): + await task + self.assertTrue(inner_task.done()) + self.assertEqual(await inner_task, 'done') + self.assertFalse(reached_end) + + +class WaitForShieldTests(unittest.IsolatedAsyncioTestCase): + + async def test_zero_timeout(self): + # `asyncio.shield` creates a new task which wraps the passed in + # awaitable and shields it from cancellation so with timeout=0 + # the task returned by `asyncio.shield` aka shielded_task gets + # cancelled immediately and the task wrapped by it is scheduled + # to run. + + async def coro(): + await asyncio.sleep(0.01) + return 'done' + + task = asyncio.create_task(coro()) + with self.assertRaises(asyncio.TimeoutError): + shielded_task = asyncio.shield(task) + await asyncio.wait_for(shielded_task, timeout=0) + + # Task is running in background + self.assertFalse(task.done()) + self.assertFalse(task.cancelled()) + self.assertTrue(shielded_task.cancelled()) + + # Wait for the task to complete + await asyncio.sleep(0.1) + self.assertTrue(task.done()) + + + async def test_none_timeout(self): + # With timeout=None the timeout is disabled so it + # runs till completion. + async def coro(): + await asyncio.sleep(0.1) + return 'done' + + task = asyncio.create_task(coro()) + await asyncio.wait_for(asyncio.shield(task), timeout=None) + + self.assertTrue(task.done()) + self.assertEqual(await task, "done") + + async def test_shielded_timeout(self): + # shield prevents the task from being cancelled. + async def coro(): + await asyncio.sleep(0.1) + return 'done' + + task = asyncio.create_task(coro()) + with self.assertRaises(asyncio.TimeoutError): + await asyncio.wait_for(asyncio.shield(task), timeout=0.01) + + self.assertFalse(task.done()) + self.assertFalse(task.cancelled()) + self.assertEqual(await task, "done") + if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Library/2022-10-22-09-26-43.gh-issue-96764.Dh9Y5L.rst b/Misc/NEWS.d/next/Library/2022-10-22-09-26-43.gh-issue-96764.Dh9Y5L.rst new file mode 100644 index 00000000000000..a0174291cbc311 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-10-22-09-26-43.gh-issue-96764.Dh9Y5L.rst @@ -0,0 +1 @@ +:func:`asyncio.wait_for` now uses :func:`asyncio.timeout` as its underlying implementation. Patch by Kumar Aditya. From 4d8959b73ac194ca9a2f623dcb5c23680f7d8536 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 16 Feb 2023 14:05:31 -0700 Subject: [PATCH 114/247] gh-101758: Add _PyState_AddModule() Back for the Stable ABI (gh-101956) We're adding the function back, only for the stable ABI symbol and not as any form of API. I had removed it yesterday. This undocumented "private" function was added with the implementation for PEP 3121 (3.0, 2007) for internal use and later moved out of the limited API (3.6, 2016) and then into the internal API (3.9, 2019). I removed it completely yesterday, including from the stable ABI manifest (where it was added because the symbol happened to be exported). It's unlikely that anyone is using _PyState_AddModule(), especially any stable ABI extensions built against 3.2-3.5, but we're playing it safe. https://github.com/python/cpython/issues/101758 --- Include/internal/pycore_pystate.h | 6 ++++++ Lib/test/test_stable_abi_ctypes.py | 1 + Misc/stable_abi.toml | 3 +++ PC/python3dll.c | 1 + Python/import.c | 20 ++++++++++++++++++++ 5 files changed, 31 insertions(+) diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h index 638b86253879ea..7046ec8d9adaaf 100644 --- a/Include/internal/pycore_pystate.h +++ b/Include/internal/pycore_pystate.h @@ -152,6 +152,12 @@ extern void _PySignal_AfterFork(void); #endif +PyAPI_FUNC(int) _PyState_AddModule( + PyThreadState *tstate, + PyObject* module, + PyModuleDef* def); + + PyAPI_FUNC(int) _PyOS_InterruptOccurred(PyThreadState *tstate); #define HEAD_LOCK(runtime) \ diff --git a/Lib/test/test_stable_abi_ctypes.py b/Lib/test/test_stable_abi_ctypes.py index 7e50fbda2c07cb..e77c1c8409880d 100644 --- a/Lib/test/test_stable_abi_ctypes.py +++ b/Lib/test/test_stable_abi_ctypes.py @@ -864,6 +864,7 @@ def test_windows_feature_macros(self): "_PyObject_GC_Resize", "_PyObject_New", "_PyObject_NewVar", + "_PyState_AddModule", "_PyThreadState_Init", "_PyThreadState_Prealloc", "_PyWeakref_CallableProxyType", diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml index c04a3a228caf56..21ff9616133445 100644 --- a/Misc/stable_abi.toml +++ b/Misc/stable_abi.toml @@ -1684,6 +1684,9 @@ [function._PyObject_NewVar] added = '3.2' abi_only = true +[function._PyState_AddModule] + added = '3.2' + abi_only = true [function._PyThreadState_Init] added = '3.2' abi_only = true diff --git a/PC/python3dll.c b/PC/python3dll.c index 79f09037282f54..e300819365756e 100755 --- a/PC/python3dll.c +++ b/PC/python3dll.c @@ -34,6 +34,7 @@ EXPORT_FUNC(_PyObject_GC_NewVar) EXPORT_FUNC(_PyObject_GC_Resize) EXPORT_FUNC(_PyObject_New) EXPORT_FUNC(_PyObject_NewVar) +EXPORT_FUNC(_PyState_AddModule) EXPORT_FUNC(_PyThreadState_Init) EXPORT_FUNC(_PyThreadState_Prealloc) EXPORT_FUNC(Py_AddPendingCall) diff --git a/Python/import.c b/Python/import.c index ec126f28b85816..fabf03b1c5d698 100644 --- a/Python/import.c +++ b/Python/import.c @@ -487,6 +487,26 @@ PyState_FindModule(PyModuleDef* module) return _modules_by_index_get(interp, module); } +/* _PyState_AddModule() has been completely removed from the C-API + (and was removed from the limited API in 3.6). However, we're + playing it safe and keeping it around for any stable ABI extensions + built against 3.2-3.5. */ +int +_PyState_AddModule(PyThreadState *tstate, PyObject* module, PyModuleDef* def) +{ + if (!def) { + assert(_PyErr_Occurred(tstate)); + return -1; + } + if (def->m_slots) { + _PyErr_SetString(tstate, + PyExc_SystemError, + "PyState_AddModule called on module with slots"); + return -1; + } + return _modules_by_index_set(tstate->interp, def, module); +} + int PyState_AddModule(PyObject* module, PyModuleDef* def) { From 984f8ab018f847fe8d66768af962f69ec0e81849 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 16 Feb 2023 17:21:22 -0700 Subject: [PATCH 115/247] gh-101758: Fix Refleak-Related Failures in test_singlephase_variants (gh-101969) gh-101891 is causing failures under `$> ./python -m test test_imp -R 3:3`. Furthermore, with that fixed, "test_singlephase_variants" is leaking references. This change addresses the first part, but skips the leaking tests until we can follow up with a fix. https://github.com/python/cpython/issues/101758 --- Lib/test/test_imp.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_imp.py b/Lib/test/test_imp.py index 5997ffad8e1232..2292bb20939599 100644 --- a/Lib/test/test_imp.py +++ b/Lib/test/test_imp.py @@ -263,6 +263,7 @@ def test_issue16421_multiple_modules_in_one_dll(self): with self.assertRaises(ImportError): imp.load_dynamic('nonexistent', pathname) + @unittest.skip('known refleak (temporarily skipping)') @requires_subinterpreters @requires_load_dynamic def test_singlephase_multiple_interpreters(self): @@ -329,9 +330,10 @@ def clean_up(): # However, globals are still shared. _interpreters.run_string(interp2, script % 2) + @unittest.skip('known refleak (temporarily skipping)') @requires_load_dynamic def test_singlephase_variants(self): - '''Exercise the most meaningful variants described in Python/import.c.''' + # Exercise the most meaningful variants described in Python/import.c. self.maxDiff = None basename = '_testsinglephase' @@ -343,6 +345,11 @@ def clean_up(): _testsinglephase._clear_globals() self.addCleanup(clean_up) + def add_ext_cleanup(name): + def clean_up(): + _testinternalcapi.clear_extension(name, pathname) + self.addCleanup(clean_up) + modules = {} def load(name): assert name not in modules @@ -440,6 +447,7 @@ def check_with_reinit_reloaded(module, lookedup, initialized, # Check the "basic" module. name = basename + add_ext_cleanup(name) expected_init_count = 1 with self.subTest(name): mod = load(name) @@ -457,6 +465,7 @@ def check_with_reinit_reloaded(module, lookedup, initialized, # Check its indirect variants. name = f'{basename}_basic_wrapper' + add_ext_cleanup(name) expected_init_count += 1 with self.subTest(name): mod = load(name) @@ -480,6 +489,7 @@ def check_with_reinit_reloaded(module, lookedup, initialized, # Check its direct variant. name = f'{basename}_basic_copy' + add_ext_cleanup(name) expected_init_count += 1 with self.subTest(name): mod = load(name) @@ -500,6 +510,7 @@ def check_with_reinit_reloaded(module, lookedup, initialized, # Check the non-basic variant that has no state. name = f'{basename}_with_reinit' + add_ext_cleanup(name) with self.subTest(name): mod = load(name) lookedup, initialized, cached = check_common(name, mod) @@ -518,6 +529,7 @@ def check_with_reinit_reloaded(module, lookedup, initialized, # Check the basic variant that has state. name = f'{basename}_with_state' + add_ext_cleanup(name) with self.subTest(name): mod = load(name) lookedup, initialized, cached = check_common(name, mod) From a3bb7fbe7eecfae6bf7b2f0912f9b2b12fac8db1 Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Fri, 17 Feb 2023 12:43:07 +0400 Subject: [PATCH 116/247] gh-101973: Fix parameter reference for PyModule_FromDefAndSpec (#101976) --- Doc/c-api/module.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/c-api/module.rst b/Doc/c-api/module.rst index e2ba157b32c7d9..c0351c8a6c72aa 100644 --- a/Doc/c-api/module.rst +++ b/Doc/c-api/module.rst @@ -388,7 +388,7 @@ objects dynamically. Note that both ``PyModule_FromDefAndSpec`` and .. c:function:: PyObject * PyModule_FromDefAndSpec(PyModuleDef *def, PyObject *spec) - Create a new module object, given the definition in *module* and the + 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`. @@ -396,7 +396,7 @@ objects dynamically. Note that both ``PyModule_FromDefAndSpec`` and .. c:function:: PyObject * PyModule_FromDefAndSpec2(PyModuleDef *def, PyObject *spec, int module_api_version) - Create a new module object, given the definition in *module* and the + Create a new module object, given the definition in *def* and the ModuleSpec *spec*, assuming the API version *module_api_version*. If that version does not match the version of the running interpreter, a :exc:`RuntimeWarning` is emitted. From 3c0a31cbfd1258bd96153a007dd44a96f2947dbf Mon Sep 17 00:00:00 2001 From: Yeojin Kim Date: Fri, 17 Feb 2023 17:47:02 +0900 Subject: [PATCH 117/247] Docs: fix typos in PyFunction_WatchCallback docs and in 3.12 NEWS (GH-101980) - possitibility => possibility - disaallowed => disallowed --- Doc/c-api/function.rst | 2 +- Misc/NEWS.d/3.12.0a2.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/c-api/function.rst b/Doc/c-api/function.rst index 3cce18bdde3057..bc7569d0add97d 100644 --- a/Doc/c-api/function.rst +++ b/Doc/c-api/function.rst @@ -169,7 +169,7 @@ There are a few functions specific to Python functions. before the modification to *func* takes place, so the prior state of *func* can be inspected. The runtime is permitted to optimize away the creation of function objects when possible. In such cases no event will be emitted. - Although this creates the possitibility of an observable difference of + Although this creates the possibility of an observable difference of runtime behavior depending on optimization decisions, it does not change the semantics of the Python code being executed. diff --git a/Misc/NEWS.d/3.12.0a2.rst b/Misc/NEWS.d/3.12.0a2.rst index 318f3f71f11546..41ad8cd22b5d89 100644 --- a/Misc/NEWS.d/3.12.0a2.rst +++ b/Misc/NEWS.d/3.12.0a2.rst @@ -1060,7 +1060,7 @@ Add ``getbufferproc`` and ``releasebufferproc`` to the stable API. Some configurable capabilities of sub-interpreters have changed. They always allow subprocesses (:mod:`subprocess`) now, whereas before subprocesses -could be optionally disaallowed for a sub-interpreter. Instead +could be optionally disallowed for a sub-interpreter. Instead :func:`os.exec` can now be disallowed. Disallowing daemon threads is now supported. Disallowing all threads is still allowed, but is never done by default. Note that the optional restrictions are only available through From 775f8819e319127f9bfb046773b74bcc62c68b6a Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Fri, 17 Feb 2023 19:14:07 +0900 Subject: [PATCH 118/247] gh-101766: Fix refleak for _BlockingOnManager resources (gh-101942) --- Lib/importlib/_bootstrap.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py index bebe7e15cbce67..1ef7b6adb04434 100644 --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -85,6 +85,11 @@ def __enter__(self): def __exit__(self, *args, **kwargs): """Remove self.lock from this thread's _blocking_on list.""" self.blocked_on.remove(self.lock) + if len(self.blocked_on) == 0: + # gh-101766: glboal cache should be cleaned-up + # if there is no more _blocking_on for this thread. + del _blocking_on[self.thread_id] + del self.blocked_on class _DeadlockError(RuntimeError): From d401b20630965c0e1d2a5a0d60d5fc51aa5a8d80 Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Fri, 17 Feb 2023 14:05:38 +0000 Subject: [PATCH 119/247] gh-101360: Fix anchor matching in pathlib.PureWindowsPath.match() (GH-101363) Use `fnmatch` to match path and pattern anchors, just as we do for other path parts. This allows patterns such as `'*:/Users/*'` to be matched. --- Lib/pathlib.py | 5 ----- Lib/test/test_ntpath.py | 4 ++++ Lib/test/test_pathlib.py | 9 ++++++--- .../2023-01-27-02-53-50.gh-issue-101360.bPB7SL.rst | 3 +++ 4 files changed, 13 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-01-27-02-53-50.gh-issue-101360.bPB7SL.rst diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 17659bcd3e2d7f..d7994a331d5eac 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -647,15 +647,10 @@ def match(self, path_pattern): drv, root, pat_parts = self._parse_parts((path_pattern,)) if not pat_parts: raise ValueError("empty pattern") - elif drv and drv != self._flavour.normcase(self._drv): - return False - elif root and root != self._root: - return False parts = self._parts_normcase if drv or root: if len(pat_parts) != len(parts): return False - pat_parts = pat_parts[1:] elif len(pat_parts) > len(parts): return False for part, pat in zip(reversed(parts), reversed(pat_parts)): diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py index b32900697874b1..08c8a7a1f94b95 100644 --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -200,6 +200,10 @@ def test_splitroot(self): tester('ntpath.splitroot("//x")', ("//x", "", "")) # non-empty server & missing share tester('ntpath.splitroot("//x/")', ("//x/", "", "")) # non-empty server & empty share + # gh-101363: match GetFullPathNameW() drive letter parsing behaviour + tester('ntpath.splitroot(" :/foo")', (" :", "/", "foo")) + tester('ntpath.splitroot("/:/foo")', ("", "/", ":/foo")) + def test_split(self): tester('ntpath.split("c:\\foo\\bar")', ('c:\\foo', 'bar')) tester('ntpath.split("\\\\conky\\mountpoint\\foo\\bar")', diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index a596795b44f0fa..b8683796d9600d 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -852,8 +852,7 @@ def test_as_uri(self): def test_match_common(self): P = self.cls # Absolute patterns. - self.assertTrue(P('c:/b.py').match('/*.py')) - self.assertTrue(P('c:/b.py').match('c:*.py')) + self.assertTrue(P('c:/b.py').match('*:/*.py')) self.assertTrue(P('c:/b.py').match('c:/*.py')) self.assertFalse(P('d:/b.py').match('c:/*.py')) # wrong drive self.assertFalse(P('b.py').match('/*.py')) @@ -864,7 +863,7 @@ def test_match_common(self): self.assertFalse(P('/b.py').match('c:*.py')) self.assertFalse(P('/b.py').match('c:/*.py')) # UNC patterns. - self.assertTrue(P('//some/share/a.py').match('/*.py')) + self.assertTrue(P('//some/share/a.py').match('//*/*/*.py')) self.assertTrue(P('//some/share/a.py').match('//some/share/*.py')) self.assertFalse(P('//other/share/a.py').match('//some/share/*.py')) self.assertFalse(P('//some/share/a/b.py').match('//some/share/*.py')) @@ -872,6 +871,10 @@ def test_match_common(self): self.assertTrue(P('B.py').match('b.PY')) self.assertTrue(P('c:/a/B.Py').match('C:/A/*.pY')) self.assertTrue(P('//Some/Share/B.Py').match('//somE/sharE/*.pY')) + # Path anchor doesn't match pattern anchor + self.assertFalse(P('c:/b.py').match('/*.py')) # 'c:/' vs '/' + self.assertFalse(P('c:/b.py').match('c:*.py')) # 'c:/' vs 'c:' + self.assertFalse(P('//some/share/a.py').match('/*.py')) # '//some/share/' vs '/' def test_ordering_common(self): # Case-insensitivity. diff --git a/Misc/NEWS.d/next/Library/2023-01-27-02-53-50.gh-issue-101360.bPB7SL.rst b/Misc/NEWS.d/next/Library/2023-01-27-02-53-50.gh-issue-101360.bPB7SL.rst new file mode 100644 index 00000000000000..4cfb136c5db853 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-27-02-53-50.gh-issue-101360.bPB7SL.rst @@ -0,0 +1,3 @@ +Fix anchor matching in :meth:`pathlib.PureWindowsPath.match`. Path and +pattern anchors are now matched with :mod:`fnmatch`, just like other path +parts. This allows patterns such as ``"*:/Users/*"`` to be matched. From 072011b3c38f871cdc3ab62630ea2234d09456d1 Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Fri, 17 Feb 2023 14:08:14 +0000 Subject: [PATCH 120/247] gh-100809: Fix handling of drive-relative paths in pathlib.Path.absolute() (GH-100812) Resolving the drive independently uses the OS API, which ensures it starts from the current directory on that drive. --- Lib/pathlib.py | 7 +++- Lib/test/support/os_helper.py | 35 +++++++++++++++++++ Lib/test/test_pathlib.py | 20 +++++++++++ ...-01-06-21-14-41.gh-issue-100809.I697UT.rst | 3 ++ 4 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2023-01-06-21-14-41.gh-issue-100809.I697UT.rst diff --git a/Lib/pathlib.py b/Lib/pathlib.py index d7994a331d5eac..dde573592fddce 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -816,7 +816,12 @@ def absolute(self): """ if self.is_absolute(): return self - return self._from_parts([os.getcwd()] + self._parts) + elif self._drv: + # There is a CWD on each drive-letter drive. + cwd = self._flavour.abspath(self._drv) + else: + cwd = os.getcwd() + return self._from_parts([cwd] + self._parts) def resolve(self, strict=False): """ diff --git a/Lib/test/support/os_helper.py b/Lib/test/support/os_helper.py index 2d4356a1191b1e..821a4b1ffd5077 100644 --- a/Lib/test/support/os_helper.py +++ b/Lib/test/support/os_helper.py @@ -4,6 +4,7 @@ import os import re import stat +import string import sys import time import unittest @@ -716,3 +717,37 @@ def __exit__(self, *ignore_exc): else: self._environ[k] = v os.environ = self._environ + + +try: + import ctypes + kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) + + ERROR_FILE_NOT_FOUND = 2 + DDD_REMOVE_DEFINITION = 2 + DDD_EXACT_MATCH_ON_REMOVE = 4 + DDD_NO_BROADCAST_SYSTEM = 8 +except (ImportError, AttributeError): + def subst_drive(path): + raise unittest.SkipTest('ctypes or kernel32 is not available') +else: + @contextlib.contextmanager + def subst_drive(path): + """Temporarily yield a substitute drive for a given path.""" + for c in reversed(string.ascii_uppercase): + drive = f'{c}:' + if (not kernel32.QueryDosDeviceW(drive, None, 0) and + ctypes.get_last_error() == ERROR_FILE_NOT_FOUND): + break + else: + raise unittest.SkipTest('no available logical drive') + if not kernel32.DefineDosDeviceW( + DDD_NO_BROADCAST_SYSTEM, drive, path): + raise ctypes.WinError(ctypes.get_last_error()) + try: + yield drive + finally: + if not kernel32.DefineDosDeviceW( + DDD_REMOVE_DEFINITION | DDD_EXACT_MATCH_ON_REMOVE, + drive, path): + raise ctypes.WinError(ctypes.get_last_error()) diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index b8683796d9600d..4de91d52c6d10c 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -2973,6 +2973,26 @@ def test_absolute(self): self.assertEqual(str(P('a', 'b', 'c').absolute()), os.path.join(share, 'a', 'b', 'c')) + drive = os.path.splitdrive(BASE)[0] + with os_helper.change_cwd(BASE): + # Relative path with root + self.assertEqual(str(P('\\').absolute()), drive + '\\') + self.assertEqual(str(P('\\foo').absolute()), drive + '\\foo') + + # Relative path on current drive + self.assertEqual(str(P(drive).absolute()), BASE) + self.assertEqual(str(P(drive + 'foo').absolute()), os.path.join(BASE, 'foo')) + + with os_helper.subst_drive(BASE) as other_drive: + # Set the working directory on the substitute drive + saved_cwd = os.getcwd() + other_cwd = f'{other_drive}\\dirA' + os.chdir(other_cwd) + os.chdir(saved_cwd) + + # Relative path on another drive + self.assertEqual(str(P(other_drive).absolute()), other_cwd) + self.assertEqual(str(P(other_drive + 'foo').absolute()), other_cwd + '\\foo') def test_glob(self): P = self.cls diff --git a/Misc/NEWS.d/next/Library/2023-01-06-21-14-41.gh-issue-100809.I697UT.rst b/Misc/NEWS.d/next/Library/2023-01-06-21-14-41.gh-issue-100809.I697UT.rst new file mode 100644 index 00000000000000..54082de88ccf4a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-06-21-14-41.gh-issue-100809.I697UT.rst @@ -0,0 +1,3 @@ +Fix handling of drive-relative paths (like 'C:' and 'C:foo') in +:meth:`pathlib.Path.absolute`. This method now uses the OS API +to retrieve the correct current working directory for the drive. From f482ade4c7887c49dfd8bba3be76f839e562608d Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Sat, 18 Feb 2023 00:18:47 +0900 Subject: [PATCH 121/247] gh-101766: Fix refleak for _BlockingOnManager resources from test suite level (gh-101988) --- Lib/importlib/_bootstrap.py | 5 ----- Lib/test/test_importlib/test_locks.py | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py index 1ef7b6adb04434..bebe7e15cbce67 100644 --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -85,11 +85,6 @@ def __enter__(self): def __exit__(self, *args, **kwargs): """Remove self.lock from this thread's _blocking_on list.""" self.blocked_on.remove(self.lock) - if len(self.blocked_on) == 0: - # gh-101766: glboal cache should be cleaned-up - # if there is no more _blocking_on for this thread. - del _blocking_on[self.thread_id] - del self.blocked_on class _DeadlockError(RuntimeError): diff --git a/Lib/test/test_importlib/test_locks.py b/Lib/test/test_importlib/test_locks.py index 56d73c496e6bbb..ba9cf51c261d52 100644 --- a/Lib/test/test_importlib/test_locks.py +++ b/Lib/test/test_importlib/test_locks.py @@ -33,6 +33,11 @@ class ModuleLockAsRLockTests: test_repr = None test_locked_repr = None + def tearDown(self): + for splitinit in init.values(): + splitinit._bootstrap._blocking_on.clear() + + LOCK_TYPES = {kind: splitinit._bootstrap._ModuleLock for kind, splitinit in init.items()} From a1723caabfcdca5d675c4cb04554fb04c7edf601 Mon Sep 17 00:00:00 2001 From: Dustin Rodrigues Date: Fri, 17 Feb 2023 14:30:29 -0500 Subject: [PATCH 122/247] gh-101992: update plistlib examples to be runnable (#101994) * gh-101992: update plistlib examples to be runnable * Update Doc/library/plistlib.rst --------- Co-authored-by: Terry Jan Reedy --- Doc/library/plistlib.rst | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/Doc/library/plistlib.rst b/Doc/library/plistlib.rst index 5ded9661f08014..7aad15ec91a0ac 100644 --- a/Doc/library/plistlib.rst +++ b/Doc/library/plistlib.rst @@ -159,6 +159,9 @@ Examples Generating a plist:: + import datetime + import plistlib + pl = dict( aString = "Doodah", aList = ["A", "B", 12, 32.1, [1, 2, 3]], @@ -172,13 +175,19 @@ Generating a plist:: ), someData = b"", someMoreData = b"" * 10, - aDate = datetime.datetime.fromtimestamp(time.mktime(time.gmtime())), + aDate = datetime.datetime.now() ) - with open(fileName, 'wb') as fp: - dump(pl, fp) + print(plistlib.dumps(pl).decode()) Parsing a plist:: - with open(fileName, 'rb') as fp: - pl = load(fp) - print(pl["aKey"]) + import plistlib + + plist = b""" + + foo + bar + + """ + pl = plistlib.loads(plist) + print(pl["foo"]) From 77d95c83733722ada35eb1ef89ae5b84a51ddd32 Mon Sep 17 00:00:00 2001 From: Jan Gosmann Date: Fri, 17 Feb 2023 22:01:26 +0100 Subject: [PATCH 123/247] gh-100226: Clarify StreamReader.read behavior (#101807) --- Doc/library/asyncio-stream.rst | 12 ++++++++++-- Lib/asyncio/streams.py | 17 +++++++++-------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/Doc/library/asyncio-stream.rst b/Doc/library/asyncio-stream.rst index 3b3c68ab6ef625..bbac1c32b5695f 100644 --- a/Doc/library/asyncio-stream.rst +++ b/Doc/library/asyncio-stream.rst @@ -206,12 +206,20 @@ StreamReader .. coroutinemethod:: read(n=-1) - Read up to *n* bytes. If *n* is not provided, or set to ``-1``, - read until EOF and return all read bytes. + Read up to *n* bytes from the stream. + If *n* is not provided or set to ``-1``, + read until EOF, then return all read :class:`bytes`. If EOF was received and the internal buffer is empty, return an empty ``bytes`` object. + If *n* is ``0``, return an empty ``bytes`` object immediately. + + If *n* is positive, return at most *n* available ``bytes`` + as soon as at least 1 byte is available in the internal buffer. + If EOF is received before any byte is read, return an empty + ``bytes`` object. + .. coroutinemethod:: readline() Read one line, where "line" is a sequence of bytes diff --git a/Lib/asyncio/streams.py b/Lib/asyncio/streams.py index 7d13e961bd2de4..bf15f517e50dce 100644 --- a/Lib/asyncio/streams.py +++ b/Lib/asyncio/streams.py @@ -649,16 +649,17 @@ async def readuntil(self, separator=b'\n'): async def read(self, n=-1): """Read up to `n` bytes from the stream. - If n is not provided, or set to -1, read until EOF and return all read - bytes. If the EOF was received and the internal buffer is empty, return - an empty bytes object. + If `n` is not provided or set to -1, + read until EOF, then return all read bytes. + If EOF was received and the internal buffer is empty, + return an empty bytes object. - If n is zero, return empty bytes object immediately. + If `n` is 0, return an empty bytes object immediately. - If n is positive, this function try to read `n` bytes, and may return - less or equal bytes than requested, but at least one byte. If EOF was - received before any byte is read, this function returns empty byte - object. + If `n` is positive, return at most `n` available bytes + as soon as at least 1 byte is available in the internal buffer. + If EOF is received before any byte is read, return an empty + bytes object. Returned value is not limited with limit, configured at stream creation. From 7f1c72175600b21c1c840e8988cc6e6b4b244582 Mon Sep 17 00:00:00 2001 From: Owain Davies <116417456+OTheDev@users.noreply.github.com> Date: Sat, 18 Feb 2023 04:36:47 +0700 Subject: [PATCH 124/247] gh-101739: [Enum] update docs - default boundary for Flag is CONFORM (GH-101746) --- Doc/library/enum.rst | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index 13591a1bdc7347..24b6dbfe37cd38 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -696,10 +696,9 @@ Data Types .. attribute:: STRICT - Out-of-range values cause a :exc:`ValueError` to be raised. This is the - default for :class:`Flag`:: + Out-of-range values cause a :exc:`ValueError` to be raised:: - >>> from enum import Flag, STRICT + >>> from enum import Flag, STRICT, auto >>> class StrictFlag(Flag, boundary=STRICT): ... RED = auto() ... GREEN = auto() @@ -715,9 +714,9 @@ Data Types .. attribute:: CONFORM Out-of-range values have invalid values removed, leaving a valid *Flag* - value:: + value. This is the default for :class:`Flag`:: - >>> from enum import Flag, CONFORM + >>> from enum import Flag, CONFORM, auto >>> class ConformFlag(Flag, boundary=CONFORM): ... RED = auto() ... GREEN = auto() @@ -731,7 +730,7 @@ Data Types Out-of-range values lose their *Flag* membership and revert to :class:`int`. This is the default for :class:`IntFlag`:: - >>> from enum import Flag, EJECT + >>> from enum import Flag, EJECT, auto >>> class EjectFlag(Flag, boundary=EJECT): ... RED = auto() ... GREEN = auto() @@ -742,10 +741,10 @@ Data Types .. attribute:: KEEP - Out-of-range values are kept, and the *Flag* membership is kept. This is - used for some stdlib flags: + Out-of-range values are kept, and the *Flag* membership is kept. This is + used for some stdlib flags:: - >>> from enum import Flag, KEEP + >>> from enum import Flag, KEEP, auto >>> class KeepFlag(Flag, boundary=KEEP): ... RED = auto() ... GREEN = auto() From 89413bbccb9261b72190e275eefe4b0d49671477 Mon Sep 17 00:00:00 2001 From: Eclips4 <80244920+Eclips4@users.noreply.github.com> Date: Sat, 18 Feb 2023 03:52:23 +0300 Subject: [PATCH 125/247] gh-101967: add a missing error check (#101968) --- .../2023-02-16-23-19-01.gh-issue-101967.Kqr1dz.rst | 1 + Python/ceval.c | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-16-23-19-01.gh-issue-101967.Kqr1dz.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-16-23-19-01.gh-issue-101967.Kqr1dz.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-16-23-19-01.gh-issue-101967.Kqr1dz.rst new file mode 100644 index 00000000000000..6e681f910f5359 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-16-23-19-01.gh-issue-101967.Kqr1dz.rst @@ -0,0 +1 @@ +Fix possible segfault in ``positional_only_passed_as_keyword`` function, when new list created. diff --git a/Python/ceval.c b/Python/ceval.c index 09fd2f29266c87..308ef52259df3d 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1255,7 +1255,9 @@ positional_only_passed_as_keyword(PyThreadState *tstate, PyCodeObject *co, { int posonly_conflicts = 0; PyObject* posonly_names = PyList_New(0); - + if (posonly_names == NULL) { + goto fail; + } for(int k=0; k < co->co_posonlyargcount; k++){ PyObject* posonly_name = PyTuple_GET_ITEM(co->co_localsplusnames, k); From af446bbb76f64e67831444a0ceee6863a1527088 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Sat, 18 Feb 2023 18:46:33 +0300 Subject: [PATCH 126/247] gh-101536: [docs] Improve attributes of `urllib.error.HTTPError` (#101612) * gh-101536: [docs] Improve attributes of `urllib.error.HTTPError` * Address review --- Doc/library/urllib.error.rst | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Doc/library/urllib.error.rst b/Doc/library/urllib.error.rst index f7d47ed76aca18..3adbdd26132273 100644 --- a/Doc/library/urllib.error.rst +++ b/Doc/library/urllib.error.rst @@ -31,7 +31,7 @@ The following exceptions are raised by :mod:`urllib.error` as appropriate: of :exc:`IOError`. -.. exception:: HTTPError +.. exception:: HTTPError(url, code, msg, hdrs, fp) Though being an exception (a subclass of :exc:`URLError`), an :exc:`HTTPError` can also function as a non-exceptional file-like return @@ -39,6 +39,11 @@ The following exceptions are raised by :mod:`urllib.error` as appropriate: is useful when handling exotic HTTP errors, such as requests for authentication. + .. attribute:: url + + Contains the request URL. + An alias for *filename* attribute. + .. attribute:: code An HTTP status code as defined in :rfc:`2616`. This numeric value corresponds @@ -48,14 +53,20 @@ The following exceptions are raised by :mod:`urllib.error` as appropriate: .. attribute:: reason This is usually a string explaining the reason for this error. + An alias for *msg* attribute. .. attribute:: headers The HTTP response headers for the HTTP request that caused the :exc:`HTTPError`. + An alias for *hdrs* attribute. .. versionadded:: 3.4 + .. attribute:: fp + + A file-like object where the HTTP error body can be read from. + .. exception:: ContentTooShortError(msg, content) This exception is raised when the :func:`~urllib.request.urlretrieve` From 128379b8cdb88a6d3d7fed24df082c9a654b3fb8 Mon Sep 17 00:00:00 2001 From: Nicko van Someren Date: Sat, 18 Feb 2023 11:44:41 -0700 Subject: [PATCH 127/247] bpo-46978: Correct docstrings for in-place builtin operators (#31802) --- .../Core and Builtins/2022-03-10-21-48-05.bpo-46978.f5QFfw.rst | 1 + Objects/typeobject.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-03-10-21-48-05.bpo-46978.f5QFfw.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-03-10-21-48-05.bpo-46978.f5QFfw.rst b/Misc/NEWS.d/next/Core and Builtins/2022-03-10-21-48-05.bpo-46978.f5QFfw.rst new file mode 100644 index 00000000000000..72291d042a0394 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-03-10-21-48-05.bpo-46978.f5QFfw.rst @@ -0,0 +1 @@ +Fixed docstrings for in-place operators of built-in types. diff --git a/Objects/typeobject.c b/Objects/typeobject.c index bf6ccdb77a90f0..f2e8092aa37eec 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -8564,7 +8564,7 @@ an all-zero entry. #NAME "($self, /)\n--\n\n" DOC) #define IBSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \ ETSLOT(NAME, as_number.SLOT, FUNCTION, WRAPPER, \ - #NAME "($self, value, /)\n--\n\nReturn self" DOC "value.") + #NAME "($self, value, /)\n--\n\nCompute self " DOC " value.") #define BINSLOT(NAME, SLOT, FUNCTION, DOC) \ ETSLOT(NAME, as_number.SLOT, FUNCTION, wrap_binaryfunc_l, \ #NAME "($self, value, /)\n--\n\nReturn self" DOC "value.") From 5170caf3059fdacc92d7370eecb9fe4f0c5a1c76 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 18 Feb 2023 16:29:22 -0500 Subject: [PATCH 128/247] gh-97930: Apply changes from importlib_resources 5.12. (GH-102010) --- Lib/importlib/resources/_adapters.py | 4 +- Lib/importlib/resources/_itertools.py | 69 ++++++------ Lib/importlib/resources/readers.py | 36 +++++-- Lib/test/test_importlib/resources/_path.py | 18 ++-- .../subdirectory/subsubdir/resource.txt | 1 + .../resources/test_compatibilty_files.py | 6 +- .../test_importlib/resources/test_custom.py | 46 ++++++++ .../test_importlib/resources/test_files.py | 4 +- .../test_importlib/resources/test_open.py | 14 ++- .../test_importlib/resources/test_path.py | 15 ++- .../test_importlib/resources/test_read.py | 12 ++- .../test_importlib/resources/test_reader.py | 11 ++ .../test_importlib/resources/test_resource.py | 100 ++++++++---------- Lib/test/test_importlib/resources/util.py | 28 +++-- ...3-02-17-19-00-58.gh-issue-97930.C_nQjb.rst | 4 + 15 files changed, 241 insertions(+), 127 deletions(-) create mode 100644 Lib/test/test_importlib/resources/data02/subdirectory/subsubdir/resource.txt create mode 100644 Lib/test/test_importlib/resources/test_custom.py create mode 100644 Misc/NEWS.d/next/Library/2023-02-17-19-00-58.gh-issue-97930.C_nQjb.rst diff --git a/Lib/importlib/resources/_adapters.py b/Lib/importlib/resources/_adapters.py index f22f6bc509a3d7..50688fbb666658 100644 --- a/Lib/importlib/resources/_adapters.py +++ b/Lib/importlib/resources/_adapters.py @@ -34,9 +34,7 @@ def _io_wrapper(file, mode='r', *args, **kwargs): return TextIOWrapper(file, *args, **kwargs) elif mode == 'rb': return file - raise ValueError( - f"Invalid mode value '{mode}', only 'r' and 'rb' are supported" - ) + raise ValueError(f"Invalid mode value '{mode}', only 'r' and 'rb' are supported") class CompatibilityFiles: diff --git a/Lib/importlib/resources/_itertools.py b/Lib/importlib/resources/_itertools.py index cce05582ffc6fe..7b775ef5ae893f 100644 --- a/Lib/importlib/resources/_itertools.py +++ b/Lib/importlib/resources/_itertools.py @@ -1,35 +1,38 @@ -from itertools import filterfalse +# from more_itertools 9.0 +def only(iterable, default=None, too_long=None): + """If *iterable* has only one item, return it. + If it has zero items, return *default*. + If it has more than one item, raise the exception given by *too_long*, + which is ``ValueError`` by default. + >>> only([], default='missing') + 'missing' + >>> only([1]) + 1 + >>> only([1, 2]) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + ValueError: Expected exactly one item in iterable, but got 1, 2, + and perhaps more.' + >>> only([1, 2], too_long=TypeError) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + TypeError + Note that :func:`only` attempts to advance *iterable* twice to ensure there + is only one item. See :func:`spy` or :func:`peekable` to check + iterable contents less destructively. + """ + it = iter(iterable) + first_value = next(it, default) -from typing import ( - Callable, - Iterable, - Iterator, - Optional, - Set, - TypeVar, - Union, -) - -# Type and type variable definitions -_T = TypeVar('_T') -_U = TypeVar('_U') - - -def unique_everseen( - iterable: Iterable[_T], key: Optional[Callable[[_T], _U]] = None -) -> Iterator[_T]: - "List unique elements, preserving order. Remember all elements ever seen." - # unique_everseen('AAAABBBCCDAABBB') --> A B C D - # unique_everseen('ABBCcAD', str.lower) --> A B C D - seen: Set[Union[_T, _U]] = set() - seen_add = seen.add - if key is None: - for element in filterfalse(seen.__contains__, iterable): - seen_add(element) - yield element + try: + second_value = next(it) + except StopIteration: + pass else: - for element in iterable: - k = key(element) - if k not in seen: - seen_add(k) - yield element + msg = ( + 'Expected exactly one item in iterable, but got {!r}, {!r}, ' + 'and perhaps more.'.format(first_value, second_value) + ) + raise too_long or ValueError(msg) + + return first_value diff --git a/Lib/importlib/resources/readers.py b/Lib/importlib/resources/readers.py index 80cb320dd8bda0..c3cdf769cbecb0 100644 --- a/Lib/importlib/resources/readers.py +++ b/Lib/importlib/resources/readers.py @@ -1,11 +1,12 @@ import collections -import operator +import itertools import pathlib +import operator import zipfile from . import abc -from ._itertools import unique_everseen +from ._itertools import only def remove_duplicates(items): @@ -41,8 +42,10 @@ def open_resource(self, resource): raise FileNotFoundError(exc.args[0]) def is_resource(self, path): - # workaround for `zipfile.Path.is_file` returning true - # for non-existent paths. + """ + Workaround for `zipfile.Path.is_file` returning true + for non-existent paths. + """ target = self.files().joinpath(path) return target.is_file() and target.exists() @@ -67,8 +70,10 @@ def __init__(self, *paths): raise NotADirectoryError('MultiplexedPath only supports directories') def iterdir(self): - files = (file for path in self._paths for file in path.iterdir()) - return unique_everseen(files, key=operator.attrgetter('name')) + children = (child for path in self._paths for child in path.iterdir()) + by_name = operator.attrgetter('name') + groups = itertools.groupby(sorted(children, key=by_name), key=by_name) + return map(self._follow, (locs for name, locs in groups)) def read_bytes(self): raise FileNotFoundError(f'{self} is not a file') @@ -90,6 +95,25 @@ def joinpath(self, *descendants): # Just return something that will not exist. return self._paths[0].joinpath(*descendants) + @classmethod + def _follow(cls, children): + """ + Construct a MultiplexedPath if needed. + + If children contains a sole element, return it. + Otherwise, return a MultiplexedPath of the items. + Unless one of the items is not a Directory, then return the first. + """ + subdirs, one_dir, one_file = itertools.tee(children, 3) + + try: + return only(one_dir) + except ValueError: + try: + return cls(*subdirs) + except NotADirectoryError: + return next(one_file) + def open(self, *args, **kwargs): raise FileNotFoundError(f'{self} is not a file') diff --git a/Lib/test/test_importlib/resources/_path.py b/Lib/test/test_importlib/resources/_path.py index c630e4d3d3f352..1f97c96146960d 100644 --- a/Lib/test/test_importlib/resources/_path.py +++ b/Lib/test/test_importlib/resources/_path.py @@ -1,12 +1,16 @@ import pathlib import functools +from typing import Dict, Union + #### -# from jaraco.path 3.4 +# from jaraco.path 3.4.1 + +FilesSpec = Dict[str, Union[str, bytes, 'FilesSpec']] # type: ignore -def build(spec, prefix=pathlib.Path()): +def build(spec: FilesSpec, prefix=pathlib.Path()): """ Build a set of files/directories, as described by the spec. @@ -23,15 +27,17 @@ def build(spec, prefix=pathlib.Path()): ... "baz.py": "# Some code", ... } ... } - >>> tmpdir = getfixture('tmpdir') - >>> build(spec, tmpdir) + >>> target = getfixture('tmp_path') + >>> build(spec, target) + >>> target.joinpath('foo/baz.py').read_text(encoding='utf-8') + '# Some code' """ for name, contents in spec.items(): create(contents, pathlib.Path(prefix) / name) @functools.singledispatch -def create(content, path): +def create(content: Union[str, bytes, FilesSpec], path): path.mkdir(exist_ok=True) build(content, prefix=path) # type: ignore @@ -43,7 +49,7 @@ def _(content: bytes, path): @create.register def _(content: str, path): - path.write_text(content) + path.write_text(content, encoding='utf-8') # end from jaraco.path diff --git a/Lib/test/test_importlib/resources/data02/subdirectory/subsubdir/resource.txt b/Lib/test/test_importlib/resources/data02/subdirectory/subsubdir/resource.txt new file mode 100644 index 00000000000000..48f587a2d0ac53 --- /dev/null +++ b/Lib/test/test_importlib/resources/data02/subdirectory/subsubdir/resource.txt @@ -0,0 +1 @@ +a resource \ No newline at end of file diff --git a/Lib/test/test_importlib/resources/test_compatibilty_files.py b/Lib/test/test_importlib/resources/test_compatibilty_files.py index 6fa18a24973f64..bcf608d9e2cbdf 100644 --- a/Lib/test/test_importlib/resources/test_compatibilty_files.py +++ b/Lib/test/test_importlib/resources/test_compatibilty_files.py @@ -64,11 +64,13 @@ def test_orphan_path_name(self): def test_spec_path_open(self): self.assertEqual(self.files.read_bytes(), b'Hello, world!') - self.assertEqual(self.files.read_text(), 'Hello, world!') + self.assertEqual(self.files.read_text(encoding='utf-8'), 'Hello, world!') def test_child_path_open(self): self.assertEqual((self.files / 'a').read_bytes(), b'Hello, world!') - self.assertEqual((self.files / 'a').read_text(), 'Hello, world!') + self.assertEqual( + (self.files / 'a').read_text(encoding='utf-8'), 'Hello, world!' + ) def test_orphan_path_open(self): with self.assertRaises(FileNotFoundError): diff --git a/Lib/test/test_importlib/resources/test_custom.py b/Lib/test/test_importlib/resources/test_custom.py new file mode 100644 index 00000000000000..73127209a2761b --- /dev/null +++ b/Lib/test/test_importlib/resources/test_custom.py @@ -0,0 +1,46 @@ +import unittest +import contextlib +import pathlib + +from test.support import os_helper + +from importlib import resources +from importlib.resources.abc import TraversableResources, ResourceReader +from . import util + + +class SimpleLoader: + """ + A simple loader that only implements a resource reader. + """ + + def __init__(self, reader: ResourceReader): + self.reader = reader + + def get_resource_reader(self, package): + return self.reader + + +class MagicResources(TraversableResources): + """ + Magically returns the resources at path. + """ + + def __init__(self, path: pathlib.Path): + self.path = path + + def files(self): + return self.path + + +class CustomTraversableResourcesTests(unittest.TestCase): + def setUp(self): + self.fixtures = contextlib.ExitStack() + self.addCleanup(self.fixtures.close) + + def test_custom_loader(self): + temp_dir = self.fixtures.enter_context(os_helper.temp_dir()) + loader = SimpleLoader(MagicResources(temp_dir)) + pkg = util.create_package_from_loader(loader) + files = resources.files(pkg) + assert files is temp_dir diff --git a/Lib/test/test_importlib/resources/test_files.py b/Lib/test/test_importlib/resources/test_files.py index fe813ae7d08881..1450cfb310926a 100644 --- a/Lib/test/test_importlib/resources/test_files.py +++ b/Lib/test/test_importlib/resources/test_files.py @@ -85,7 +85,7 @@ def test_module_resources(self): _path.build(spec, self.site_dir) import mod - actual = resources.files(mod).joinpath('res.txt').read_text() + actual = resources.files(mod).joinpath('res.txt').read_text(encoding='utf-8') assert actual == spec['res.txt'] @@ -99,7 +99,7 @@ def test_implicit_files(self): '__init__.py': textwrap.dedent( """ import importlib.resources as res - val = res.files().joinpath('res.txt').read_text() + val = res.files().joinpath('res.txt').read_text(encoding='utf-8') """ ), 'res.txt': 'resources are the best', diff --git a/Lib/test/test_importlib/resources/test_open.py b/Lib/test/test_importlib/resources/test_open.py index 0554c41ba67d0e..86becb4bfaad37 100644 --- a/Lib/test/test_importlib/resources/test_open.py +++ b/Lib/test/test_importlib/resources/test_open.py @@ -15,7 +15,7 @@ def execute(self, package, path): class CommonTextTests(util.CommonTests, unittest.TestCase): def execute(self, package, path): target = resources.files(package).joinpath(path) - with target.open(): + with target.open(encoding='utf-8'): pass @@ -28,7 +28,7 @@ def test_open_binary(self): def test_open_text_default_encoding(self): target = resources.files(self.data) / 'utf-8.file' - with target.open() as fp: + with target.open(encoding='utf-8') as fp: result = fp.read() self.assertEqual(result, 'Hello, UTF-8 world!\n') @@ -39,7 +39,9 @@ def test_open_text_given_encoding(self): self.assertEqual(result, 'Hello, UTF-16 world!\n') def test_open_text_with_errors(self): - # Raises UnicodeError without the 'errors' argument. + """ + Raises UnicodeError without the 'errors' argument. + """ target = resources.files(self.data) / 'utf-16.file' with target.open(encoding='utf-8', errors='strict') as fp: self.assertRaises(UnicodeError, fp.read) @@ -54,11 +56,13 @@ def test_open_text_with_errors(self): def test_open_binary_FileNotFoundError(self): target = resources.files(self.data) / 'does-not-exist' - self.assertRaises(FileNotFoundError, target.open, 'rb') + with self.assertRaises(FileNotFoundError): + target.open('rb') def test_open_text_FileNotFoundError(self): target = resources.files(self.data) / 'does-not-exist' - self.assertRaises(FileNotFoundError, target.open) + with self.assertRaises(FileNotFoundError): + target.open(encoding='utf-8') class OpenDiskTests(OpenTests, unittest.TestCase): diff --git a/Lib/test/test_importlib/resources/test_path.py b/Lib/test/test_importlib/resources/test_path.py index adcf75feea78ec..34a6bdd2d58b91 100644 --- a/Lib/test/test_importlib/resources/test_path.py +++ b/Lib/test/test_importlib/resources/test_path.py @@ -14,9 +14,12 @@ def execute(self, package, path): class PathTests: def test_reading(self): - # Path should be readable. - # Test also implicitly verifies the returned object is a pathlib.Path - # instance. + """ + Path should be readable. + + Test also implicitly verifies the returned object is a pathlib.Path + instance. + """ target = resources.files(self.data) / 'utf-8.file' with resources.as_file(target) as path: self.assertTrue(path.name.endswith("utf-8.file"), repr(path)) @@ -51,8 +54,10 @@ def setUp(self): class PathZipTests(PathTests, util.ZipSetup, unittest.TestCase): def test_remove_in_context_manager(self): - # It is not an error if the file that was temporarily stashed on the - # file system is removed inside the `with` stanza. + """ + It is not an error if the file that was temporarily stashed on the + file system is removed inside the `with` stanza. + """ target = resources.files(self.data) / 'utf-8.file' with resources.as_file(target) as path: path.unlink() diff --git a/Lib/test/test_importlib/resources/test_read.py b/Lib/test/test_importlib/resources/test_read.py index 0ca8ee9d02856b..088982681e8b0c 100644 --- a/Lib/test/test_importlib/resources/test_read.py +++ b/Lib/test/test_importlib/resources/test_read.py @@ -12,7 +12,7 @@ def execute(self, package, path): class CommonTextTests(util.CommonTests, unittest.TestCase): def execute(self, package, path): - resources.files(package).joinpath(path).read_text() + resources.files(package).joinpath(path).read_text(encoding='utf-8') class ReadTests: @@ -21,7 +21,11 @@ def test_read_bytes(self): self.assertEqual(result, b'\0\1\2\3') def test_read_text_default_encoding(self): - result = resources.files(self.data).joinpath('utf-8.file').read_text() + result = ( + resources.files(self.data) + .joinpath('utf-8.file') + .read_text(encoding='utf-8') + ) self.assertEqual(result, 'Hello, UTF-8 world!\n') def test_read_text_given_encoding(self): @@ -33,7 +37,9 @@ def test_read_text_given_encoding(self): self.assertEqual(result, 'Hello, UTF-16 world!\n') def test_read_text_with_errors(self): - # Raises UnicodeError without the 'errors' argument. + """ + Raises UnicodeError without the 'errors' argument. + """ target = resources.files(self.data) / 'utf-16.file' self.assertRaises(UnicodeError, target.read_text, encoding='utf-8') result = target.read_text(encoding='utf-8', errors='ignore') diff --git a/Lib/test/test_importlib/resources/test_reader.py b/Lib/test/test_importlib/resources/test_reader.py index 4fd9e6bbe4281c..8670f72a334585 100644 --- a/Lib/test/test_importlib/resources/test_reader.py +++ b/Lib/test/test_importlib/resources/test_reader.py @@ -81,6 +81,17 @@ def test_join_path_compound(self): path = MultiplexedPath(self.folder) assert not path.joinpath('imaginary/foo.py').exists() + def test_join_path_common_subdir(self): + prefix = os.path.abspath(os.path.join(__file__, '..')) + data01 = os.path.join(prefix, 'data01') + data02 = os.path.join(prefix, 'data02') + path = MultiplexedPath(data01, data02) + self.assertIsInstance(path.joinpath('subdirectory'), MultiplexedPath) + self.assertEqual( + str(path.joinpath('subdirectory', 'subsubdir'))[len(prefix) + 1 :], + os.path.join('data02', 'subdirectory', 'subsubdir'), + ) + def test_repr(self): self.assertEqual( repr(MultiplexedPath(self.folder)), diff --git a/Lib/test/test_importlib/resources/test_resource.py b/Lib/test/test_importlib/resources/test_resource.py index f7e3abbdc805a7..6f75cf57f03d02 100644 --- a/Lib/test/test_importlib/resources/test_resource.py +++ b/Lib/test/test_importlib/resources/test_resource.py @@ -1,3 +1,4 @@ +import contextlib import sys import unittest import uuid @@ -7,7 +8,7 @@ from . import zipdata01, zipdata02 from . import util from importlib import resources, import_module -from test.support import import_helper +from test.support import import_helper, os_helper from test.support.os_helper import unlink @@ -69,10 +70,12 @@ def test_resource_missing(self): class ResourceCornerCaseTests(unittest.TestCase): def test_package_has_no_reader_fallback(self): - # Test odd ball packages which: + """ + Test odd ball packages which: # 1. Do not have a ResourceReader as a loader # 2. Are not on the file system # 3. Are not in a zip file + """ module = util.create_package( file=data01, path=data01.__file__, contents=['A', 'B', 'C'] ) @@ -138,82 +141,71 @@ def test_unrelated_contents(self): ) +@contextlib.contextmanager +def zip_on_path(dir): + data_path = pathlib.Path(zipdata01.__file__) + source_zip_path = data_path.parent.joinpath('ziptestdata.zip') + zip_path = pathlib.Path(dir) / f'{uuid.uuid4()}.zip' + zip_path.write_bytes(source_zip_path.read_bytes()) + sys.path.append(str(zip_path)) + import_module('ziptestdata') + + try: + yield + finally: + with contextlib.suppress(ValueError): + sys.path.remove(str(zip_path)) + + with contextlib.suppress(KeyError): + del sys.path_importer_cache[str(zip_path)] + del sys.modules['ziptestdata'] + + with contextlib.suppress(OSError): + unlink(zip_path) + + class DeletingZipsTest(unittest.TestCase): """Having accessed resources in a zip file should not keep an open reference to the zip. """ - ZIP_MODULE = zipdata01 - def setUp(self): + self.fixtures = contextlib.ExitStack() + self.addCleanup(self.fixtures.close) + modules = import_helper.modules_setup() self.addCleanup(import_helper.modules_cleanup, *modules) - data_path = pathlib.Path(self.ZIP_MODULE.__file__) - data_dir = data_path.parent - self.source_zip_path = data_dir / 'ziptestdata.zip' - self.zip_path = pathlib.Path(f'{uuid.uuid4()}.zip').absolute() - self.zip_path.write_bytes(self.source_zip_path.read_bytes()) - sys.path.append(str(self.zip_path)) - self.data = import_module('ziptestdata') - - def tearDown(self): - try: - sys.path.remove(str(self.zip_path)) - except ValueError: - pass - - try: - del sys.path_importer_cache[str(self.zip_path)] - del sys.modules[self.data.__name__] - except KeyError: - pass - - try: - unlink(self.zip_path) - except OSError: - # If the test fails, this will probably fail too - pass + temp_dir = self.fixtures.enter_context(os_helper.temp_dir()) + self.fixtures.enter_context(zip_on_path(temp_dir)) def test_iterdir_does_not_keep_open(self): - c = [item.name for item in resources.files('ziptestdata').iterdir()] - self.zip_path.unlink() - del c + [item.name for item in resources.files('ziptestdata').iterdir()] def test_is_file_does_not_keep_open(self): - c = resources.files('ziptestdata').joinpath('binary.file').is_file() - self.zip_path.unlink() - del c + resources.files('ziptestdata').joinpath('binary.file').is_file() def test_is_file_failure_does_not_keep_open(self): - c = resources.files('ziptestdata').joinpath('not-present').is_file() - self.zip_path.unlink() - del c + resources.files('ziptestdata').joinpath('not-present').is_file() @unittest.skip("Desired but not supported.") def test_as_file_does_not_keep_open(self): # pragma: no cover - c = resources.as_file(resources.files('ziptestdata') / 'binary.file') - self.zip_path.unlink() - del c + resources.as_file(resources.files('ziptestdata') / 'binary.file') def test_entered_path_does_not_keep_open(self): - # This is what certifi does on import to make its bundle - # available for the process duration. - c = resources.as_file( - resources.files('ziptestdata') / 'binary.file' - ).__enter__() - self.zip_path.unlink() - del c + """ + Mimic what certifi does on import to make its bundle + available for the process duration. + """ + resources.as_file(resources.files('ziptestdata') / 'binary.file').__enter__() def test_read_binary_does_not_keep_open(self): - c = resources.files('ziptestdata').joinpath('binary.file').read_bytes() - self.zip_path.unlink() - del c + resources.files('ziptestdata').joinpath('binary.file').read_bytes() def test_read_text_does_not_keep_open(self): - c = resources.files('ziptestdata').joinpath('utf-8.file').read_text() - self.zip_path.unlink() - del c + resources.files('ziptestdata').joinpath('utf-8.file').read_text( + encoding='utf-8' + ) class ResourceFromNamespaceTest01(unittest.TestCase): diff --git a/Lib/test/test_importlib/resources/util.py b/Lib/test/test_importlib/resources/util.py index 1e72b91ff6b31f..dbe6ee81476699 100644 --- a/Lib/test/test_importlib/resources/util.py +++ b/Lib/test/test_importlib/resources/util.py @@ -80,32 +80,44 @@ def execute(self, package, path): """ def test_package_name(self): - # Passing in the package name should succeed. + """ + Passing in the package name should succeed. + """ self.execute(data01.__name__, 'utf-8.file') def test_package_object(self): - # Passing in the package itself should succeed. + """ + Passing in the package itself should succeed. + """ self.execute(data01, 'utf-8.file') def test_string_path(self): - # Passing in a string for the path should succeed. + """ + Passing in a string for the path should succeed. + """ path = 'utf-8.file' self.execute(data01, path) def test_pathlib_path(self): - # Passing in a pathlib.PurePath object for the path should succeed. + """ + Passing in a pathlib.PurePath object for the path should succeed. + """ path = pathlib.PurePath('utf-8.file') self.execute(data01, path) def test_importing_module_as_side_effect(self): - # The anchor package can already be imported. + """ + The anchor package can already be imported. + """ del sys.modules[data01.__name__] self.execute(data01.__name__, 'utf-8.file') def test_missing_path(self): - # Attempting to open or read or request the path for a - # non-existent path should succeed if open_resource - # can return a viable data stream. + """ + Attempting to open or read or request the path for a + non-existent path should succeed if open_resource + can return a viable data stream. + """ bytes_data = io.BytesIO(b'Hello, world!') package = create_package(file=bytes_data, path=FileNotFoundError()) self.execute(package, 'utf-8.file') diff --git a/Misc/NEWS.d/next/Library/2023-02-17-19-00-58.gh-issue-97930.C_nQjb.rst b/Misc/NEWS.d/next/Library/2023-02-17-19-00-58.gh-issue-97930.C_nQjb.rst new file mode 100644 index 00000000000000..967e13f752bcd1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-17-19-00-58.gh-issue-97930.C_nQjb.rst @@ -0,0 +1,4 @@ +Apply changes from `importlib_resources 5.12 +`_, +including fix for ``MultiplexedPath`` to support directories in multiple +namespaces (python/importlib_resources#265). From 61f1e67c6fcbf80eb9be2b75f7d62954e28c89e6 Mon Sep 17 00:00:00 2001 From: Furkan Onder Date: Sun, 19 Feb 2023 00:22:02 +0000 Subject: [PATCH 129/247] GH-84783: Make the slice object hashable (GH-101264) --- Doc/library/functions.rst | 3 ++ Lib/test/test_capi/test_misc.py | 7 +--- Lib/test/test_doctest.py | 2 +- Lib/test/test_slice.py | 12 ++++-- ...3-02-11-23-14-06.gh-issue-84783._P5sMa.rst | 1 + Objects/sliceobject.c | 38 ++++++++++++++++++- Objects/tupleobject.c | 2 +- 7 files changed, 53 insertions(+), 12 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-11-23-14-06.gh-issue-84783._P5sMa.rst diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 658d6768457d16..3ff28849025153 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -1635,6 +1635,9 @@ are always available. They are listed here in alphabetical order. example: ``a[start:stop:step]`` or ``a[start:stop, i]``. See :func:`itertools.islice` for an alternate version that returns an iterator. + .. versionchanged:: 3.12 + Slice objects are now :term:`hashable` (provided :attr:`~slice.start`, + :attr:`~slice.stop`, and :attr:`~slice.step` are hashable). .. function:: sorted(iterable, /, *, key=None, reverse=False) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index f26b4723d1e68b..ad099c61463b66 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -419,11 +419,6 @@ def __setitem__(self, index, value): with self.assertRaises(TypeError): _testcapi.sequence_set_slice(None, 1, 3, 'xy') - mapping = {1: 'a', 2: 'b', 3: 'c'} - with self.assertRaises(TypeError): - _testcapi.sequence_set_slice(mapping, 1, 3, 'xy') - self.assertEqual(mapping, {1: 'a', 2: 'b', 3: 'c'}) - def test_sequence_del_slice(self): # Correct case: data = [1, 2, 3, 4, 5] @@ -459,7 +454,7 @@ def __delitem__(self, index): _testcapi.sequence_del_slice(None, 1, 3) mapping = {1: 'a', 2: 'b', 3: 'c'} - with self.assertRaises(TypeError): + with self.assertRaises(KeyError): _testcapi.sequence_del_slice(mapping, 1, 3) self.assertEqual(mapping, {1: 'a', 2: 'b', 3: 'c'}) diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py index 65e215f1cdda4a..3491d4cdb1c18b 100644 --- a/Lib/test/test_doctest.py +++ b/Lib/test/test_doctest.py @@ -707,7 +707,7 @@ def non_Python_modules(): r""" >>> import builtins >>> tests = doctest.DocTestFinder().find(builtins) - >>> 825 < len(tests) < 845 # approximate number of objects with docstrings + >>> 830 < len(tests) < 850 # approximate number of objects with docstrings True >>> real_tests = [t for t in tests if len(t.examples) > 0] >>> len(real_tests) # objects that actually have doctests diff --git a/Lib/test/test_slice.py b/Lib/test/test_slice.py index 03fde3275e1475..c35a2293f790a2 100644 --- a/Lib/test/test_slice.py +++ b/Lib/test/test_slice.py @@ -80,10 +80,16 @@ def test_repr(self): self.assertEqual(repr(slice(1, 2, 3)), "slice(1, 2, 3)") def test_hash(self): - # Verify clearing of SF bug #800796 - self.assertRaises(TypeError, hash, slice(5)) + self.assertEqual(hash(slice(5)), slice(5).__hash__()) + self.assertEqual(hash(slice(1, 2)), slice(1, 2).__hash__()) + self.assertEqual(hash(slice(1, 2, 3)), slice(1, 2, 3).__hash__()) + self.assertNotEqual(slice(5), slice(6)) + + with self.assertRaises(TypeError): + hash(slice(1, 2, [])) + with self.assertRaises(TypeError): - slice(5).__hash__() + hash(slice(4, {})) def test_cmp(self): s1 = slice(1, 2, 3) diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-11-23-14-06.gh-issue-84783._P5sMa.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-11-23-14-06.gh-issue-84783._P5sMa.rst new file mode 100644 index 00000000000000..e1c851a0825a7f --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-11-23-14-06.gh-issue-84783._P5sMa.rst @@ -0,0 +1 @@ +Make the slice object hashable. diff --git a/Objects/sliceobject.c b/Objects/sliceobject.c index 5694bd9c661fa5..5d2e6ad522bcf2 100644 --- a/Objects/sliceobject.c +++ b/Objects/sliceobject.c @@ -628,6 +628,42 @@ slice_traverse(PySliceObject *v, visitproc visit, void *arg) return 0; } +/* code based on tuplehash() of Objects/tupleobject.c */ +#if SIZEOF_PY_UHASH_T > 4 +#define _PyHASH_XXPRIME_1 ((Py_uhash_t)11400714785074694791ULL) +#define _PyHASH_XXPRIME_2 ((Py_uhash_t)14029467366897019727ULL) +#define _PyHASH_XXPRIME_5 ((Py_uhash_t)2870177450012600261ULL) +#define _PyHASH_XXROTATE(x) ((x << 31) | (x >> 33)) /* Rotate left 31 bits */ +#else +#define _PyHASH_XXPRIME_1 ((Py_uhash_t)2654435761UL) +#define _PyHASH_XXPRIME_2 ((Py_uhash_t)2246822519UL) +#define _PyHASH_XXPRIME_5 ((Py_uhash_t)374761393UL) +#define _PyHASH_XXROTATE(x) ((x << 13) | (x >> 19)) /* Rotate left 13 bits */ +#endif + +static Py_hash_t +slicehash(PySliceObject *v) +{ + Py_uhash_t acc = _PyHASH_XXPRIME_5; +#define _PyHASH_SLICE_PART(com) { \ + Py_uhash_t lane = PyObject_Hash(v->com); \ + if(lane == (Py_uhash_t)-1) { \ + return -1; \ + } \ + acc += lane * _PyHASH_XXPRIME_2; \ + acc = _PyHASH_XXROTATE(acc); \ + acc *= _PyHASH_XXPRIME_1; \ +} + _PyHASH_SLICE_PART(start); + _PyHASH_SLICE_PART(stop); + _PyHASH_SLICE_PART(step); +#undef _PyHASH_SLICE_PART + if(acc == (Py_uhash_t)-1) { + return 1546275796; + } + return acc; +} + PyTypeObject PySlice_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "slice", /* Name of this type */ @@ -642,7 +678,7 @@ PyTypeObject PySlice_Type = { 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ - PyObject_HashNotImplemented, /* tp_hash */ + (hashfunc)slicehash, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c index e1b9953226c0d7..7d6d0e1bea249e 100644 --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -288,7 +288,7 @@ tuplerepr(PyTupleObject *v) /* Hash for tuples. This is a slightly simplified version of the xxHash non-cryptographic hash: - - we do not use any parallellism, there is only 1 accumulator. + - we do not use any parallelism, there is only 1 accumulator. - we drop the final mixing since this is just a permutation of the output space: it does not help against collisions. - at the end, we mangle the length with a single constant. From 36b670908b3546f46283aae4dbf311e53289f3d1 Mon Sep 17 00:00:00 2001 From: Reza Rastak Date: Sat, 18 Feb 2023 19:55:43 -0500 Subject: [PATCH 130/247] Fix incorrectly documented attribute in csv docs (#101250) --- Doc/library/csv.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/csv.rst b/Doc/library/csv.rst index 41f11505aa1030..f1776554d8b9f2 100644 --- a/Doc/library/csv.rst +++ b/Doc/library/csv.rst @@ -458,7 +458,7 @@ Reader objects have the following public attributes: DictReader objects have the following public attribute: -.. attribute:: csvreader.fieldnames +.. attribute:: DictReader.fieldnames If not passed as a parameter when creating the object, this attribute is initialized upon first access or when the first record is read from the From 6aab56f3c2ee331116eba242d2fcdca592577328 Mon Sep 17 00:00:00 2001 From: Patricio Paez Date: Sat, 18 Feb 2023 19:06:03 -0600 Subject: [PATCH 131/247] gh-99735: Use required=True in argparse subparsers example (#100927) --- Doc/library/argparse.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst index dbaa5d0d9b995b..34b4c61649b99f 100644 --- a/Doc/library/argparse.rst +++ b/Doc/library/argparse.rst @@ -1867,7 +1867,7 @@ Sub-commands ... >>> # create the top-level parser >>> parser = argparse.ArgumentParser() - >>> subparsers = parser.add_subparsers() + >>> subparsers = parser.add_subparsers(required=True) >>> >>> # create the parser for the "foo" command >>> parser_foo = subparsers.add_parser('foo') From 072935951f7cd44b40ee37fe561478b2e431c2fb Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 18 Feb 2023 21:32:50 -0500 Subject: [PATCH 132/247] gh-97930: Also include subdirectory in makefile. (#102030) --- Makefile.pre.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile.pre.in b/Makefile.pre.in index 490483a712014c..b28c6067a535b8 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -2075,6 +2075,8 @@ TESTSUBDIRS= idlelib/idle_test \ test/test_importlib/resources/data01/subdirectory \ test/test_importlib/resources/data02 \ test/test_importlib/resources/data02/one \ + test/test_importlib/resources/data02/subdirectory \ + test/test_importlib/resources/data02/subdirectory/subsubdir \ test/test_importlib/resources/data02/two \ test/test_importlib/resources/data03 \ test/test_importlib/resources/data03/namespace \ From 9a07eff628c1cd88b7cdda88a8fd0db3fe7ea552 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Amiel Date: Sun, 19 Feb 2023 11:18:12 +0100 Subject: [PATCH 133/247] gh-100210: Correct the comment link for unescaping HTML (#100212) gh-100210: correct the comment link for unescaping HTML --- Lib/html/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/html/__init__.py b/Lib/html/__init__.py index da0a0a3ce70eed..1543460ca33b0a 100644 --- a/Lib/html/__init__.py +++ b/Lib/html/__init__.py @@ -25,7 +25,7 @@ def escape(s, quote=True): return s -# see http://www.w3.org/TR/html5/syntax.html#tokenizing-character-references +# see https://html.spec.whatwg.org/multipage/parsing.html#numeric-character-reference-end-state _invalid_charrefs = { 0x00: '\ufffd', # REPLACEMENT CHARACTER From 71f614ef2a3d66213b9cae807cbbc1ed03741221 Mon Sep 17 00:00:00 2001 From: Owain Davies <116417456+OTheDev@users.noreply.github.com> Date: Sun, 19 Feb 2023 22:00:59 +0700 Subject: [PATCH 134/247] Add missing 'is' to `cmath.log()` docstring (#102049) Fix missing 'is' in cmath.log() docstring --- Modules/clinic/cmathmodule.c.h | 4 ++-- Modules/cmathmodule.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Modules/clinic/cmathmodule.c.h b/Modules/clinic/cmathmodule.c.h index b1da9452c61db8..941448e76e80de 100644 --- a/Modules/clinic/cmathmodule.c.h +++ b/Modules/clinic/cmathmodule.c.h @@ -644,7 +644,7 @@ PyDoc_STRVAR(cmath_log__doc__, "\n" "log(z[, base]) -> the logarithm of z to the given base.\n" "\n" -"If the base not specified, returns the natural logarithm (base e) of z."); +"If the base is not specified, returns the natural logarithm (base e) of z."); #define CMATH_LOG_METHODDEF \ {"log", _PyCFunction_CAST(cmath_log), METH_FASTCALL, cmath_log__doc__}, @@ -982,4 +982,4 @@ cmath_isclose(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec exit: return return_value; } -/*[clinic end generated code: output=0146c656e67f5d5f input=a9049054013a1b77]*/ +/*[clinic end generated code: output=87f609786ef270cd input=a9049054013a1b77]*/ diff --git a/Modules/cmathmodule.c b/Modules/cmathmodule.c index 2038ac26e65857..53e34061d53773 100644 --- a/Modules/cmathmodule.c +++ b/Modules/cmathmodule.c @@ -957,12 +957,12 @@ cmath.log log(z[, base]) -> the logarithm of z to the given base. -If the base not specified, returns the natural logarithm (base e) of z. +If the base is not specified, returns the natural logarithm (base e) of z. [clinic start generated code]*/ static PyObject * cmath_log_impl(PyObject *module, Py_complex x, PyObject *y_obj) -/*[clinic end generated code: output=4effdb7d258e0d94 input=230ed3a71ecd000a]*/ +/*[clinic end generated code: output=4effdb7d258e0d94 input=e1f81d4fcfd26497]*/ { Py_complex y; From 32df540635cacce1053ee0ef98ee23f3f6a43c02 Mon Sep 17 00:00:00 2001 From: neuralstring <107343209+neuralstring@users.noreply.github.com> Date: Sun, 19 Feb 2023 16:39:03 +0000 Subject: [PATCH 135/247] gh-100425: Update tutorial docs related to sum() accuracy (FH-101854) --- Doc/tutorial/stdlib2.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/tutorial/stdlib2.rst b/Doc/tutorial/stdlib2.rst index 0c101c1f207235..33f311db3a24d2 100644 --- a/Doc/tutorial/stdlib2.rst +++ b/Doc/tutorial/stdlib2.rst @@ -394,7 +394,7 @@ point:: >>> sum([Decimal('0.1')]*10) == Decimal('1.0') True - >>> sum([0.1]*10) == 1.0 + >>> 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 == 1.0 False The :mod:`decimal` module provides arithmetic with as much precision as needed:: From b513c46d998344dc07eb6d510782c2e23d2b859e Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sun, 19 Feb 2023 19:15:44 +0000 Subject: [PATCH 136/247] gh-85417: Clarify behaviour on branch cuts in cmath module (#102046) This PR updates the cmath module documentation to reflect the reality that Python is almost always (and as far as I can tell, that "almost" can be omitted) running on a machine whose C double supports signed zeros. * Removes misleading references to functions being continuous from above / below / the left / the right at branch cuts * Expands the note on branch cuts at the top of the module documentation to explain the double-sided sign-of-zero-based behaviour --- Doc/library/cmath.rst | 66 +++++++++++-------- ...3-02-19-10-33-01.gh-issue-85417.kYO8u3.rst | 1 + 2 files changed, 39 insertions(+), 28 deletions(-) create mode 100644 Misc/NEWS.d/next/Documentation/2023-02-19-10-33-01.gh-issue-85417.kYO8u3.rst diff --git a/Doc/library/cmath.rst b/Doc/library/cmath.rst index 28cd96b0e12da9..5ed7a09b3e9db2 100644 --- a/Doc/library/cmath.rst +++ b/Doc/library/cmath.rst @@ -15,11 +15,27 @@ the function is then applied to the result of the conversion. .. note:: - On platforms with hardware and system-level support for signed - zeros, functions involving branch cuts are continuous on *both* - sides of the branch cut: the sign of the zero distinguishes one - side of the branch cut from the other. On platforms that do not - support signed zeros the continuity is as specified below. + For functions involving branch cuts, we have the problem of deciding how to + define those functions on the cut itself. Following Kahan's "Branch cuts for + complex elementary functions" paper, as well as Annex G of C99 and later C + standards, we use the sign of zero to distinguish one side of the branch cut + from the other: for a branch cut along (a portion of) the real axis we look + at the sign of the imaginary part, while for a branch cut along the + imaginary axis we look at the sign of the real part. + + For example, the :func:`cmath.sqrt` function has a branch cut along the + negative real axis. An argument of ``complex(-2.0, -0.0)`` is treated as + though it lies *below* the branch cut, and so gives a result on the negative + imaginary axis:: + + >>> cmath.sqrt(complex(-2.0, -0.0)) + -1.4142135623730951j + + But an argument of ``complex(-2.0, 0.0)`` is treated as though it lies above + the branch cut:: + + >>> cmath.sqrt(complex(-2.0, 0.0)) + 1.4142135623730951j Conversions to and from polar coordinates @@ -44,14 +60,11 @@ rectangular coordinates to polar coordinates and back. .. function:: phase(x) - Return the phase of *x* (also known as the *argument* of *x*), as a - float. ``phase(x)`` is equivalent to ``math.atan2(x.imag, - x.real)``. The result lies in the range [-\ *π*, *π*], and the branch - cut for this operation lies along the negative real axis, - continuous from above. On systems with support for signed zeros - (which includes most systems in current use), this means that the - sign of the result is the same as the sign of ``x.imag``, even when - ``x.imag`` is zero:: + Return the phase of *x* (also known as the *argument* of *x*), as a float. + ``phase(x)`` is equivalent to ``math.atan2(x.imag, x.real)``. The result + lies in the range [-\ *π*, *π*], and the branch cut for this operation lies + along the negative real axis. The sign of the result is the same as the + sign of ``x.imag``, even when ``x.imag`` is zero:: >>> phase(complex(-1.0, 0.0)) 3.141592653589793 @@ -92,8 +105,8 @@ Power and logarithmic functions .. function:: log(x[, base]) Returns the logarithm of *x* to the given *base*. If the *base* is not - specified, returns the natural logarithm of *x*. There is one branch cut, from 0 - along the negative real axis to -∞, continuous from above. + specified, returns the natural logarithm of *x*. There is one branch cut, + from 0 along the negative real axis to -∞. .. function:: log10(x) @@ -112,9 +125,9 @@ Trigonometric functions .. function:: acos(x) - Return the arc cosine of *x*. There are two branch cuts: One extends right from - 1 along the real axis to ∞, continuous from below. The other extends left from - -1 along the real axis to -∞, continuous from above. + Return the arc cosine of *x*. There are two branch cuts: One extends right + from 1 along the real axis to ∞. The other extends left from -1 along the + real axis to -∞. .. function:: asin(x) @@ -125,9 +138,8 @@ Trigonometric functions .. function:: atan(x) Return the arc tangent of *x*. There are two branch cuts: One extends from - ``1j`` along the imaginary axis to ``∞j``, continuous from the right. The - other extends from ``-1j`` along the imaginary axis to ``-∞j``, continuous - from the left. + ``1j`` along the imaginary axis to ``∞j``. The other extends from ``-1j`` + along the imaginary axis to ``-∞j``. .. function:: cos(x) @@ -151,23 +163,21 @@ Hyperbolic functions .. function:: acosh(x) Return the inverse hyperbolic cosine of *x*. There is one branch cut, - extending left from 1 along the real axis to -∞, continuous from above. + extending left from 1 along the real axis to -∞. .. function:: asinh(x) Return the inverse hyperbolic sine of *x*. There are two branch cuts: - One extends from ``1j`` along the imaginary axis to ``∞j``, - continuous from the right. The other extends from ``-1j`` along - the imaginary axis to ``-∞j``, continuous from the left. + One extends from ``1j`` along the imaginary axis to ``∞j``. The other + extends from ``-1j`` along the imaginary axis to ``-∞j``. .. function:: atanh(x) Return the inverse hyperbolic tangent of *x*. There are two branch cuts: One - extends from ``1`` along the real axis to ``∞``, continuous from below. The - other extends from ``-1`` along the real axis to ``-∞``, continuous from - above. + extends from ``1`` along the real axis to ``∞``. The other extends from + ``-1`` along the real axis to ``-∞``. .. function:: cosh(x) diff --git a/Misc/NEWS.d/next/Documentation/2023-02-19-10-33-01.gh-issue-85417.kYO8u3.rst b/Misc/NEWS.d/next/Documentation/2023-02-19-10-33-01.gh-issue-85417.kYO8u3.rst new file mode 100644 index 00000000000000..a5532df14795d2 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2023-02-19-10-33-01.gh-issue-85417.kYO8u3.rst @@ -0,0 +1 @@ +Update :mod:`cmath` documentation to clarify behaviour on branch cuts. From 3b264df470c82d77f5b01c6f9d1d7173d1cb9597 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sun, 19 Feb 2023 13:21:37 -0600 Subject: [PATCH 137/247] Misc improvements to the float tutorial (GH-102052) --- Doc/tutorial/floatingpoint.rst | 139 +++++++++++++++++++++++++-------- 1 file changed, 106 insertions(+), 33 deletions(-) diff --git a/Doc/tutorial/floatingpoint.rst b/Doc/tutorial/floatingpoint.rst index cedade6e336608..306b1eba3c45b8 100644 --- a/Doc/tutorial/floatingpoint.rst +++ b/Doc/tutorial/floatingpoint.rst @@ -1,6 +1,7 @@ .. testsetup:: import math + from fractions import Fraction .. _tut-fp-issues: @@ -9,12 +10,13 @@ Floating Point Arithmetic: Issues and Limitations ************************************************** .. sectionauthor:: Tim Peters +.. sectionauthor:: Raymond Hettinger Floating-point numbers are represented in computer hardware as base 2 (binary) -fractions. For example, the **decimal** fraction ``0.125`` -has value 1/10 + 2/100 + 5/1000, and in the same way the **binary** fraction ``0.001`` -has value 0/2 + 0/4 + 1/8. These two fractions have identical values, the only +fractions. For example, the **decimal** fraction ``0.625`` +has value 6/10 + 2/100 + 5/1000, and in the same way the **binary** fraction ``0.101`` +has value 1/2 + 0/4 + 1/8. These two fractions have identical values, the only real difference being that the first is written in base 10 fractional notation, and the second in base 2. @@ -57,13 +59,15 @@ Many users are not aware of the approximation because of the way values are displayed. Python only prints a decimal approximation to the true decimal value of the binary approximation stored by the machine. On most machines, if Python were to print the true decimal value of the binary approximation stored -for 0.1, it would have to display :: +for 0.1, it would have to display:: >>> 0.1 0.1000000000000000055511151231257827021181583404541015625 That is more digits than most people find useful, so Python keeps the number -of digits manageable by displaying a rounded value instead :: +of digits manageable by displaying a rounded value instead: + +.. doctest:: >>> 1 / 10 0.1 @@ -90,7 +94,10 @@ thing in all languages that support your hardware's floating-point arithmetic (although some languages may not *display* the difference by default, or in all output modes). -For more pleasant output, you may wish to use string formatting to produce a limited number of significant digits:: +For more pleasant output, you may wish to use string formatting to produce a +limited number of significant digits: + +.. doctest:: >>> format(math.pi, '.12g') # give 12 significant digits '3.14159265359' @@ -101,33 +108,49 @@ For more pleasant output, you may wish to use string formatting to produce a lim >>> repr(math.pi) '3.141592653589793' - It's important to realize that this is, in a real sense, an illusion: you're simply rounding the *display* of the true machine value. One illusion may beget another. For example, since 0.1 is not exactly 1/10, -summing three values of 0.1 may not yield exactly 0.3, either:: +summing three values of 0.1 may not yield exactly 0.3, either: + +.. doctest:: - >>> .1 + .1 + .1 == .3 + >>> 0.1 + 0.1 + 0.1 == 0.3 False Also, since the 0.1 cannot get any closer to the exact value of 1/10 and 0.3 cannot get any closer to the exact value of 3/10, then pre-rounding with -:func:`round` function cannot help:: +:func:`round` function cannot help: - >>> round(.1, 1) + round(.1, 1) + round(.1, 1) == round(.3, 1) +.. doctest:: + + >>> round(0.1, 1) + round(0.1, 1) + round(0.1, 1) == round(0.3, 1) False Though the numbers cannot be made closer to their intended exact values, -the :func:`round` function can be useful for post-rounding so that results -with inexact values become comparable to one another:: +the :func:`math.isclose` function can be useful for comparing inexact values: - >>> round(.1 + .1 + .1, 10) == round(.3, 10) - True +.. doctest:: + + >>> math.isclose(0.1 + 0.1 + 0.1, 0.3) + True + +Alternatively, the :func:`round` function can be used to compare rough +approximations:: + +.. doctest:: + + >>> round(math.pi, ndigits=2) == round(22 / 7, ndigits=2) + True Binary floating-point arithmetic holds many surprises like this. The problem with "0.1" is explained in precise detail below, in the "Representation Error" -section. See `The Perils of Floating Point `_ +section. See `Examples of Floating Point Problems +`_ for +a pleasant summary of how binary floating point works and the kinds of +problems commonly encountered in practice. Also see +`The Perils of Floating Point `_ for a more complete account of other common surprises. As that says near the end, "there are no easy answers." Still, don't be unduly @@ -158,26 +181,34 @@ statistical operations supplied by the SciPy project. See . Python provides tools that may help on those rare occasions when you really *do* want to know the exact value of a float. The :meth:`float.as_integer_ratio` method expresses the value of a float as a -fraction:: +fraction: + +.. doctest:: >>> x = 3.14159 >>> x.as_integer_ratio() (3537115888337719, 1125899906842624) Since the ratio is exact, it can be used to losslessly recreate the -original value:: +original value: + +.. doctest:: >>> x == 3537115888337719 / 1125899906842624 True The :meth:`float.hex` method expresses a float in hexadecimal (base -16), again giving the exact value stored by your computer:: +16), again giving the exact value stored by your computer: + +.. doctest:: >>> x.hex() '0x1.921f9f01b866ep+1' This precise hexadecimal representation can be used to reconstruct -the float value exactly:: +the float value exactly: + +.. doctest:: >>> x == float.fromhex('0x1.921f9f01b866ep+1') True @@ -186,17 +217,43 @@ Since the representation is exact, it is useful for reliably porting values across different versions of Python (platform independence) and exchanging data with other languages that support the same format (such as Java and C99). -Another helpful tool is the :func:`math.fsum` function which helps mitigate -loss-of-precision during summation. It tracks "lost digits" as values are -added onto a running total. That can make a difference in overall accuracy -so that the errors do not accumulate to the point where they affect the -final total: +Another helpful tool is the :func:`sum` function which helps mitigate +loss-of-precision during summation. It uses extended precision for +intermediate rounding steps as values are added onto a running total. +That can make a difference in overall accuracy so that the errors do not +accumulate to the point where they affect the final total: + +.. doctest:: >>> 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 == 1.0 False - >>> math.fsum([0.1] * 10) == 1.0 + >>> sum([0.1] * 10) == 1.0 True +The :func:`math.fsum()` goes further and tracks all of the "lost digits" +as values are added onto a running total so that the result has only a +single rounding. This is slower than :func:`sum` but will be more +accurate in uncommon cases where large magnitude inputs mostly cancel +each other out leaving a final sum near zero: + +.. doctest:: + + >>> arr = [-0.10430216751806065, -266310978.67179024, 143401161448607.16, + ... -143401161400469.7, 266262841.31058735, -0.003244936839808227] + >>> float(sum(map(Fraction, arr))) # Exact summation with single rounding + 8.042173697819788e-13 + >>> math.fsum(arr) # Single rounding + 8.042173697819788e-13 + >>> sum(arr) # Multiple roundings in extended precision + 8.042178034628478e-13 + >>> total = 0.0 + >>> for x in arr: + ... total += x # Multiple roundings in standard precision + ... + >>> total # Straight addition has no correct digits! + -0.0051575902860057365 + + .. _tut-fp-error: Representation Error @@ -225,20 +282,28 @@ as :: J ~= 2**N / 10 and recalling that *J* has exactly 53 bits (is ``>= 2**52`` but ``< 2**53``), -the best value for *N* is 56:: +the best value for *N* is 56: + +.. doctest:: >>> 2**52 <= 2**56 // 10 < 2**53 True That is, 56 is the only value for *N* that leaves *J* with exactly 53 bits. The -best possible value for *J* is then that quotient rounded:: +best possible value for *J* is then that quotient rounded: + +.. doctest:: >>> q, r = divmod(2**56, 10) >>> r 6 Since the remainder is more than half of 10, the best approximation is obtained -by rounding up:: +by rounding up: + +.. doctest:: + + >>> q+1 7205759403792794 @@ -256,13 +321,17 @@ if we had not rounded up, the quotient would have been a little bit smaller than 1/10. But in no case can it be *exactly* 1/10! So the computer never "sees" 1/10: what it sees is the exact fraction given -above, the best 754 double approximation it can get:: +above, the best 754 double approximation it can get: + +.. doctest:: >>> 0.1 * 2 ** 55 3602879701896397.0 If we multiply that fraction by 10\*\*55, we can see the value out to -55 decimal digits:: +55 decimal digits: + +.. doctest:: >>> 3602879701896397 * 10 ** 55 // 2 ** 55 1000000000000000055511151231257827021181583404541015625 @@ -270,13 +339,17 @@ If we multiply that fraction by 10\*\*55, we can see the value out to meaning that the exact number stored in the computer is equal to the decimal value 0.1000000000000000055511151231257827021181583404541015625. Instead of displaying the full decimal value, many languages (including -older versions of Python), round the result to 17 significant digits:: +older versions of Python), round the result to 17 significant digits: + +.. doctest:: >>> format(0.1, '.17f') '0.10000000000000001' The :mod:`fractions` and :mod:`decimal` modules make these calculations -easy:: +easy: + +.. doctest:: >>> from decimal import Decimal >>> from fractions import Fraction From 60bbed7f174e481d3fc69984430a4667951678b3 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sun, 19 Feb 2023 21:22:29 +0100 Subject: [PATCH 138/247] gh-101578: Amend PyErr_{Set,Get}RaisedException docs (#101962) Co-authored-by: C.A.M. Gerlach --- Doc/c-api/exceptions.rst | 49 ++++++++++++++-------------------------- Doc/data/refcounts.dat | 3 +++ 2 files changed, 20 insertions(+), 32 deletions(-) diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst index de9b15edd6859a..e735f8db402363 100644 --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -402,51 +402,36 @@ Querying the error indicator .. c:function:: PyObject *PyErr_GetRaisedException(void) - Returns the exception currently being raised, clearing the exception at - the same time. Do not confuse this with the exception currently being - handled which can be accessed with :c:func:`PyErr_GetHandledException`. + Return the exception currently being raised, clearing the error indicator at + the same time. - .. note:: + This function is used by code that needs to catch exceptions, + or code that needs to save and restore the error indicator temporarily. - This function is normally only used by code that needs to catch exceptions or - by code that needs to save and restore the error indicator temporarily, e.g.:: + For example:: - { - PyObject *exc = PyErr_GetRaisedException(); + { + PyObject *exc = PyErr_GetRaisedException(); - /* ... code that might produce other errors ... */ + /* ... code that might produce other errors ... */ - PyErr_SetRaisedException(exc); - } + PyErr_SetRaisedException(exc); + } + + .. seealso:: :c:func:`PyErr_GetHandledException`, + to save the exception currently being handled. .. versionadded:: 3.12 .. c:function:: void PyErr_SetRaisedException(PyObject *exc) - Sets the exception currently being raised ``exc``. - If the exception is already set, it is cleared first. - - ``exc`` must be a valid exception. - (Violating this rules will cause subtle problems later.) - This call consumes a reference to the ``exc`` object: you must own a - reference to that object before the call and after the call you no longer own - that reference. - (If you don't understand this, don't use this function. I warned you.) + Set *exc* as the exception currently being raised, + clearing the existing exception if one is set. - .. note:: - - This function is normally only used by code that needs to save and restore the - error indicator temporarily. Use :c:func:`PyErr_GetRaisedException` to save - the current exception, e.g.:: - - { - PyObject *exc = PyErr_GetRaisedException(); - - /* ... code that might produce other errors ... */ + .. warning:: - PyErr_SetRaisedException(exc); - } + This call steals a reference to *exc*, which must be a valid exception. .. versionadded:: 3.12 diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat index 349c4dd5be3d81..ed2958f8cd2205 100644 --- a/Doc/data/refcounts.dat +++ b/Doc/data/refcounts.dat @@ -606,6 +606,9 @@ PyErr_GetExcInfo:PyObject**:ptype:+1: PyErr_GetExcInfo:PyObject**:pvalue:+1: PyErr_GetExcInfo:PyObject**:ptraceback:+1: +PyErr_GetRaisedException:PyObject*::+1: +PyErr_SetRaisedException:::: + PyErr_GivenExceptionMatches:int::: PyErr_GivenExceptionMatches:PyObject*:given:0: PyErr_GivenExceptionMatches:PyObject*:exc:0: From b1b375e2670a58fc37cb4c2629ed73b045159918 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Mon, 20 Feb 2023 01:16:11 +0000 Subject: [PATCH 139/247] gh-97786: Fix compiler warnings in pytime.c (#101826) Fixes compiler warnings in pytime.c. --- ...3-02-11-13-23-29.gh-issue-97786.QjvQ1B.rst | 2 ++ Python/pytime.c | 35 +++++++++++++++---- 2 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-02-11-13-23-29.gh-issue-97786.QjvQ1B.rst diff --git a/Misc/NEWS.d/next/Library/2023-02-11-13-23-29.gh-issue-97786.QjvQ1B.rst b/Misc/NEWS.d/next/Library/2023-02-11-13-23-29.gh-issue-97786.QjvQ1B.rst new file mode 100644 index 00000000000000..df194b67590d67 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-11-13-23-29.gh-issue-97786.QjvQ1B.rst @@ -0,0 +1,2 @@ +Fix potential undefined behaviour in corner cases of floating-point-to-time +conversions. diff --git a/Python/pytime.c b/Python/pytime.c index 01c07da074757e..acd1842056af43 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -1,5 +1,4 @@ #include "Python.h" -#include "pycore_pymath.h" // _Py_InIntegralTypeRange() #ifdef MS_WINDOWS # include // struct timeval #endif @@ -41,6 +40,14 @@ # error "unsupported time_t size" #endif +#if PY_TIME_T_MAX + PY_TIME_T_MIN != -1 +# error "time_t is not a two's complement integer type" +#endif + +#if _PyTime_MIN + _PyTime_MAX != -1 +# error "_PyTime_t is not a two's complement integer type" +#endif + static void pytime_time_t_overflow(void) @@ -294,7 +301,21 @@ pytime_double_to_denominator(double d, time_t *sec, long *numerator, } assert(0.0 <= floatpart && floatpart < denominator); - if (!_Py_InIntegralTypeRange(time_t, intpart)) { + /* + Conversion of an out-of-range value to time_t gives undefined behaviour + (C99 §6.3.1.4p1), so we must guard against it. However, checking that + `intpart` is in range is delicate: the obvious expression `intpart <= + PY_TIME_T_MAX` will first convert the value `PY_TIME_T_MAX` to a double, + potentially changing its value and leading to us failing to catch some + UB-inducing values. The code below works correctly under the mild + assumption that time_t is a two's complement integer type with no trap + representation, and that `PY_TIME_T_MIN` is within the representable + range of a C double. + + Note: we want the `if` condition below to be true for NaNs; therefore, + resist any temptation to simplify by applying De Morgan's laws. + */ + if (!((double)PY_TIME_T_MIN <= intpart && intpart < -(double)PY_TIME_T_MIN)) { pytime_time_t_overflow(); return -1; } @@ -349,7 +370,8 @@ _PyTime_ObjectToTime_t(PyObject *obj, time_t *sec, _PyTime_round_t round) d = pytime_round(d, round); (void)modf(d, &intpart); - if (!_Py_InIntegralTypeRange(time_t, intpart)) { + /* See comments in pytime_double_to_denominator */ + if (!((double)PY_TIME_T_MIN <= intpart && intpart < -(double)PY_TIME_T_MIN)) { pytime_time_t_overflow(); return -1; } @@ -515,8 +537,9 @@ pytime_from_double(_PyTime_t *tp, double value, _PyTime_round_t round, d *= (double)unit_to_ns; d = pytime_round(d, round); - if (!_Py_InIntegralTypeRange(_PyTime_t, d)) { - pytime_overflow(); + /* See comments in pytime_double_to_denominator */ + if (!((double)_PyTime_MIN <= d && d < -(double)_PyTime_MIN)) { + pytime_time_t_overflow(); return -1; } _PyTime_t ns = (_PyTime_t)d; @@ -910,7 +933,7 @@ py_get_system_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise_exc) info->monotonic = 0; info->adjustable = 1; if (clock_getres(CLOCK_REALTIME, &res) == 0) { - info->resolution = res.tv_sec + res.tv_nsec * 1e-9; + info->resolution = (double)res.tv_sec + (double)res.tv_nsec * 1e-9; } else { info->resolution = 1e-9; From 27136310414965a3ea7f835e416cf74b91cefb48 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 20 Feb 2023 14:07:25 +0100 Subject: [PATCH 140/247] gh-101981: Build macOS as recommended by the devguide (GH-102070) Automerge-Triggered-By: GH:erlend-aasland --- .github/workflows/build.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 97ea2d94598e2c..acc8d936774af5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -157,12 +157,19 @@ jobs: PYTHONSTRICTEXTENSIONBUILD: 1 steps: - uses: actions/checkout@v3 - - name: Prepare homebrew environment variables + - name: Install Homebrew dependencies + run: brew install pkg-config openssl@1.1 xz gdbm tcl-tk + - name: Prepare Homebrew environment variables run: | - echo "LDFLAGS=-L$(brew --prefix tcl-tk)/lib" >> $GITHUB_ENV + echo "CFLAGS=-I$(brew --prefix gdbm)/include -I$(brew --prefix xz)/include" >> $GITHUB_ENV + echo "LDFLAGS=-L$(brew --prefix gdbm)/lib -I$(brew --prefix xz)/lib" >> $GITHUB_ENV echo "PKG_CONFIG_PATH=$(brew --prefix openssl@1.1)/lib/pkgconfig:$(brew --prefix tcl-tk)/lib/pkgconfig" >> $GITHUB_ENV - name: Configure CPython - run: ./configure --with-pydebug --prefix=/opt/python-dev + run: | + ./configure \ + --with-pydebug \ + --prefix=/opt/python-dev \ + --with-openssl="$(brew --prefix openssl@1.1)" - name: Build CPython run: make -j4 - name: Display build info From c00faf79438cc7f0d98af2679c695f747e4369a3 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 20 Feb 2023 14:46:20 +0100 Subject: [PATCH 141/247] gh-101819: Adapt _io types to heap types, batch 1 (GH-101949) Adapt StringIO, TextIOWrapper, FileIO, Buffered*, and BytesIO types. Automerge-Triggered-By: GH:erlend-aasland --- Lib/test/test_io.py | 91 +++++- Modules/_io/_iomodule.c | 99 +++--- Modules/_io/_iomodule.h | 47 ++- Modules/_io/bufferedio.c | 341 +++++++------------- Modules/_io/bytesio.c | 77 ++--- Modules/_io/clinic/bufferedio.c.h | 4 +- Modules/_io/fileio.c | 90 ++---- Modules/_io/stringio.c | 85 +++-- Modules/_io/textio.c | 99 +++--- Tools/c-analyzer/cpython/globals-to-fix.tsv | 8 - 10 files changed, 463 insertions(+), 478 deletions(-) diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index c5f2e5060a546d..9bcc92a40c61df 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -1042,6 +1042,95 @@ def close(self): support.gc_collect() self.assertIsNone(wr(), wr) +@support.cpython_only +class TestIOCTypes(unittest.TestCase): + def setUp(self): + _io = import_helper.import_module("_io") + self.types = [ + _io.BufferedRWPair, + _io.BufferedRandom, + _io.BufferedReader, + _io.BufferedWriter, + _io.BytesIO, + _io.FileIO, + _io.IncrementalNewlineDecoder, + _io.StringIO, + _io.TextIOWrapper, + _io._BufferedIOBase, + _io._BytesIOBuffer, + _io._IOBase, + _io._RawIOBase, + _io._TextIOBase, + ] + if sys.platform == "win32": + self.types.append(_io._WindowsConsoleIO) + self._io = _io + + def test_immutable_types(self): + for tp in self.types: + with self.subTest(tp=tp): + with self.assertRaisesRegex(TypeError, "immutable"): + tp.foo = "bar" + + def test_class_hierarchy(self): + def check_subs(types, base): + for tp in types: + with self.subTest(tp=tp, base=base): + self.assertTrue(issubclass(tp, base)) + + def recursive_check(d): + for k, v in d.items(): + if isinstance(v, dict): + recursive_check(v) + elif isinstance(v, set): + check_subs(v, k) + else: + self.fail("corrupt test dataset") + + _io = self._io + hierarchy = { + _io._IOBase: { + _io._BufferedIOBase: { + _io.BufferedRWPair, + _io.BufferedRandom, + _io.BufferedReader, + _io.BufferedWriter, + _io.BytesIO, + }, + _io._RawIOBase: { + _io.FileIO, + }, + _io._TextIOBase: { + _io.StringIO, + _io.TextIOWrapper, + }, + }, + } + if sys.platform == "win32": + hierarchy[_io._IOBase][_io._RawIOBase].add(_io._WindowsConsoleIO) + + recursive_check(hierarchy) + + def test_subclassing(self): + _io = self._io + dataset = {k: True for k in self.types} + dataset[_io._BytesIOBuffer] = False + + for tp, is_basetype in dataset.items(): + with self.subTest(tp=tp, is_basetype=is_basetype): + name = f"{tp.__name__}_subclass" + bases = (tp,) + if is_basetype: + _ = type(name, bases, {}) + else: + msg = "not an acceptable base type" + with self.assertRaisesRegex(TypeError, msg): + _ = type(name, bases, {}) + + def test_disallow_instantiation(self): + _io = self._io + support.check_disallow_instantiation(self, _io._BytesIOBuffer) + class PyIOTest(IOTest): pass @@ -4671,7 +4760,7 @@ def load_tests(loader, tests, pattern): CIncrementalNewlineDecoderTest, PyIncrementalNewlineDecoderTest, CTextIOWrapperTest, PyTextIOWrapperTest, CMiscIOTest, PyMiscIOTest, - CSignalsTest, PySignalsTest, + CSignalsTest, PySignalsTest, TestIOCTypes, ) # Put the namespaces of the IO module we are testing and some useful mock diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c index 811b1d221a0122..55b6535eb34b66 100644 --- a/Modules/_io/_iomodule.c +++ b/Modules/_io/_iomodule.c @@ -10,7 +10,6 @@ #define PY_SSIZE_T_CLEAN #include "Python.h" #include "_iomodule.h" -#include "pycore_moduleobject.h" // _PyModule_GetState() #include "pycore_pystate.h" // _PyInterpreterState_GET() #ifdef HAVE_SYS_TYPES_H @@ -315,8 +314,9 @@ _io_open_impl(PyObject *module, PyObject *file, const char *mode, } /* Create the Raw file stream */ + _PyIO_State *state = get_io_state(module); { - PyObject *RawIO_class = (PyObject *)&PyFileIO_Type; + PyObject *RawIO_class = (PyObject *)state->PyFileIO_Type; #ifdef MS_WINDOWS const PyConfig *config = _Py_GetConfig(); if (!config->legacy_windows_stdio && _PyIO_get_console_type(path_or_fd) != '\0') { @@ -390,12 +390,15 @@ _io_open_impl(PyObject *module, PyObject *file, const char *mode, { PyObject *Buffered_class; - if (updating) - Buffered_class = (PyObject *)&PyBufferedRandom_Type; - else if (creating || writing || appending) - Buffered_class = (PyObject *)&PyBufferedWriter_Type; - else if (reading) - Buffered_class = (PyObject *)&PyBufferedReader_Type; + if (updating) { + Buffered_class = (PyObject *)state->PyBufferedRandom_Type; + } + else if (creating || writing || appending) { + Buffered_class = (PyObject *)state->PyBufferedWriter_Type; + } + else if (reading) { + Buffered_class = (PyObject *)state->PyBufferedReader_Type; + } else { PyErr_Format(PyExc_ValueError, "unknown mode: '%s'", mode); @@ -417,7 +420,7 @@ _io_open_impl(PyObject *module, PyObject *file, const char *mode, } /* wraps into a TextIOWrapper */ - wrapper = PyObject_CallFunction((PyObject *)&PyTextIOWrapper_Type, + wrapper = PyObject_CallFunction((PyObject *)state->PyTextIOWrapper_Type, "OsssO", buffer, encoding, errors, newline, @@ -558,14 +561,6 @@ PyNumber_AsOff_t(PyObject *item, PyObject *err) return result; } -static inline _PyIO_State* -get_io_state(PyObject *module) -{ - void *state = _PyModule_GetState(module); - assert(state != NULL); - return (_PyIO_State *)state; -} - _PyIO_State * _PyIO_get_module_state(void) { @@ -587,6 +582,15 @@ iomodule_traverse(PyObject *mod, visitproc visit, void *arg) { return 0; Py_VISIT(state->locale_module); Py_VISIT(state->unsupported_operation); + + Py_VISIT(state->PyBufferedRWPair_Type); + Py_VISIT(state->PyBufferedRandom_Type); + Py_VISIT(state->PyBufferedReader_Type); + Py_VISIT(state->PyBufferedWriter_Type); + Py_VISIT(state->PyBytesIO_Type); + Py_VISIT(state->PyFileIO_Type); + Py_VISIT(state->PyStringIO_Type); + Py_VISIT(state->PyTextIOWrapper_Type); return 0; } @@ -599,6 +603,15 @@ iomodule_clear(PyObject *mod) { if (state->locale_module != NULL) Py_CLEAR(state->locale_module); Py_CLEAR(state->unsupported_operation); + + Py_CLEAR(state->PyBufferedRWPair_Type); + Py_CLEAR(state->PyBufferedRandom_Type); + Py_CLEAR(state->PyBufferedReader_Type); + Py_CLEAR(state->PyBufferedWriter_Type); + Py_CLEAR(state->PyBytesIO_Type); + Py_CLEAR(state->PyFileIO_Type); + Py_CLEAR(state->PyStringIO_Type); + Py_CLEAR(state->PyTextIOWrapper_Type); return 0; } @@ -612,7 +625,9 @@ iomodule_free(PyObject *mod) { * Module definition */ +#define clinic_state() (get_io_state(module)) #include "clinic/_iomodule.c.h" +#undef clinic_state static PyMethodDef module_methods[] = { _IO_OPEN_METHODDEF @@ -644,23 +659,11 @@ static PyTypeObject* static_types[] = { &PyRawIOBase_Type, &PyTextIOBase_Type, - // PyBufferedIOBase_Type(PyIOBase_Type) subclasses - &PyBytesIO_Type, - &PyBufferedReader_Type, - &PyBufferedWriter_Type, - &PyBufferedRWPair_Type, - &PyBufferedRandom_Type, - // PyRawIOBase_Type(PyIOBase_Type) subclasses - &PyFileIO_Type, &_PyBytesIOBuffer_Type, #ifdef MS_WINDOWS &PyWindowsConsoleIO_Type, #endif - - // PyTextIOBase_Type(PyIOBase_Type) subclasses - &PyStringIO_Type, - &PyTextIOWrapper_Type, }; @@ -673,6 +676,17 @@ _PyIO_Fini(void) } } +#define ADD_TYPE(module, type, spec, base) \ +do { \ + type = (PyTypeObject *)PyType_FromModuleAndSpec(module, spec, \ + (PyObject *)base); \ + if (type == NULL) { \ + goto fail; \ + } \ + if (PyModule_AddType(module, type) < 0) { \ + goto fail; \ + } \ +} while (0) PyMODINIT_FUNC PyInit__io(void) @@ -705,17 +719,9 @@ PyInit__io(void) } // Set type base classes - PyFileIO_Type.tp_base = &PyRawIOBase_Type; - PyBytesIO_Type.tp_base = &PyBufferedIOBase_Type; - PyStringIO_Type.tp_base = &PyTextIOBase_Type; #ifdef MS_WINDOWS PyWindowsConsoleIO_Type.tp_base = &PyRawIOBase_Type; #endif - PyBufferedReader_Type.tp_base = &PyBufferedIOBase_Type; - PyBufferedWriter_Type.tp_base = &PyBufferedIOBase_Type; - PyBufferedRWPair_Type.tp_base = &PyBufferedIOBase_Type; - PyBufferedRandom_Type.tp_base = &PyBufferedIOBase_Type; - PyTextIOWrapper_Type.tp_base = &PyTextIOBase_Type; // Add types for (size_t i=0; i < Py_ARRAY_LENGTH(static_types); i++) { @@ -725,6 +731,25 @@ PyInit__io(void) } } + // PyBufferedIOBase_Type(PyIOBase_Type) subclasses + ADD_TYPE(m, state->PyBytesIO_Type, &bytesio_spec, &PyBufferedIOBase_Type); + ADD_TYPE(m, state->PyBufferedWriter_Type, &bufferedwriter_spec, + &PyBufferedIOBase_Type); + ADD_TYPE(m, state->PyBufferedReader_Type, &bufferedreader_spec, + &PyBufferedIOBase_Type); + ADD_TYPE(m, state->PyBufferedRWPair_Type, &bufferedrwpair_spec, + &PyBufferedIOBase_Type); + ADD_TYPE(m, state->PyBufferedRandom_Type, &bufferedrandom_spec, + &PyBufferedIOBase_Type); + + // PyRawIOBase_Type(PyIOBase_Type) subclasses + ADD_TYPE(m, state->PyFileIO_Type, &fileio_spec, &PyRawIOBase_Type); + + // PyTextIOBase_Type(PyIOBase_Type) subclasses + ADD_TYPE(m, state->PyStringIO_Type, &stringio_spec, &PyTextIOBase_Type); + ADD_TYPE(m, state->PyTextIOWrapper_Type, &textiowrapper_spec, + &PyTextIOBase_Type); + state->initialized = 1; return m; diff --git a/Modules/_io/_iomodule.h b/Modules/_io/_iomodule.h index 7617cb8fb70e43..02daef9e85677e 100644 --- a/Modules/_io/_iomodule.h +++ b/Modules/_io/_iomodule.h @@ -4,6 +4,9 @@ #include "exports.h" +#include "pycore_moduleobject.h" // _PyModule_GetState() +#include "structmember.h" + /* ABCs */ extern PyTypeObject PyIOBase_Type; extern PyTypeObject PyRawIOBase_Type; @@ -11,16 +14,18 @@ extern PyTypeObject PyBufferedIOBase_Type; extern PyTypeObject PyTextIOBase_Type; /* Concrete classes */ -extern PyTypeObject PyFileIO_Type; -extern PyTypeObject PyBytesIO_Type; -extern PyTypeObject PyStringIO_Type; -extern PyTypeObject PyBufferedReader_Type; -extern PyTypeObject PyBufferedWriter_Type; -extern PyTypeObject PyBufferedRWPair_Type; -extern PyTypeObject PyBufferedRandom_Type; -extern PyTypeObject PyTextIOWrapper_Type; extern PyTypeObject PyIncrementalNewlineDecoder_Type; +/* Type specs */ +extern PyType_Spec bufferedrandom_spec; +extern PyType_Spec bufferedreader_spec; +extern PyType_Spec bufferedrwpair_spec; +extern PyType_Spec bufferedwriter_spec; +extern PyType_Spec bytesio_spec; +extern PyType_Spec fileio_spec; +extern PyType_Spec stringio_spec; +extern PyType_Spec textiowrapper_spec; + #ifdef MS_WINDOWS extern PyTypeObject PyWindowsConsoleIO_Type; #endif /* MS_WINDOWS */ @@ -140,11 +145,37 @@ typedef struct { PyObject *locale_module; PyObject *unsupported_operation; + + /* Types */ + PyTypeObject *PyBufferedRWPair_Type; + PyTypeObject *PyBufferedRandom_Type; + PyTypeObject *PyBufferedReader_Type; + PyTypeObject *PyBufferedWriter_Type; + PyTypeObject *PyBytesIO_Type; + PyTypeObject *PyFileIO_Type; + PyTypeObject *PyStringIO_Type; + PyTypeObject *PyTextIOWrapper_Type; } _PyIO_State; #define IO_MOD_STATE(mod) ((_PyIO_State *)PyModule_GetState(mod)) #define IO_STATE() _PyIO_get_module_state() +static inline _PyIO_State * +get_io_state(PyObject *module) +{ + void *state = _PyModule_GetState(module); + assert(state != NULL); + return (_PyIO_State *)state; +} + +static inline _PyIO_State * +find_io_state_by_def(PyTypeObject *type) +{ + PyObject *mod = PyType_GetModuleByDef(type, &_PyIO_Module); + assert(mod != NULL); + return get_io_state(mod); +} + extern _PyIO_State *_PyIO_get_module_state(void); #ifdef MS_WINDOWS diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index ba8969f0bcd100..56491f097100c0 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -18,12 +18,12 @@ module _io class _io._BufferedIOBase "PyObject *" "&PyBufferedIOBase_Type" class _io._Buffered "buffered *" "&PyBufferedIOBase_Type" -class _io.BufferedReader "buffered *" "&PyBufferedReader_Type" -class _io.BufferedWriter "buffered *" "&PyBufferedWriter_Type" -class _io.BufferedRWPair "rwpair *" "&PyBufferedRWPair_Type" -class _io.BufferedRandom "buffered *" "&PyBufferedRandom_Type" +class _io.BufferedReader "buffered *" "clinic_state()->PyBufferedReader_Type" +class _io.BufferedWriter "buffered *" "clinic_state()->PyBufferedWriter_Type" +class _io.BufferedRWPair "rwpair *" "clinic_state()->PyBufferedRWPair_Type" +class _io.BufferedRandom "buffered *" "clinic_state()->PyBufferedRandom_Type" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=59460b9c5639984d]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=abd685b9d94b9888]*/ /* * BufferedIOBase class, inherits from IOBase. @@ -366,6 +366,7 @@ _enter_buffered_busy(buffered *self) static void buffered_dealloc(buffered *self) { + PyTypeObject *tp = Py_TYPE(self); self->finalizing = 1; if (_PyIOBase_finalize((PyObject *) self) < 0) return; @@ -383,7 +384,8 @@ buffered_dealloc(buffered *self) self->lock = NULL; } Py_CLEAR(self->dict); - Py_TYPE(self)->tp_free((PyObject *)self); + tp->tp_free((PyObject *)self); + Py_DECREF(tp); } static PyObject * @@ -399,6 +401,7 @@ buffered_sizeof(buffered *self, PyObject *Py_UNUSED(ignored)) static int buffered_traverse(buffered *self, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(self)); Py_VISIT(self->raw); Py_VISIT(self->dict); return 0; @@ -1328,9 +1331,11 @@ buffered_iternext(buffered *self) CHECK_INITIALIZED(self); + _PyIO_State *state = find_io_state_by_def(Py_TYPE(self)); tp = Py_TYPE(self); - if (tp == &PyBufferedReader_Type || - tp == &PyBufferedRandom_Type) { + if (Py_IS_TYPE(tp, state->PyBufferedReader_Type) || + Py_IS_TYPE(tp, state->PyBufferedRandom_Type)) + { /* Skip method call overhead for speed */ line = _buffered_readline(self, -1); } @@ -1428,8 +1433,11 @@ _io_BufferedReader___init___impl(buffered *self, PyObject *raw, return -1; _bufferedreader_reset_buf(self); - self->fast_closed_checks = (Py_IS_TYPE(self, &PyBufferedReader_Type) && - Py_IS_TYPE(raw, &PyFileIO_Type)); + _PyIO_State *state = find_io_state_by_def(Py_TYPE(self)); + self->fast_closed_checks = ( + Py_IS_TYPE(self, state->PyBufferedReader_Type) && + Py_IS_TYPE(raw, state->PyFileIO_Type) + ); self->ok = 1; return 0; @@ -1783,8 +1791,11 @@ _io_BufferedWriter___init___impl(buffered *self, PyObject *raw, _bufferedwriter_reset_buf(self); self->pos = 0; - self->fast_closed_checks = (Py_IS_TYPE(self, &PyBufferedWriter_Type) && - Py_IS_TYPE(raw, &PyFileIO_Type)); + _PyIO_State *state = find_io_state_by_def(Py_TYPE(self)); + self->fast_closed_checks = ( + Py_IS_TYPE(self, state->PyBufferedWriter_Type) && + Py_IS_TYPE(raw, state->PyFileIO_Type) + ); self->ok = 1; return 0; @@ -2090,13 +2101,16 @@ _io_BufferedRWPair___init___impl(rwpair *self, PyObject *reader, if (_PyIOBase_check_writable(writer, Py_True) == NULL) return -1; + _PyIO_State *state = find_io_state_by_def(Py_TYPE(self)); self->reader = (buffered *) PyObject_CallFunction( - (PyObject *) &PyBufferedReader_Type, "On", reader, buffer_size); + (PyObject *)state->PyBufferedReader_Type, + "On", reader, buffer_size); if (self->reader == NULL) return -1; self->writer = (buffered *) PyObject_CallFunction( - (PyObject *) &PyBufferedWriter_Type, "On", writer, buffer_size); + (PyObject *)state->PyBufferedWriter_Type, + "On", writer, buffer_size); if (self->writer == NULL) { Py_CLEAR(self->reader); return -1; @@ -2108,6 +2122,7 @@ _io_BufferedRWPair___init___impl(rwpair *self, PyObject *reader, static int bufferedrwpair_traverse(rwpair *self, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(self)); Py_VISIT(self->dict); return 0; } @@ -2124,13 +2139,15 @@ bufferedrwpair_clear(rwpair *self) static void bufferedrwpair_dealloc(rwpair *self) { + PyTypeObject *tp = Py_TYPE(self); _PyObject_GC_UNTRACK(self); if (self->weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *)self); Py_CLEAR(self->reader); Py_CLEAR(self->writer); Py_CLEAR(self->dict); - Py_TYPE(self)->tp_free((PyObject *) self); + tp->tp_free((PyObject *) self); + Py_DECREF(tp); } static PyObject * @@ -2295,14 +2312,17 @@ _io_BufferedRandom___init___impl(buffered *self, PyObject *raw, _bufferedwriter_reset_buf(self); self->pos = 0; - self->fast_closed_checks = (Py_IS_TYPE(self, &PyBufferedRandom_Type) && - Py_IS_TYPE(raw, &PyFileIO_Type)); + _PyIO_State *state = find_io_state_by_def(Py_TYPE(self)); + self->fast_closed_checks = (Py_IS_TYPE(self, state->PyBufferedRandom_Type) && + Py_IS_TYPE(raw, state->PyFileIO_Type)); self->ok = 1; return 0; } +#define clinic_state() (find_io_state_by_def(Py_TYPE(self))) #include "clinic/bufferedio.c.h" +#undef clinic_state static PyMethodDef bufferediobase_methods[] = { @@ -2394,6 +2414,8 @@ static PyMethodDef bufferedreader_methods[] = { static PyMemberDef bufferedreader_members[] = { {"raw", T_OBJECT, offsetof(buffered, raw), READONLY}, {"_finalizing", T_BOOL, offsetof(buffered, finalizing), 0}, + {"__weaklistoffset__", T_PYSSIZET, offsetof(buffered, weakreflist), READONLY}, + {"__dictoffset__", T_PYSSIZET, offsetof(buffered, dict), READONLY}, {NULL} }; @@ -2405,58 +2427,27 @@ static PyGetSetDef bufferedreader_getset[] = { }; -PyTypeObject PyBufferedReader_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_io.BufferedReader", /*tp_name*/ - sizeof(buffered), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)buffered_dealloc, /*tp_dealloc*/ - 0, /*tp_vectorcall_offset*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_as_async*/ - (reprfunc)buffered_repr, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE - | Py_TPFLAGS_HAVE_GC, /*tp_flags*/ - _io_BufferedReader___init____doc__, /* tp_doc */ - (traverseproc)buffered_traverse, /* tp_traverse */ - (inquiry)buffered_clear, /* tp_clear */ - 0, /* tp_richcompare */ - offsetof(buffered, weakreflist), /*tp_weaklistoffset*/ - 0, /* tp_iter */ - (iternextfunc)buffered_iternext, /* tp_iternext */ - bufferedreader_methods, /* tp_methods */ - bufferedreader_members, /* tp_members */ - bufferedreader_getset, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - offsetof(buffered, dict), /* tp_dictoffset */ - _io_BufferedReader___init__, /* tp_init */ - 0, /* tp_alloc */ - PyType_GenericNew, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ - 0, /* tp_finalize */ +static PyType_Slot bufferedreader_slots[] = { + {Py_tp_dealloc, buffered_dealloc}, + {Py_tp_repr, buffered_repr}, + {Py_tp_doc, (void *)_io_BufferedReader___init____doc__}, + {Py_tp_traverse, buffered_traverse}, + {Py_tp_clear, buffered_clear}, + {Py_tp_iternext, buffered_iternext}, + {Py_tp_methods, bufferedreader_methods}, + {Py_tp_members, bufferedreader_members}, + {Py_tp_getset, bufferedreader_getset}, + {Py_tp_init, _io_BufferedReader___init__}, + {0, NULL}, }; +PyType_Spec bufferedreader_spec = { + .name = "_io.BufferedReader", + .basicsize = sizeof(buffered), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = bufferedreader_slots, +}; static PyMethodDef bufferedwriter_methods[] = { /* BufferedIOMixin methods */ @@ -2480,6 +2471,8 @@ static PyMethodDef bufferedwriter_methods[] = { static PyMemberDef bufferedwriter_members[] = { {"raw", T_OBJECT, offsetof(buffered, raw), READONLY}, {"_finalizing", T_BOOL, offsetof(buffered, finalizing), 0}, + {"__weaklistoffset__", T_PYSSIZET, offsetof(buffered, weakreflist), READONLY}, + {"__dictoffset__", T_PYSSIZET, offsetof(buffered, dict), READONLY}, {NULL} }; @@ -2491,58 +2484,26 @@ static PyGetSetDef bufferedwriter_getset[] = { }; -PyTypeObject PyBufferedWriter_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_io.BufferedWriter", /*tp_name*/ - sizeof(buffered), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)buffered_dealloc, /*tp_dealloc*/ - 0, /*tp_vectorcall_offset*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_as_async*/ - (reprfunc)buffered_repr, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE - | Py_TPFLAGS_HAVE_GC, /*tp_flags*/ - _io_BufferedWriter___init____doc__, /* tp_doc */ - (traverseproc)buffered_traverse, /* tp_traverse */ - (inquiry)buffered_clear, /* tp_clear */ - 0, /* tp_richcompare */ - offsetof(buffered, weakreflist), /*tp_weaklistoffset*/ - 0, /* tp_iter */ - 0, /* tp_iternext */ - bufferedwriter_methods, /* tp_methods */ - bufferedwriter_members, /* tp_members */ - bufferedwriter_getset, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - offsetof(buffered, dict), /* tp_dictoffset */ - _io_BufferedWriter___init__, /* tp_init */ - 0, /* tp_alloc */ - PyType_GenericNew, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ - 0, /* tp_finalize */ +static PyType_Slot bufferedwriter_slots[] = { + {Py_tp_dealloc, buffered_dealloc}, + {Py_tp_repr, buffered_repr}, + {Py_tp_doc, (void *)_io_BufferedWriter___init____doc__}, + {Py_tp_traverse, buffered_traverse}, + {Py_tp_clear, buffered_clear}, + {Py_tp_methods, bufferedwriter_methods}, + {Py_tp_members, bufferedwriter_members}, + {Py_tp_getset, bufferedwriter_getset}, + {Py_tp_init, _io_BufferedWriter___init__}, + {0, NULL}, }; +PyType_Spec bufferedwriter_spec = { + .name = "_io.BufferedWriter", + .basicsize = sizeof(buffered), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = bufferedwriter_slots, +}; static PyMethodDef bufferedrwpair_methods[] = { {"read", (PyCFunction)bufferedrwpair_read, METH_VARARGS}, @@ -2563,61 +2524,35 @@ static PyMethodDef bufferedrwpair_methods[] = { {NULL, NULL} }; +static PyMemberDef bufferedrwpair_members[] = { + {"__weaklistoffset__", T_PYSSIZET, offsetof(rwpair, weakreflist), READONLY}, + {"__dictoffset__", T_PYSSIZET, offsetof(rwpair, dict), READONLY}, + {NULL} +}; + static PyGetSetDef bufferedrwpair_getset[] = { {"closed", (getter)bufferedrwpair_closed_get, NULL, NULL}, {NULL} }; -PyTypeObject PyBufferedRWPair_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_io.BufferedRWPair", /*tp_name*/ - sizeof(rwpair), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)bufferedrwpair_dealloc, /*tp_dealloc*/ - 0, /*tp_vectorcall_offset*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_as_async*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE - | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - _io_BufferedRWPair___init____doc__, /* tp_doc */ - (traverseproc)bufferedrwpair_traverse, /* tp_traverse */ - (inquiry)bufferedrwpair_clear, /* tp_clear */ - 0, /* tp_richcompare */ - offsetof(rwpair, weakreflist), /*tp_weaklistoffset*/ - 0, /* tp_iter */ - 0, /* tp_iternext */ - bufferedrwpair_methods, /* tp_methods */ - 0, /* tp_members */ - bufferedrwpair_getset, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - offsetof(rwpair, dict), /* tp_dictoffset */ - _io_BufferedRWPair___init__, /* tp_init */ - 0, /* tp_alloc */ - PyType_GenericNew, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ - 0, /* tp_finalize */ +static PyType_Slot bufferedrwpair_slots[] = { + {Py_tp_dealloc, bufferedrwpair_dealloc}, + {Py_tp_doc, (void *)_io_BufferedRWPair___init____doc__}, + {Py_tp_traverse, bufferedrwpair_traverse}, + {Py_tp_clear, bufferedrwpair_clear}, + {Py_tp_methods, bufferedrwpair_methods}, + {Py_tp_members, bufferedrwpair_members}, + {Py_tp_getset, bufferedrwpair_getset}, + {Py_tp_init, _io_BufferedRWPair___init__}, + {0, NULL}, +}; + +PyType_Spec bufferedrwpair_spec = { + .name = "_io.BufferedRWPair", + .basicsize = sizeof(rwpair), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = bufferedrwpair_slots, }; @@ -2651,6 +2586,8 @@ static PyMethodDef bufferedrandom_methods[] = { static PyMemberDef bufferedrandom_members[] = { {"raw", T_OBJECT, offsetof(buffered, raw), READONLY}, {"_finalizing", T_BOOL, offsetof(buffered, finalizing), 0}, + {"__weaklistoffset__", T_PYSSIZET, offsetof(buffered, weakreflist), READONLY}, + {"__dictoffset__", T_PYSSIZET, offsetof(buffered, dict), READONLY}, {NULL} }; @@ -2662,54 +2599,24 @@ static PyGetSetDef bufferedrandom_getset[] = { }; -PyTypeObject PyBufferedRandom_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_io.BufferedRandom", /*tp_name*/ - sizeof(buffered), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)buffered_dealloc, /*tp_dealloc*/ - 0, /*tp_vectorcall_offset*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_as_async*/ - (reprfunc)buffered_repr, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE - | Py_TPFLAGS_HAVE_GC, /*tp_flags*/ - _io_BufferedRandom___init____doc__, /* tp_doc */ - (traverseproc)buffered_traverse, /* tp_traverse */ - (inquiry)buffered_clear, /* tp_clear */ - 0, /* tp_richcompare */ - offsetof(buffered, weakreflist), /*tp_weaklistoffset*/ - 0, /* tp_iter */ - (iternextfunc)buffered_iternext, /* tp_iternext */ - bufferedrandom_methods, /* tp_methods */ - bufferedrandom_members, /* tp_members */ - bufferedrandom_getset, /* tp_getset */ - 0, /* tp_base */ - 0, /*tp_dict*/ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - offsetof(buffered, dict), /*tp_dictoffset*/ - _io_BufferedRandom___init__, /* tp_init */ - 0, /* tp_alloc */ - PyType_GenericNew, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ - 0, /* tp_finalize */ +static PyType_Slot bufferedrandom_slots[] = { + {Py_tp_dealloc, buffered_dealloc}, + {Py_tp_repr, buffered_repr}, + {Py_tp_doc, (void *)_io_BufferedRandom___init____doc__}, + {Py_tp_traverse, buffered_traverse}, + {Py_tp_clear, buffered_clear}, + {Py_tp_iternext, buffered_iternext}, + {Py_tp_methods, bufferedrandom_methods}, + {Py_tp_members, bufferedrandom_members}, + {Py_tp_getset, bufferedrandom_getset}, + {Py_tp_init, _io_BufferedRandom___init__}, + {0, NULL}, +}; + +PyType_Spec bufferedrandom_spec = { + .name = "_io.BufferedRandom", + .basicsize = sizeof(buffered), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = bufferedrandom_slots, }; diff --git a/Modules/_io/bytesio.c b/Modules/_io/bytesio.c index 6698c60355fcc5..7e9d28b3b9655c 100644 --- a/Modules/_io/bytesio.c +++ b/Modules/_io/bytesio.c @@ -5,9 +5,9 @@ /*[clinic input] module _io -class _io.BytesIO "bytesio *" "&PyBytesIO_Type" +class _io.BytesIO "bytesio *" "clinic_state()->PyBytesIO_Type" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=7f50ec034f5c0b26]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=48ede2f330f847c3]*/ typedef struct { PyObject_HEAD @@ -881,6 +881,7 @@ bytesio_setstate(bytesio *self, PyObject *state) static void bytesio_dealloc(bytesio *self) { + PyTypeObject *tp = Py_TYPE(self); _PyObject_GC_UNTRACK(self); if (self->exports > 0) { PyErr_SetString(PyExc_SystemError, @@ -891,7 +892,8 @@ bytesio_dealloc(bytesio *self) Py_CLEAR(self->dict); if (self->weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *) self); - Py_TYPE(self)->tp_free(self); + tp->tp_free(self); + Py_DECREF(tp); } static PyObject * @@ -971,6 +973,7 @@ bytesio_sizeof(bytesio *self, void *unused) static int bytesio_traverse(bytesio *self, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(self)); Py_VISIT(self->dict); return 0; } @@ -983,7 +986,9 @@ bytesio_clear(bytesio *self) } +#define clinic_state() (find_io_state_by_def(Py_TYPE(self))) #include "clinic/bytesio.c.h" +#undef clinic_state static PyGetSetDef bytesio_getsetlist[] = { {"closed", (getter)bytesio_get_closed, NULL, @@ -1016,48 +1021,34 @@ static struct PyMethodDef bytesio_methods[] = { {NULL, NULL} /* sentinel */ }; -PyTypeObject PyBytesIO_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_io.BytesIO", /*tp_name*/ - sizeof(bytesio), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)bytesio_dealloc, /*tp_dealloc*/ - 0, /*tp_vectorcall_offset*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_as_async*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | - Py_TPFLAGS_HAVE_GC, /*tp_flags*/ - _io_BytesIO___init____doc__, /*tp_doc*/ - (traverseproc)bytesio_traverse, /*tp_traverse*/ - (inquiry)bytesio_clear, /*tp_clear*/ - 0, /*tp_richcompare*/ - offsetof(bytesio, weakreflist), /*tp_weaklistoffset*/ - PyObject_SelfIter, /*tp_iter*/ - (iternextfunc)bytesio_iternext, /*tp_iternext*/ - bytesio_methods, /*tp_methods*/ - 0, /*tp_members*/ - bytesio_getsetlist, /*tp_getset*/ - 0, /*tp_base*/ - 0, /*tp_dict*/ - 0, /*tp_descr_get*/ - 0, /*tp_descr_set*/ - offsetof(bytesio, dict), /*tp_dictoffset*/ - _io_BytesIO___init__, /*tp_init*/ - 0, /*tp_alloc*/ - bytesio_new, /*tp_new*/ +static PyMemberDef bytesio_members[] = { + {"__weaklistoffset__", T_PYSSIZET, offsetof(bytesio, weakreflist), READONLY}, + {"__dictoffset__", T_PYSSIZET, offsetof(bytesio, dict), READONLY}, + {NULL} }; +static PyType_Slot bytesio_slots[] = { + {Py_tp_dealloc, bytesio_dealloc}, + {Py_tp_doc, (void *)_io_BytesIO___init____doc__}, + {Py_tp_traverse, bytesio_traverse}, + {Py_tp_clear, bytesio_clear}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, bytesio_iternext}, + {Py_tp_methods, bytesio_methods}, + {Py_tp_members, bytesio_members}, + {Py_tp_getset, bytesio_getsetlist}, + {Py_tp_init, _io_BytesIO___init__}, + {Py_tp_new, bytesio_new}, + {0, NULL}, +}; + +PyType_Spec bytesio_spec = { + .name = "_io.BytesIO", + .basicsize = sizeof(bytesio), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = bytesio_slots, +}; /* * Implementation of the small intermediate object used by getbuffer(). diff --git a/Modules/_io/clinic/bufferedio.c.h b/Modules/_io/clinic/bufferedio.c.h index 38ea756879c122..d44321bb8b960e 100644 --- a/Modules/_io/clinic/bufferedio.c.h +++ b/Modules/_io/clinic/bufferedio.c.h @@ -601,7 +601,7 @@ static int _io_BufferedRWPair___init__(PyObject *self, PyObject *args, PyObject *kwargs) { int return_value = -1; - PyTypeObject *base_tp = &PyBufferedRWPair_Type; + PyTypeObject *base_tp = clinic_state()->PyBufferedRWPair_Type; PyObject *reader; PyObject *writer; Py_ssize_t buffer_size = DEFAULT_BUFFER_SIZE; @@ -714,4 +714,4 @@ _io_BufferedRandom___init__(PyObject *self, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=953f1577e96e8d86 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=8412b10c04259bb8 input=a9049054013a1b77]*/ diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c index d1a183cedac53a..f424fb8439d7a8 100644 --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -51,9 +51,9 @@ /*[clinic input] module _io -class _io.FileIO "fileio *" "&PyFileIO_Type" +class _io.FileIO "fileio *" "clinic_state()->PyFileIO_Type" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=1c77708b41fda70c]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=ac25ec278f4d6703]*/ typedef struct { PyObject_HEAD @@ -70,9 +70,7 @@ typedef struct { PyObject *dict; } fileio; -PyTypeObject PyFileIO_Type; - -#define PyFileIO_Check(op) (PyObject_TypeCheck((op), &PyFileIO_Type)) +#define PyFileIO_Check(state, op) (PyObject_TypeCheck((op), state->PyFileIO_Type)) /* Forward declarations */ static PyObject* portable_lseek(fileio *self, PyObject *posobj, int whence, bool suppress_pipe_error); @@ -242,7 +240,10 @@ _io_FileIO___init___impl(fileio *self, PyObject *nameobj, const char *mode, int fstat_result; int async_err = 0; - assert(PyFileIO_Check(self)); +#ifdef Py_DEBUG + _PyIO_State *state = find_io_state_by_def(Py_TYPE(self)); + assert(PyFileIO_Check(state, self)); +#endif if (self->fd >= 0) { if (self->closefd) { /* Have to close the existing file first. */ @@ -503,6 +504,7 @@ _io_FileIO___init___impl(fileio *self, PyObject *nameobj, const char *mode, static int fileio_traverse(fileio *self, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(self)); Py_VISIT(self->dict); return 0; } @@ -517,6 +519,7 @@ fileio_clear(fileio *self) static void fileio_dealloc(fileio *self) { + PyTypeObject *tp = Py_TYPE(self); self->finalizing = 1; if (_PyIOBase_finalize((PyObject *) self) < 0) return; @@ -524,7 +527,8 @@ fileio_dealloc(fileio *self) if (self->weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *) self); Py_CLEAR(self->dict); - Py_TYPE(self)->tp_free((PyObject *)self); + tp->tp_free((PyObject *)self); + Py_DECREF(tp); } static PyObject * @@ -1177,57 +1181,29 @@ static PyGetSetDef fileio_getsetlist[] = { static PyMemberDef fileio_members[] = { {"_blksize", T_UINT, offsetof(fileio, blksize), 0}, {"_finalizing", T_BOOL, offsetof(fileio, finalizing), 0}, + {"__weaklistoffset__", T_PYSSIZET, offsetof(fileio, weakreflist), READONLY}, + {"__dictoffset__", T_PYSSIZET, offsetof(fileio, dict), READONLY}, {NULL} }; -PyTypeObject PyFileIO_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_io.FileIO", - sizeof(fileio), - 0, - (destructor)fileio_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - (reprfunc)fileio_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE - | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - _io_FileIO___init____doc__, /* tp_doc */ - (traverseproc)fileio_traverse, /* tp_traverse */ - (inquiry)fileio_clear, /* tp_clear */ - 0, /* tp_richcompare */ - offsetof(fileio, weakreflist), /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - fileio_methods, /* tp_methods */ - fileio_members, /* tp_members */ - fileio_getsetlist, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - offsetof(fileio, dict), /* tp_dictoffset */ - _io_FileIO___init__, /* tp_init */ - PyType_GenericAlloc, /* tp_alloc */ - fileio_new, /* tp_new */ - PyObject_GC_Del, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ - 0, /* tp_finalize */ +static PyType_Slot fileio_slots[] = { + {Py_tp_dealloc, fileio_dealloc}, + {Py_tp_repr, fileio_repr}, + {Py_tp_doc, (void *)_io_FileIO___init____doc__}, + {Py_tp_traverse, fileio_traverse}, + {Py_tp_clear, fileio_clear}, + {Py_tp_methods, fileio_methods}, + {Py_tp_members, fileio_members}, + {Py_tp_getset, fileio_getsetlist}, + {Py_tp_init, _io_FileIO___init__}, + {Py_tp_new, fileio_new}, + {0, NULL}, +}; + +PyType_Spec fileio_spec = { + .name = "_io.FileIO", + .basicsize = sizeof(fileio), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = fileio_slots, }; diff --git a/Modules/_io/stringio.c b/Modules/_io/stringio.c index ae6c3125a2d9da..54c050f0be4688 100644 --- a/Modules/_io/stringio.c +++ b/Modules/_io/stringio.c @@ -13,9 +13,9 @@ /*[clinic input] module _io -class _io.StringIO "stringio *" "&PyStringIO_Type" +class _io.StringIO "stringio *" "clinic_state()->PyStringIO_Type" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=c17bc0f42165cd7d]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=2693eada0658d470]*/ typedef struct { PyObject_HEAD @@ -43,6 +43,7 @@ typedef struct { PyObject *dict; PyObject *weakreflist; + _PyIO_State *module_state; } stringio; static int _io_StringIO___init__(PyObject *self, PyObject *args, PyObject *kwargs); @@ -401,7 +402,7 @@ stringio_iternext(stringio *self) CHECK_CLOSED(self); ENSURE_REALIZED(self); - if (Py_IS_TYPE(self, &PyStringIO_Type)) { + if (Py_IS_TYPE(self, self->module_state->PyStringIO_Type)) { /* Skip method call overhead for speed */ line = _stringio_readline(self, -1); } @@ -581,6 +582,7 @@ _io_StringIO_close_impl(stringio *self) static int stringio_traverse(stringio *self, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(self)); Py_VISIT(self->dict); return 0; } @@ -595,6 +597,7 @@ stringio_clear(stringio *self) static void stringio_dealloc(stringio *self) { + PyTypeObject *tp = Py_TYPE(self); _PyObject_GC_UNTRACK(self); self->ok = 0; if (self->buf) { @@ -606,9 +609,11 @@ stringio_dealloc(stringio *self) Py_CLEAR(self->writenl); Py_CLEAR(self->decoder); Py_CLEAR(self->dict); - if (self->weakreflist != NULL) + if (self->weakreflist != NULL) { PyObject_ClearWeakRefs((PyObject *) self); - Py_TYPE(self)->tp_free(self); + } + tp->tp_free(self); + Py_DECREF(tp); } static PyObject * @@ -745,7 +750,7 @@ _io_StringIO___init___impl(stringio *self, PyObject *value, self->state = STATE_ACCUMULATING; } self->pos = 0; - + self->module_state = find_io_state_by_def(Py_TYPE(self)); self->closed = 0; self->ok = 1; return 0; @@ -963,7 +968,9 @@ stringio_newlines(stringio *self, void *context) return PyObject_GetAttr(self->decoder, &_Py_ID(newlines)); } +#define clinic_state() (find_io_state_by_def(Py_TYPE(self))) #include "clinic/stringio.c.h" +#undef clinic_state static struct PyMethodDef stringio_methods[] = { _IO_STRINGIO_CLOSE_METHODDEF @@ -997,44 +1004,30 @@ static PyGetSetDef stringio_getset[] = { {NULL} }; -PyTypeObject PyStringIO_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_io.StringIO", /*tp_name*/ - sizeof(stringio), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)stringio_dealloc, /*tp_dealloc*/ - 0, /*tp_vectorcall_offset*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_as_async*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE - | Py_TPFLAGS_HAVE_GC, /*tp_flags*/ - _io_StringIO___init____doc__, /*tp_doc*/ - (traverseproc)stringio_traverse, /*tp_traverse*/ - (inquiry)stringio_clear, /*tp_clear*/ - 0, /*tp_richcompare*/ - offsetof(stringio, weakreflist), /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - (iternextfunc)stringio_iternext, /*tp_iternext*/ - stringio_methods, /*tp_methods*/ - 0, /*tp_members*/ - stringio_getset, /*tp_getset*/ - 0, /*tp_base*/ - 0, /*tp_dict*/ - 0, /*tp_descr_get*/ - 0, /*tp_descr_set*/ - offsetof(stringio, dict), /*tp_dictoffset*/ - _io_StringIO___init__, /*tp_init*/ - 0, /*tp_alloc*/ - stringio_new, /*tp_new*/ +static struct PyMemberDef stringio_members[] = { + {"__weaklistoffset__", T_PYSSIZET, offsetof(stringio, weakreflist), READONLY}, + {"__dictoffset__", T_PYSSIZET, offsetof(stringio, dict), READONLY}, + {NULL}, +}; + +static PyType_Slot stringio_slots[] = { + {Py_tp_dealloc, stringio_dealloc}, + {Py_tp_doc, (void *)_io_StringIO___init____doc__}, + {Py_tp_traverse, stringio_traverse}, + {Py_tp_clear, stringio_clear}, + {Py_tp_iternext, stringio_iternext}, + {Py_tp_methods, stringio_methods}, + {Py_tp_members, stringio_members}, + {Py_tp_getset, stringio_getset}, + {Py_tp_init, _io_StringIO___init__}, + {Py_tp_new, stringio_new}, + {0, NULL}, +}; + +PyType_Spec stringio_spec = { + .name = "_io.StringIO", + .basicsize = sizeof(stringio), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = stringio_slots, }; diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index ea2ea32c336954..fbf0bf46840374 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -19,9 +19,9 @@ /*[clinic input] module _io class _io.IncrementalNewlineDecoder "nldecoder_object *" "&PyIncrementalNewlineDecoder_Type" -class _io.TextIOWrapper "textio *" "&TextIOWrapper_Type" +class _io.TextIOWrapper "textio *" "clinic_state()->TextIOWrapper_Type" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=ed072384f8aada2c]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=d3f032e90f74c8f2]*/ /* TextIOBase */ @@ -682,6 +682,8 @@ typedef struct PyObject *weakreflist; PyObject *dict; + + _PyIO_State *state; } textio; static void @@ -1175,15 +1177,16 @@ _io_TextIOWrapper___init___impl(textio *self, PyObject *buffer, /* Finished sorting out the codec details */ Py_CLEAR(codec_info); - if (Py_IS_TYPE(buffer, &PyBufferedReader_Type) || - Py_IS_TYPE(buffer, &PyBufferedWriter_Type) || - Py_IS_TYPE(buffer, &PyBufferedRandom_Type)) + _PyIO_State *state = find_io_state_by_def(Py_TYPE(self)); + if (Py_IS_TYPE(buffer, state->PyBufferedReader_Type) || + Py_IS_TYPE(buffer, state->PyBufferedWriter_Type) || + Py_IS_TYPE(buffer, state->PyBufferedRandom_Type)) { if (_PyObject_LookupAttr(buffer, &_Py_ID(raw), &raw) < 0) goto error; /* Cache the raw FileIO object to speed up 'closed' checks */ if (raw != NULL) { - if (Py_IS_TYPE(raw, &PyFileIO_Type)) + if (Py_IS_TYPE(raw, state->PyFileIO_Type)) self->raw = raw; else Py_DECREF(raw); @@ -1211,6 +1214,7 @@ _io_TextIOWrapper___init___impl(textio *self, PyObject *buffer, goto error; } + self->state = state; self->ok = 1; return 0; @@ -1387,6 +1391,7 @@ textiowrapper_clear(textio *self) static void textiowrapper_dealloc(textio *self) { + PyTypeObject *tp = Py_TYPE(self); self->finalizing = 1; if (_PyIOBase_finalize((PyObject *) self) < 0) return; @@ -1395,12 +1400,14 @@ textiowrapper_dealloc(textio *self) if (self->weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *)self); textiowrapper_clear(self); - Py_TYPE(self)->tp_free((PyObject *)self); + tp->tp_free((PyObject *)self); + Py_DECREF(tp); } static int textiowrapper_traverse(textio *self, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(self)); Py_VISIT(self->buffer); Py_VISIT(self->encoding); Py_VISIT(self->encoder); @@ -1424,7 +1431,7 @@ textiowrapper_closed_get(textio *self, void *context); do { \ int r; \ PyObject *_res; \ - if (Py_IS_TYPE(self, &PyTextIOWrapper_Type)) { \ + if (Py_IS_TYPE(self, self->state->PyTextIOWrapper_Type)) { \ if (self->raw != NULL) \ r = _PyFileIO_closed(self->raw); \ else { \ @@ -3053,7 +3060,7 @@ textiowrapper_iternext(textio *self) CHECK_ATTACHED(self); self->telling = 0; - if (Py_IS_TYPE(self, &PyTextIOWrapper_Type)) { + if (Py_IS_TYPE(self, self->state->PyTextIOWrapper_Type)) { /* Skip method call overhead for speed */ line = _textiowrapper_readline(self, -1); } @@ -3145,7 +3152,9 @@ textiowrapper_chunk_size_set(textio *self, PyObject *arg, void *context) return 0; } +#define clinic_state() (find_io_state_by_def(Py_TYPE(self))) #include "clinic/textio.c.h" +#undef clinic_state static PyMethodDef incrementalnewlinedecoder_methods[] = { _IO_INCREMENTALNEWLINEDECODER_DECODE_METHODDEF @@ -3229,6 +3238,8 @@ static PyMemberDef textiowrapper_members[] = { {"line_buffering", T_BOOL, offsetof(textio, line_buffering), READONLY}, {"write_through", T_BOOL, offsetof(textio, write_through), READONLY}, {"_finalizing", T_BOOL, offsetof(textio, finalizing), 0}, + {"__weaklistoffset__", T_PYSSIZET, offsetof(textio, weakreflist), READONLY}, + {"__dictoffset__", T_PYSSIZET, offsetof(textio, dict), READONLY}, {NULL} }; @@ -3244,54 +3255,24 @@ static PyGetSetDef textiowrapper_getset[] = { {NULL} }; -PyTypeObject PyTextIOWrapper_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_io.TextIOWrapper", /*tp_name*/ - sizeof(textio), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)textiowrapper_dealloc, /*tp_dealloc*/ - 0, /*tp_vectorcall_offset*/ - 0, /*tp_getattr*/ - 0, /*tps_etattr*/ - 0, /*tp_as_async*/ - (reprfunc)textiowrapper_repr,/*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE - | Py_TPFLAGS_HAVE_GC, /*tp_flags*/ - _io_TextIOWrapper___init____doc__, /* tp_doc */ - (traverseproc)textiowrapper_traverse, /* tp_traverse */ - (inquiry)textiowrapper_clear, /* tp_clear */ - 0, /* tp_richcompare */ - offsetof(textio, weakreflist), /*tp_weaklistoffset*/ - 0, /* tp_iter */ - (iternextfunc)textiowrapper_iternext, /* tp_iternext */ - textiowrapper_methods, /* tp_methods */ - textiowrapper_members, /* tp_members */ - textiowrapper_getset, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - offsetof(textio, dict), /*tp_dictoffset*/ - _io_TextIOWrapper___init__, /* tp_init */ - 0, /* tp_alloc */ - PyType_GenericNew, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ - 0, /* tp_finalize */ +PyType_Slot textiowrapper_slots[] = { + {Py_tp_dealloc, textiowrapper_dealloc}, + {Py_tp_repr, textiowrapper_repr}, + {Py_tp_doc, (void *)_io_TextIOWrapper___init____doc__}, + {Py_tp_traverse, textiowrapper_traverse}, + {Py_tp_clear, textiowrapper_clear}, + {Py_tp_iternext, textiowrapper_iternext}, + {Py_tp_methods, textiowrapper_methods}, + {Py_tp_members, textiowrapper_members}, + {Py_tp_getset, textiowrapper_getset}, + {Py_tp_init, _io_TextIOWrapper___init__}, + {0, NULL}, +}; + +PyType_Spec textiowrapper_spec = { + .name = "_io.TextIOWrapper", + .basicsize = sizeof(textio), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = textiowrapper_slots, }; diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index 6011b1604508af..2e28c50c6ff69a 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -317,19 +317,11 @@ Modules/_collectionsmodule.c - dequeiter_type - Modules/_collectionsmodule.c - dequereviter_type - Modules/_collectionsmodule.c - tuplegetter_type - Modules/_io/bufferedio.c - PyBufferedIOBase_Type - -Modules/_io/bufferedio.c - PyBufferedRWPair_Type - -Modules/_io/bufferedio.c - PyBufferedRandom_Type - -Modules/_io/bufferedio.c - PyBufferedReader_Type - -Modules/_io/bufferedio.c - PyBufferedWriter_Type - -Modules/_io/bytesio.c - PyBytesIO_Type - Modules/_io/bytesio.c - _PyBytesIOBuffer_Type - -Modules/_io/fileio.c - PyFileIO_Type - Modules/_io/iobase.c - PyIOBase_Type - Modules/_io/iobase.c - PyRawIOBase_Type - -Modules/_io/stringio.c - PyStringIO_Type - Modules/_io/textio.c - PyIncrementalNewlineDecoder_Type - Modules/_io/textio.c - PyTextIOBase_Type - -Modules/_io/textio.c - PyTextIOWrapper_Type - Modules/_io/winconsoleio.c - PyWindowsConsoleIO_Type - Modules/_testcapi/vectorcall.c - MethodDescriptorBase_Type - Modules/_testcapi/vectorcall.c - MethodDescriptorDerived_Type - From a99eb5cd9947629a6745a4ad99cb07af1c287b5d Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Mon, 20 Feb 2023 14:56:48 +0000 Subject: [PATCH 142/247] gh-101907: Stop using `_Py_OPCODE` and `_Py_OPARG` macros (GH-101912) * gh-101907: Removes use of non-standard C++ extension from Include/cpython/code.h * Make cases_generator correct on Windows --- Include/cpython/code.h | 28 ++- ...-02-14-15-53-01.gh-issue-101907.HgF1N2.rst | 1 + Objects/codeobject.c | 20 +- Objects/frameobject.c | 24 +-- Objects/genobject.c | 10 +- Objects/typeobject.c | 4 +- Python/bytecodes.c | 34 ++-- Python/ceval.c | 12 +- Python/ceval_macros.h | 18 +- Python/compile.c | 20 +- Python/generated_cases.c.h | 34 ++-- Python/specialize.c | 172 +++++++++--------- Tools/cases_generator/generate_cases.py | 7 +- 13 files changed, 200 insertions(+), 184 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2023-02-14-15-53-01.gh-issue-101907.HgF1N2.rst diff --git a/Include/cpython/code.h b/Include/cpython/code.h index 0cf49f06c87732..fba9296aedc99d 100644 --- a/Include/cpython/code.h +++ b/Include/cpython/code.h @@ -19,21 +19,35 @@ extern "C" { typedef union { uint16_t cache; struct { - uint8_t opcode; - uint8_t oparg; - }; + uint8_t code; + uint8_t arg; + } op; } _Py_CODEUNIT; -#define _Py_OPCODE(word) ((word).opcode) -#define _Py_OPARG(word) ((word).oparg) + +/* These macros only remain defined for compatibility. */ +#define _Py_OPCODE(word) ((word).op.code) +#define _Py_OPARG(word) ((word).op.arg) + +static inline _Py_CODEUNIT +_py_make_codeunit(uint8_t opcode, uint8_t oparg) +{ + // No designated initialisers because of C++ compat + _Py_CODEUNIT word; + word.op.code = opcode; + word.op.arg = oparg; + return word; +} static inline void _py_set_opcode(_Py_CODEUNIT *word, uint8_t opcode) { - word->opcode = opcode; + word->op.code = opcode; } -#define _Py_SET_OPCODE(word, opcode) _py_set_opocde(&(word), opcode) +#define _Py_MAKE_CODEUNIT(opcode, oparg) _py_make_codeunit((opcode), (oparg)) +#define _Py_SET_OPCODE(word, opcode) _py_set_opcode(&(word), (opcode)) + typedef struct { PyObject *_co_code; diff --git a/Misc/NEWS.d/next/C API/2023-02-14-15-53-01.gh-issue-101907.HgF1N2.rst b/Misc/NEWS.d/next/C API/2023-02-14-15-53-01.gh-issue-101907.HgF1N2.rst new file mode 100644 index 00000000000000..cfc0d72cdbca00 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2023-02-14-15-53-01.gh-issue-101907.HgF1N2.rst @@ -0,0 +1 @@ +Removes use of non-standard C++ extension in public header files. diff --git a/Objects/codeobject.c b/Objects/codeobject.c index ab31b6582cdaae..a03b14edea8d4c 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -413,7 +413,7 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con) PyBytes_GET_SIZE(con->code)); int entry_point = 0; while (entry_point < Py_SIZE(co) && - _Py_OPCODE(_PyCode_CODE(co)[entry_point]) != RESUME) { + _PyCode_CODE(co)[entry_point].op.code != RESUME) { entry_point++; } co->_co_firsttraceable = entry_point; @@ -1505,12 +1505,12 @@ deopt_code(_Py_CODEUNIT *instructions, Py_ssize_t len) { for (int i = 0; i < len; i++) { _Py_CODEUNIT instruction = instructions[i]; - int opcode = _PyOpcode_Deopt[_Py_OPCODE(instruction)]; + int opcode = _PyOpcode_Deopt[instruction.op.code]; int caches = _PyOpcode_Caches[opcode]; - instructions[i].opcode = opcode; + instructions[i].op.code = opcode; while (caches--) { - instructions[++i].opcode = CACHE; - instructions[i].oparg = 0; + instructions[++i].op.code = CACHE; + instructions[i].op.arg = 0; } } } @@ -1763,13 +1763,13 @@ code_richcompare(PyObject *self, PyObject *other, int op) for (int i = 0; i < Py_SIZE(co); i++) { _Py_CODEUNIT co_instr = _PyCode_CODE(co)[i]; _Py_CODEUNIT cp_instr = _PyCode_CODE(cp)[i]; - co_instr.opcode = _PyOpcode_Deopt[_Py_OPCODE(co_instr)]; - cp_instr.opcode =_PyOpcode_Deopt[_Py_OPCODE(cp_instr)]; + co_instr.op.code = _PyOpcode_Deopt[co_instr.op.code]; + cp_instr.op.code = _PyOpcode_Deopt[cp_instr.op.code]; eq = co_instr.cache == cp_instr.cache; if (!eq) { goto unequal; } - i += _PyOpcode_Caches[_Py_OPCODE(co_instr)]; + i += _PyOpcode_Caches[co_instr.op.code]; } /* compare constants */ @@ -1848,9 +1848,9 @@ code_hash(PyCodeObject *co) SCRAMBLE_IN(co->co_firstlineno); SCRAMBLE_IN(Py_SIZE(co)); for (int i = 0; i < Py_SIZE(co); i++) { - int deop = _PyOpcode_Deopt[_Py_OPCODE(_PyCode_CODE(co)[i])]; + int deop = _PyOpcode_Deopt[_PyCode_CODE(co)[i].op.code]; SCRAMBLE_IN(deop); - SCRAMBLE_IN(_Py_OPARG(_PyCode_CODE(co)[i])); + SCRAMBLE_IN(_PyCode_CODE(co)[i].op.arg); i += _PyOpcode_Caches[deop]; } if ((Py_hash_t)uhash == -1) { diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 581ed2d214c4d9..34143c9a40b293 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -111,13 +111,13 @@ static unsigned int get_arg(const _Py_CODEUNIT *codestr, Py_ssize_t i) { _Py_CODEUNIT word; - unsigned int oparg = _Py_OPARG(codestr[i]); - if (i >= 1 && _Py_OPCODE(word = codestr[i-1]) == EXTENDED_ARG) { - oparg |= _Py_OPARG(word) << 8; - if (i >= 2 && _Py_OPCODE(word = codestr[i-2]) == EXTENDED_ARG) { - oparg |= _Py_OPARG(word) << 16; - if (i >= 3 && _Py_OPCODE(word = codestr[i-3]) == EXTENDED_ARG) { - oparg |= _Py_OPARG(word) << 24; + unsigned int oparg = codestr[i].op.arg; + if (i >= 1 && (word = codestr[i-1]).op.code == EXTENDED_ARG) { + oparg |= word.op.arg << 8; + if (i >= 2 && (word = codestr[i-2]).op.code == EXTENDED_ARG) { + oparg |= word.op.arg << 16; + if (i >= 3 && (word = codestr[i-3]).op.code == EXTENDED_ARG) { + oparg |= word.op.arg << 24; } } } @@ -304,7 +304,7 @@ mark_stacks(PyCodeObject *code_obj, int len) if (next_stack == UNINITIALIZED) { continue; } - opcode = _Py_OPCODE(code[i]); + opcode = code[i].op.code; switch (opcode) { case JUMP_IF_FALSE_OR_POP: case JUMP_IF_TRUE_OR_POP: @@ -610,7 +610,7 @@ _PyFrame_GetState(PyFrameObject *frame) if (_PyInterpreterFrame_LASTI(frame->f_frame) < 0) { return FRAME_CREATED; } - switch (_Py_OPCODE(*frame->f_frame->prev_instr)) + switch (frame->f_frame->prev_instr->op.code) { case COPY_FREE_VARS: case MAKE_CELL: @@ -1092,8 +1092,8 @@ _PyFrame_OpAlreadyRan(_PyInterpreterFrame *frame, int opcode, int oparg) for (_Py_CODEUNIT *instruction = _PyCode_CODE(frame->f_code); instruction < frame->prev_instr; instruction++) { - int check_opcode = _PyOpcode_Deopt[_Py_OPCODE(*instruction)]; - check_oparg |= _Py_OPARG(*instruction); + int check_opcode = _PyOpcode_Deopt[instruction->op.code]; + check_oparg |= instruction->op.arg; if (check_opcode == opcode && check_oparg == oparg) { return 1; } @@ -1117,7 +1117,7 @@ frame_init_get_vars(_PyInterpreterFrame *frame) // here: PyCodeObject *co = frame->f_code; int lasti = _PyInterpreterFrame_LASTI(frame); - if (!(lasti < 0 && _Py_OPCODE(_PyCode_CODE(co)[0]) == COPY_FREE_VARS + if (!(lasti < 0 && _PyCode_CODE(co)->op.code == COPY_FREE_VARS && PyFunction_Check(frame->f_funcobj))) { /* Free vars are initialized */ diff --git a/Objects/genobject.c b/Objects/genobject.c index 35246653c45348..aec64ca7004e11 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -332,11 +332,11 @@ _PyGen_yf(PyGenObject *gen) /* Return immediately if the frame didn't start yet. SEND always come after LOAD_CONST: a code object should not start with SEND */ - assert(_Py_OPCODE(_PyCode_CODE(gen->gi_code)[0]) != SEND); + assert(_PyCode_CODE(gen->gi_code)[0].op.code != SEND); return NULL; } _Py_CODEUNIT next = frame->prev_instr[1]; - if (_Py_OPCODE(next) != RESUME || _Py_OPARG(next) < 2) + if (next.op.code != RESUME || next.op.arg < 2) { /* Not in a yield from */ return NULL; @@ -371,9 +371,9 @@ gen_close(PyGenObject *gen, PyObject *args) _PyInterpreterFrame *frame = (_PyInterpreterFrame *)gen->gi_iframe; /* It is possible for the previous instruction to not be a * YIELD_VALUE if the debugger has changed the lineno. */ - if (err == 0 && frame->prev_instr->opcode == YIELD_VALUE) { - assert(frame->prev_instr[1].opcode == RESUME); - int exception_handler_depth = frame->prev_instr->oparg; + if (err == 0 && frame->prev_instr[0].op.code == YIELD_VALUE) { + assert(frame->prev_instr[1].op.code == RESUME); + int exception_handler_depth = frame->prev_instr[0].op.code; assert(exception_handler_depth > 0); /* We can safely ignore the outermost try block * as it automatically generated to handle diff --git a/Objects/typeobject.c b/Objects/typeobject.c index f2e8092aa37eec..2d1220a0695036 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -9507,8 +9507,8 @@ super_init_without_args(_PyInterpreterFrame *cframe, PyCodeObject *co, if (_PyInterpreterFrame_LASTI(cframe) >= 0) { // MAKE_CELL and COPY_FREE_VARS have no quickened forms, so no need // to use _PyOpcode_Deopt here: - assert(_Py_OPCODE(_PyCode_CODE(co)[0]) == MAKE_CELL || - _Py_OPCODE(_PyCode_CODE(co)[0]) == COPY_FREE_VARS); + assert(_PyCode_CODE(co)[0].op.code == MAKE_CELL || + _PyCode_CODE(co)[0].op.code == COPY_FREE_VARS); assert(PyCell_Check(firstarg)); firstarg = PyCell_GET(firstarg); } diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 84747f1758e06c..c5959f2f994fdc 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -246,9 +246,9 @@ dummy_func( DEOPT_IF(!PyUnicode_CheckExact(left), BINARY_OP); DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP); _Py_CODEUNIT true_next = next_instr[INLINE_CACHE_ENTRIES_BINARY_OP]; - assert(_Py_OPCODE(true_next) == STORE_FAST || - _Py_OPCODE(true_next) == STORE_FAST__LOAD_FAST); - PyObject **target_local = &GETLOCAL(_Py_OPARG(true_next)); + assert(true_next.op.code == STORE_FAST || + true_next.op.code == STORE_FAST__LOAD_FAST); + PyObject **target_local = &GETLOCAL(true_next.op.arg); DEOPT_IF(*target_local != left, BINARY_OP); STAT_INC(BINARY_OP, hit); /* Handle `left = left + right` or `left += right` for str. @@ -1748,10 +1748,10 @@ dummy_func( Py_DECREF(left); Py_DECREF(right); ERROR_IF(cond == NULL, error); - assert(_Py_OPCODE(next_instr[1]) == POP_JUMP_IF_FALSE || - _Py_OPCODE(next_instr[1]) == POP_JUMP_IF_TRUE); - bool jump_on_true = _Py_OPCODE(next_instr[1]) == POP_JUMP_IF_TRUE; - int offset = _Py_OPARG(next_instr[1]); + assert(next_instr[1].op.code == POP_JUMP_IF_FALSE || + next_instr[1].op.code == POP_JUMP_IF_TRUE); + bool jump_on_true = next_instr[1].op.code == POP_JUMP_IF_TRUE; + int offset = next_instr[1].op.arg; int err = PyObject_IsTrue(cond); Py_DECREF(cond); if (err < 0) { @@ -1774,7 +1774,7 @@ dummy_func( _Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc); _Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc); if (sign_ish & oparg) { - int offset = _Py_OPARG(next_instr[1]); + int offset = next_instr[1].op.arg; JUMPBY(offset); } } @@ -1795,7 +1795,7 @@ dummy_func( _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); if (sign_ish & oparg) { - int offset = _Py_OPARG(next_instr[1]); + int offset = next_instr[1].op.arg; JUMPBY(offset); } } @@ -1814,7 +1814,7 @@ dummy_func( assert((oparg & 0xf) == COMPARISON_NOT_EQUALS || (oparg & 0xf) == COMPARISON_EQUALS); assert(COMPARISON_NOT_EQUALS + 1 == COMPARISON_EQUALS); if ((res + COMPARISON_NOT_EQUALS) & oparg) { - int offset = _Py_OPARG(next_instr[1]); + int offset = next_instr[1].op.arg; JUMPBY(offset); } } @@ -2122,7 +2122,7 @@ dummy_func( _PyErr_Clear(tstate); } /* iterator ended normally */ - assert(_Py_OPCODE(next_instr[INLINE_CACHE_ENTRIES_FOR_ITER + oparg]) == END_FOR); + assert(next_instr[INLINE_CACHE_ENTRIES_FOR_ITER + oparg].op.code == END_FOR); Py_DECREF(iter); STACK_SHRINK(1); /* Jump forward oparg, then skip following END_FOR instruction */ @@ -2186,7 +2186,7 @@ dummy_func( DEOPT_IF(Py_TYPE(r) != &PyRangeIter_Type, FOR_ITER); STAT_INC(FOR_ITER, hit); _Py_CODEUNIT next = next_instr[INLINE_CACHE_ENTRIES_FOR_ITER]; - assert(_PyOpcode_Deopt[_Py_OPCODE(next)] == STORE_FAST); + assert(_PyOpcode_Deopt[next.op.code] == STORE_FAST); if (r->len <= 0) { STACK_SHRINK(1); Py_DECREF(r); @@ -2197,7 +2197,7 @@ dummy_func( long value = r->start; r->start = value + r->step; r->len--; - if (_PyLong_AssignValue(&GETLOCAL(_Py_OPARG(next)), value) < 0) { + if (_PyLong_AssignValue(&GETLOCAL(next.op.arg), value) < 0) { goto error; } // The STORE_FAST is already done. @@ -2220,7 +2220,7 @@ dummy_func( gen->gi_exc_state.previous_item = tstate->exc_info; tstate->exc_info = &gen->gi_exc_state; JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg); - assert(_Py_OPCODE(*next_instr) == END_FOR); + assert(next_instr->op.code == END_FOR); DISPATCH_INLINED(gen_frame); } @@ -2809,7 +2809,7 @@ dummy_func( STACK_SHRINK(3); // CALL + POP_TOP JUMPBY(INLINE_CACHE_ENTRIES_CALL + 1); - assert(_Py_OPCODE(next_instr[-1]) == POP_TOP); + assert(next_instr[-1].op.code == POP_TOP); DISPATCH(); } @@ -3118,8 +3118,8 @@ dummy_func( inst(EXTENDED_ARG, (--)) { assert(oparg); assert(cframe.use_tracing == 0); - opcode = _Py_OPCODE(*next_instr); - oparg = oparg << 8 | _Py_OPARG(*next_instr); + opcode = next_instr->op.code; + oparg = oparg << 8 | next_instr->op.arg; PRE_DISPATCH_GOTO(); DISPATCH_GOTO(); } diff --git a/Python/ceval.c b/Python/ceval.c index 308ef52259df3d..b85231a1df8a95 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -132,8 +132,8 @@ lltrace_instruction(_PyInterpreterFrame *frame, objects enters the interpreter recursively. It is also slow. So you might want to comment it out. */ dump_stack(frame, stack_pointer); - int oparg = _Py_OPARG(*next_instr); - int opcode = _Py_OPCODE(*next_instr); + int oparg = next_instr->op.arg; + int opcode = next_instr->op.code; const char *opname = _PyOpcode_OpName[opcode]; assert(opname != NULL); int offset = (int)(next_instr - _PyCode_CODE(frame->f_code)); @@ -920,8 +920,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int // CPython hasn't ever traced the instruction after an EXTENDED_ARG. // Inline the EXTENDED_ARG here, so we can avoid branching there: INSTRUCTION_START(EXTENDED_ARG); - opcode = _Py_OPCODE(*next_instr); - oparg = oparg << 8 | _Py_OPARG(*next_instr); + opcode = next_instr->op.code; + oparg = oparg << 8 | next_instr->op.arg; // Make sure the next instruction isn't a RESUME, since that needs // to trace properly (and shouldn't have an EXTENDED_ARG, anyways): assert(opcode != RESUME); @@ -946,7 +946,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #endif /* Tell C compilers not to hold the opcode variable in the loop. next_instr points the current instruction without TARGET(). */ - opcode = _Py_OPCODE(*next_instr); + opcode = next_instr->op.code; _PyErr_Format(tstate, PyExc_SystemError, "%U:%d: unknown opcode %d", frame->f_code->co_filename, @@ -2196,7 +2196,7 @@ maybe_call_line_trace(Py_tracefunc func, PyObject *obj, (_PyInterpreterFrame_LASTI(frame) < instr_prev && // SEND has no quickened forms, so no need to use _PyOpcode_Deopt // here: - _Py_OPCODE(*frame->prev_instr) != SEND); + frame->prev_instr->op.code != SEND); if (trace) { result = call_trace(func, obj, tstate, frame, PyTrace_LINE, Py_None); } diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index 691bf8e1caae95..ac1fec77daca8a 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -100,7 +100,7 @@ #define DISPATCH_SAME_OPARG() \ { \ - opcode = _Py_OPCODE(*next_instr); \ + opcode = next_instr->op.code; \ PRE_DISPATCH_GOTO(); \ opcode |= cframe.use_tracing OR_DTRACE_LINE; \ DISPATCH_GOTO(); \ @@ -143,8 +143,8 @@ GETITEM(PyObject *v, Py_ssize_t i) { #define INSTR_OFFSET() ((int)(next_instr - _PyCode_CODE(frame->f_code))) #define NEXTOPARG() do { \ _Py_CODEUNIT word = *next_instr; \ - opcode = _Py_OPCODE(word); \ - oparg = _Py_OPARG(word); \ + opcode = word.op.code; \ + oparg = word.op.arg; \ } while (0) #define JUMPTO(x) (next_instr = _PyCode_CODE(frame->f_code) + (x)) #define JUMPBY(x) (next_instr += (x)) @@ -180,14 +180,14 @@ GETITEM(PyObject *v, Py_ssize_t i) { #if USE_COMPUTED_GOTOS #define PREDICT(op) if (0) goto PREDICT_ID(op) #else -#define PREDICT(op) \ +#define PREDICT(next_op) \ do { \ _Py_CODEUNIT word = *next_instr; \ - opcode = _Py_OPCODE(word) | cframe.use_tracing OR_DTRACE_LINE; \ - if (opcode == op) { \ - oparg = _Py_OPARG(word); \ - INSTRUCTION_START(op); \ - goto PREDICT_ID(op); \ + opcode = word.op.code | cframe.use_tracing OR_DTRACE_LINE; \ + if (opcode == next_op) { \ + oparg = word.op.arg; \ + INSTRUCTION_START(next_op); \ + goto PREDICT_ID(next_op); \ } \ } while(0) #endif diff --git a/Python/compile.c b/Python/compile.c index c3b344c7af2a7f..3f620beb0d0205 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -274,31 +274,31 @@ write_instr(_Py_CODEUNIT *codestr, struct instr *instruction, int ilen) int caches = _PyOpcode_Caches[opcode]; switch (ilen - caches) { case 4: - codestr->opcode = EXTENDED_ARG; - codestr->oparg = (oparg >> 24) & 0xFF; + codestr->op.code = EXTENDED_ARG; + codestr->op.arg = (oparg >> 24) & 0xFF; codestr++; /* fall through */ case 3: - codestr->opcode = EXTENDED_ARG; - codestr->oparg = (oparg >> 16) & 0xFF; + codestr->op.code = EXTENDED_ARG; + codestr->op.arg = (oparg >> 16) & 0xFF; codestr++; /* fall through */ case 2: - codestr->opcode = EXTENDED_ARG; - codestr->oparg = (oparg >> 8) & 0xFF; + codestr->op.code = EXTENDED_ARG; + codestr->op.arg = (oparg >> 8) & 0xFF; codestr++; /* fall through */ case 1: - codestr->opcode = opcode; - codestr->oparg = oparg & 0xFF; + codestr->op.code = opcode; + codestr->op.arg = oparg & 0xFF; codestr++; break; default: Py_UNREACHABLE(); } while (caches--) { - codestr->opcode = CACHE; - codestr->oparg = 0; + codestr->op.code = CACHE; + codestr->op.arg = 0; codestr++; } } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 730dfb7426acbf..487e63d855d14a 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -339,9 +339,9 @@ DEOPT_IF(!PyUnicode_CheckExact(left), BINARY_OP); DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP); _Py_CODEUNIT true_next = next_instr[INLINE_CACHE_ENTRIES_BINARY_OP]; - assert(_Py_OPCODE(true_next) == STORE_FAST || - _Py_OPCODE(true_next) == STORE_FAST__LOAD_FAST); - PyObject **target_local = &GETLOCAL(_Py_OPARG(true_next)); + assert(true_next.op.code == STORE_FAST || + true_next.op.code == STORE_FAST__LOAD_FAST); + PyObject **target_local = &GETLOCAL(true_next.op.arg); DEOPT_IF(*target_local != left, BINARY_OP); STAT_INC(BINARY_OP, hit); /* Handle `left = left + right` or `left += right` for str. @@ -2199,10 +2199,10 @@ Py_DECREF(left); Py_DECREF(right); if (cond == NULL) goto pop_2_error; - assert(_Py_OPCODE(next_instr[1]) == POP_JUMP_IF_FALSE || - _Py_OPCODE(next_instr[1]) == POP_JUMP_IF_TRUE); - bool jump_on_true = _Py_OPCODE(next_instr[1]) == POP_JUMP_IF_TRUE; - int offset = _Py_OPARG(next_instr[1]); + assert(next_instr[1].op.code == POP_JUMP_IF_FALSE || + next_instr[1].op.code == POP_JUMP_IF_TRUE); + bool jump_on_true = next_instr[1].op.code == POP_JUMP_IF_TRUE; + int offset = next_instr[1].op.arg; int err = PyObject_IsTrue(cond); Py_DECREF(cond); if (err < 0) { @@ -2230,7 +2230,7 @@ _Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc); _Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc); if (sign_ish & oparg) { - int offset = _Py_OPARG(next_instr[1]); + int offset = next_instr[1].op.arg; JUMPBY(offset); } STACK_SHRINK(2); @@ -2255,7 +2255,7 @@ _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); if (sign_ish & oparg) { - int offset = _Py_OPARG(next_instr[1]); + int offset = next_instr[1].op.arg; JUMPBY(offset); } STACK_SHRINK(2); @@ -2278,7 +2278,7 @@ assert((oparg & 0xf) == COMPARISON_NOT_EQUALS || (oparg & 0xf) == COMPARISON_EQUALS); assert(COMPARISON_NOT_EQUALS + 1 == COMPARISON_EQUALS); if ((res + COMPARISON_NOT_EQUALS) & oparg) { - int offset = _Py_OPARG(next_instr[1]); + int offset = next_instr[1].op.arg; JUMPBY(offset); } STACK_SHRINK(2); @@ -2682,7 +2682,7 @@ _PyErr_Clear(tstate); } /* iterator ended normally */ - assert(_Py_OPCODE(next_instr[INLINE_CACHE_ENTRIES_FOR_ITER + oparg]) == END_FOR); + assert(next_instr[INLINE_CACHE_ENTRIES_FOR_ITER + oparg].op.code == END_FOR); Py_DECREF(iter); STACK_SHRINK(1); /* Jump forward oparg, then skip following END_FOR instruction */ @@ -2761,7 +2761,7 @@ DEOPT_IF(Py_TYPE(r) != &PyRangeIter_Type, FOR_ITER); STAT_INC(FOR_ITER, hit); _Py_CODEUNIT next = next_instr[INLINE_CACHE_ENTRIES_FOR_ITER]; - assert(_PyOpcode_Deopt[_Py_OPCODE(next)] == STORE_FAST); + assert(_PyOpcode_Deopt[next.op.code] == STORE_FAST); if (r->len <= 0) { STACK_SHRINK(1); Py_DECREF(r); @@ -2772,7 +2772,7 @@ long value = r->start; r->start = value + r->step; r->len--; - if (_PyLong_AssignValue(&GETLOCAL(_Py_OPARG(next)), value) < 0) { + if (_PyLong_AssignValue(&GETLOCAL(next.op.arg), value) < 0) { goto error; } // The STORE_FAST is already done. @@ -2795,7 +2795,7 @@ gen->gi_exc_state.previous_item = tstate->exc_info; tstate->exc_info = &gen->gi_exc_state; JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg); - assert(_Py_OPCODE(*next_instr) == END_FOR); + assert(next_instr->op.code == END_FOR); DISPATCH_INLINED(gen_frame); } @@ -3516,7 +3516,7 @@ STACK_SHRINK(3); // CALL + POP_TOP JUMPBY(INLINE_CACHE_ENTRIES_CALL + 1); - assert(_Py_OPCODE(next_instr[-1]) == POP_TOP); + assert(next_instr[-1].op.code == POP_TOP); DISPATCH(); } @@ -3903,8 +3903,8 @@ TARGET(EXTENDED_ARG) { assert(oparg); assert(cframe.use_tracing == 0); - opcode = _Py_OPCODE(*next_instr); - oparg = oparg << 8 | _Py_OPARG(*next_instr); + opcode = next_instr->op.code; + oparg = oparg << 8 | next_instr->op.arg; PRE_DISPATCH_GOTO(); DISPATCH_GOTO(); } diff --git a/Python/specialize.c b/Python/specialize.c index 4ede3122d38046..c9555f8ad4dccb 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -282,7 +282,7 @@ _PyCode_Quicken(PyCodeObject *code) _Py_CODEUNIT *instructions = _PyCode_CODE(code); for (int i = 0; i < Py_SIZE(code); i++) { int previous_opcode = opcode; - opcode = _PyOpcode_Deopt[_Py_OPCODE(instructions[i])]; + opcode = _PyOpcode_Deopt[instructions[i].op.code]; int caches = _PyOpcode_Caches[opcode]; if (caches) { instructions[i + 1].cache = adaptive_counter_warmup(); @@ -291,31 +291,31 @@ _PyCode_Quicken(PyCodeObject *code) } switch (previous_opcode << 8 | opcode) { case LOAD_CONST << 8 | LOAD_FAST: - instructions[i - 1].opcode = LOAD_CONST__LOAD_FAST; + instructions[i - 1].op.code = LOAD_CONST__LOAD_FAST; break; case LOAD_FAST << 8 | LOAD_CONST: - instructions[i - 1].opcode = LOAD_FAST__LOAD_CONST; + instructions[i - 1].op.code = LOAD_FAST__LOAD_CONST; break; case LOAD_FAST << 8 | LOAD_FAST: - instructions[i - 1].opcode = LOAD_FAST__LOAD_FAST; + instructions[i - 1].op.code = LOAD_FAST__LOAD_FAST; break; case STORE_FAST << 8 | LOAD_FAST: - instructions[i - 1].opcode = STORE_FAST__LOAD_FAST; + instructions[i - 1].op.code = STORE_FAST__LOAD_FAST; break; case STORE_FAST << 8 | STORE_FAST: - instructions[i - 1].opcode = STORE_FAST__STORE_FAST; + instructions[i - 1].op.code = STORE_FAST__STORE_FAST; break; case COMPARE_OP << 8 | POP_JUMP_IF_TRUE: case COMPARE_OP << 8 | POP_JUMP_IF_FALSE: { - int oparg = instructions[i - 1 - INLINE_CACHE_ENTRIES_COMPARE_OP].oparg; + int oparg = instructions[i - 1 - INLINE_CACHE_ENTRIES_COMPARE_OP].op.arg; assert((oparg >> 4) <= Py_GE); int mask = compare_masks[oparg >> 4]; if (opcode == POP_JUMP_IF_FALSE) { mask = mask ^ 0xf; } - instructions[i - 1 - INLINE_CACHE_ENTRIES_COMPARE_OP].opcode = COMPARE_AND_BRANCH; - instructions[i - 1 - INLINE_CACHE_ENTRIES_COMPARE_OP].oparg = (oparg & 0xf0) | mask; + instructions[i - 1 - INLINE_CACHE_ENTRIES_COMPARE_OP].op.code = COMPARE_AND_BRANCH; + instructions[i - 1 - INLINE_CACHE_ENTRIES_COMPARE_OP].op.arg = (oparg & 0xf0) | mask; break; } } @@ -519,7 +519,7 @@ specialize_module_load_attr( } write_u32(cache->version, keys_version); cache->index = (uint16_t)index; - _py_set_opcode(instr, LOAD_ATTR_MODULE); + instr->op.code = LOAD_ATTR_MODULE; return 0; } @@ -674,7 +674,7 @@ specialize_dict_access( } write_u32(cache->version, type->tp_version_tag); cache->index = (uint16_t)index; - _py_set_opcode(instr, values_op); + instr->op.code = values_op; } else { PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); @@ -694,7 +694,7 @@ specialize_dict_access( } cache->index = (uint16_t)index; write_u32(cache->version, type->tp_version_tag); - _py_set_opcode(instr, hint_op); + instr->op.code = hint_op; } return 1; } @@ -739,7 +739,7 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) goto fail; case METHOD: { - int oparg = _Py_OPARG(*instr); + int oparg = instr->op.arg; if (oparg & 1) { if (specialize_attr_loadmethod(owner, instr, name, descr, kind)) { goto success; @@ -775,7 +775,7 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) write_u32(lm_cache->type_version, type->tp_version_tag); /* borrowed */ write_obj(lm_cache->descr, fget); - _py_set_opcode(instr, LOAD_ATTR_PROPERTY); + instr->op.code = LOAD_ATTR_PROPERTY; goto success; } case OBJECT_SLOT: @@ -799,7 +799,7 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) assert(offset > 0); cache->index = (uint16_t)offset; write_u32(cache->version, type->tp_version_tag); - _py_set_opcode(instr, LOAD_ATTR_SLOT); + instr->op.code = LOAD_ATTR_SLOT; goto success; } case DUNDER_CLASS: @@ -808,7 +808,7 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) assert(offset == (uint16_t)offset); cache->index = (uint16_t)offset; write_u32(cache->version, type->tp_version_tag); - _py_set_opcode(instr, LOAD_ATTR_SLOT); + instr->op.code = LOAD_ATTR_SLOT; goto success; } case OTHER_SLOT: @@ -836,7 +836,7 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) /* borrowed */ write_obj(lm_cache->descr, descr); write_u32(lm_cache->type_version, type->tp_version_tag); - _py_set_opcode(instr, LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN); + instr->op.code = LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN; goto success; } case BUILTIN_CLASSMETHOD: @@ -867,7 +867,7 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) fail: STAT_INC(LOAD_ATTR, failure); assert(!PyErr_Occurred()); - _py_set_opcode(instr, LOAD_ATTR); + instr->op.code = LOAD_ATTR; cache->counter = adaptive_counter_backoff(cache->counter); return; success: @@ -927,7 +927,7 @@ _Py_Specialize_StoreAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) assert(offset > 0); cache->index = (uint16_t)offset; write_u32(cache->version, type->tp_version_tag); - _py_set_opcode(instr, STORE_ATTR_SLOT); + instr->op.code = STORE_ATTR_SLOT; goto success; } case DUNDER_CLASS: @@ -963,7 +963,7 @@ _Py_Specialize_StoreAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) fail: STAT_INC(STORE_ATTR, failure); assert(!PyErr_Occurred()); - _py_set_opcode(instr, STORE_ATTR); + instr->op.code = STORE_ATTR; cache->counter = adaptive_counter_backoff(cache->counter); return; success: @@ -1027,7 +1027,7 @@ specialize_class_load_attr(PyObject *owner, _Py_CODEUNIT *instr, case NON_DESCRIPTOR: write_u32(cache->type_version, ((PyTypeObject *)owner)->tp_version_tag); write_obj(cache->descr, descr); - _py_set_opcode(instr, LOAD_ATTR_CLASS); + instr->op.code = LOAD_ATTR_CLASS; return 0; #ifdef Py_STATS case ABSENT: @@ -1069,7 +1069,7 @@ PyObject *descr, DescriptorClassification kind) return 0; } write_u32(cache->keys_version, keys_version); - _py_set_opcode(instr, LOAD_ATTR_METHOD_WITH_VALUES); + instr->op.code = LOAD_ATTR_METHOD_WITH_VALUES; } else { Py_ssize_t dictoffset = owner_cls->tp_dictoffset; @@ -1078,7 +1078,7 @@ PyObject *descr, DescriptorClassification kind) return 0; } if (dictoffset == 0) { - _py_set_opcode(instr, LOAD_ATTR_METHOD_NO_DICT); + instr->op.code = LOAD_ATTR_METHOD_NO_DICT; } else { PyObject *dict = *(PyObject **) ((char *)owner + dictoffset); @@ -1088,7 +1088,7 @@ PyObject *descr, DescriptorClassification kind) } assert(owner_cls->tp_dictoffset > 0); assert(owner_cls->tp_dictoffset <= INT16_MAX); - _py_set_opcode(instr, LOAD_ATTR_METHOD_LAZY_DICT); + instr->op.code = LOAD_ATTR_METHOD_LAZY_DICT; } } /* `descr` is borrowed. This is safe for methods (even inherited ones from @@ -1146,7 +1146,7 @@ _Py_Specialize_LoadGlobal( } cache->index = (uint16_t)index; write_u32(cache->module_keys_version, keys_version); - _py_set_opcode(instr, LOAD_GLOBAL_MODULE); + instr->op.code = LOAD_GLOBAL_MODULE; goto success; } if (!PyDict_CheckExact(builtins)) { @@ -1184,12 +1184,12 @@ _Py_Specialize_LoadGlobal( cache->index = (uint16_t)index; write_u32(cache->module_keys_version, globals_version); cache->builtin_keys_version = (uint16_t)builtins_version; - _py_set_opcode(instr, LOAD_GLOBAL_BUILTIN); + instr->op.code = LOAD_GLOBAL_BUILTIN; goto success; fail: STAT_INC(LOAD_GLOBAL, failure); assert(!PyErr_Occurred()); - _py_set_opcode(instr, LOAD_GLOBAL); + instr->op.code = LOAD_GLOBAL; cache->counter = adaptive_counter_backoff(cache->counter); return; success: @@ -1295,7 +1295,7 @@ _Py_Specialize_BinarySubscr( if (container_type == &PyList_Type) { if (PyLong_CheckExact(sub)) { if (Py_SIZE(sub) == 0 || Py_SIZE(sub) == 1) { - _py_set_opcode(instr, BINARY_SUBSCR_LIST_INT); + instr->op.code = BINARY_SUBSCR_LIST_INT; goto success; } SPECIALIZATION_FAIL(BINARY_SUBSCR, SPEC_FAIL_OUT_OF_RANGE); @@ -1308,7 +1308,7 @@ _Py_Specialize_BinarySubscr( if (container_type == &PyTuple_Type) { if (PyLong_CheckExact(sub)) { if (Py_SIZE(sub) == 0 || Py_SIZE(sub) == 1) { - _py_set_opcode(instr, BINARY_SUBSCR_TUPLE_INT); + instr->op.code = BINARY_SUBSCR_TUPLE_INT; goto success; } SPECIALIZATION_FAIL(BINARY_SUBSCR, SPEC_FAIL_OUT_OF_RANGE); @@ -1319,7 +1319,7 @@ _Py_Specialize_BinarySubscr( goto fail; } if (container_type == &PyDict_Type) { - _py_set_opcode(instr, BINARY_SUBSCR_DICT); + instr->op.code = BINARY_SUBSCR_DICT; goto success; } PyTypeObject *cls = Py_TYPE(container); @@ -1350,7 +1350,7 @@ _Py_Specialize_BinarySubscr( } cache->func_version = version; ((PyHeapTypeObject *)container_type)->_spec_cache.getitem = descriptor; - _py_set_opcode(instr, BINARY_SUBSCR_GETITEM); + instr->op.code = BINARY_SUBSCR_GETITEM; goto success; } SPECIALIZATION_FAIL(BINARY_SUBSCR, @@ -1358,7 +1358,7 @@ _Py_Specialize_BinarySubscr( fail: STAT_INC(BINARY_SUBSCR, failure); assert(!PyErr_Occurred()); - _py_set_opcode(instr, BINARY_SUBSCR); + instr->op.code = BINARY_SUBSCR; cache->counter = adaptive_counter_backoff(cache->counter); return; success: @@ -1378,7 +1378,7 @@ _Py_Specialize_StoreSubscr(PyObject *container, PyObject *sub, _Py_CODEUNIT *ins if ((Py_SIZE(sub) == 0 || Py_SIZE(sub) == 1) && ((PyLongObject *)sub)->long_value.ob_digit[0] < (size_t)PyList_GET_SIZE(container)) { - _py_set_opcode(instr, STORE_SUBSCR_LIST_INT); + instr->op.code = STORE_SUBSCR_LIST_INT; goto success; } else { @@ -1396,8 +1396,8 @@ _Py_Specialize_StoreSubscr(PyObject *container, PyObject *sub, _Py_CODEUNIT *ins } } if (container_type == &PyDict_Type) { - _py_set_opcode(instr, STORE_SUBSCR_DICT); - goto success; + instr->op.code = STORE_SUBSCR_DICT; + goto success; } #ifdef Py_STATS PyMappingMethods *as_mapping = container_type->tp_as_mapping; @@ -1463,7 +1463,7 @@ _Py_Specialize_StoreSubscr(PyObject *container, PyObject *sub, _Py_CODEUNIT *ins fail: STAT_INC(STORE_SUBSCR, failure); assert(!PyErr_Occurred()); - _py_set_opcode(instr, STORE_SUBSCR); + instr->op.code = STORE_SUBSCR; cache->counter = adaptive_counter_backoff(cache->counter); return; success: @@ -1482,23 +1482,23 @@ specialize_class_call(PyObject *callable, _Py_CODEUNIT *instr, int nargs, return -1; } if (tp->tp_flags & Py_TPFLAGS_IMMUTABLETYPE) { - int oparg = _Py_OPARG(*instr); + int oparg = instr->op.arg; if (nargs == 1 && kwnames == NULL && oparg == 1) { if (tp == &PyUnicode_Type) { - _py_set_opcode(instr, CALL_NO_KW_STR_1); + instr->op.code = CALL_NO_KW_STR_1; return 0; } else if (tp == &PyType_Type) { - _py_set_opcode(instr, CALL_NO_KW_TYPE_1); + instr->op.code = CALL_NO_KW_TYPE_1; return 0; } else if (tp == &PyTuple_Type) { - _py_set_opcode(instr, CALL_NO_KW_TUPLE_1); + instr->op.code = CALL_NO_KW_TUPLE_1; return 0; } } if (tp->tp_vectorcall != NULL) { - _py_set_opcode(instr, CALL_BUILTIN_CLASS); + instr->op.code = CALL_BUILTIN_CLASS; return 0; } SPECIALIZATION_FAIL(CALL, tp == &PyUnicode_Type ? @@ -1573,7 +1573,7 @@ specialize_method_descriptor(PyMethodDescrObject *descr, _Py_CODEUNIT *instr, SPECIALIZATION_FAIL(CALL, SPEC_FAIL_WRONG_NUMBER_ARGUMENTS); return -1; } - _py_set_opcode(instr, CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS); + instr->op.code = CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS; return 0; } case METH_O: { @@ -1584,21 +1584,21 @@ specialize_method_descriptor(PyMethodDescrObject *descr, _Py_CODEUNIT *instr, PyInterpreterState *interp = _PyInterpreterState_GET(); PyObject *list_append = interp->callable_cache.list_append; _Py_CODEUNIT next = instr[INLINE_CACHE_ENTRIES_CALL + 1]; - bool pop = (_Py_OPCODE(next) == POP_TOP); - int oparg = _Py_OPARG(*instr); + bool pop = (next.op.code == POP_TOP); + int oparg = instr->op.arg; if ((PyObject *)descr == list_append && oparg == 1 && pop) { - _py_set_opcode(instr, CALL_NO_KW_LIST_APPEND); + instr->op.code = CALL_NO_KW_LIST_APPEND; return 0; } - _py_set_opcode(instr, CALL_NO_KW_METHOD_DESCRIPTOR_O); + instr->op.code = CALL_NO_KW_METHOD_DESCRIPTOR_O; return 0; } case METH_FASTCALL: { - _py_set_opcode(instr, CALL_NO_KW_METHOD_DESCRIPTOR_FAST); + instr->op.code = CALL_NO_KW_METHOD_DESCRIPTOR_FAST; return 0; } case METH_FASTCALL | METH_KEYWORDS: { - _py_set_opcode(instr, CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS); + instr->op.code = CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS; return 0; } } @@ -1649,14 +1649,14 @@ specialize_py_call(PyFunctionObject *func, _Py_CODEUNIT *instr, int nargs, write_u32(cache->func_version, version); cache->min_args = min_args; if (argcount == nargs) { - _py_set_opcode(instr, bound_method ? CALL_BOUND_METHOD_EXACT_ARGS : CALL_PY_EXACT_ARGS); + instr->op.code = bound_method ? CALL_BOUND_METHOD_EXACT_ARGS : CALL_PY_EXACT_ARGS; } else if (bound_method) { SPECIALIZATION_FAIL(CALL, SPEC_FAIL_CALL_BOUND_METHOD); return -1; } else { - _py_set_opcode(instr, CALL_PY_WITH_DEFAULTS); + instr->op.code = CALL_PY_WITH_DEFAULTS; } return 0; } @@ -1683,10 +1683,10 @@ specialize_c_call(PyObject *callable, _Py_CODEUNIT *instr, int nargs, /* len(o) */ PyInterpreterState *interp = _PyInterpreterState_GET(); if (callable == interp->callable_cache.len) { - _py_set_opcode(instr, CALL_NO_KW_LEN); + instr->op.code = CALL_NO_KW_LEN; return 0; } - _py_set_opcode(instr, CALL_NO_KW_BUILTIN_O); + instr->op.code = CALL_NO_KW_BUILTIN_O; return 0; } case METH_FASTCALL: { @@ -1698,15 +1698,15 @@ specialize_c_call(PyObject *callable, _Py_CODEUNIT *instr, int nargs, /* isinstance(o1, o2) */ PyInterpreterState *interp = _PyInterpreterState_GET(); if (callable == interp->callable_cache.isinstance) { - _py_set_opcode(instr, CALL_NO_KW_ISINSTANCE); + instr->op.code = CALL_NO_KW_ISINSTANCE; return 0; } } - _py_set_opcode(instr, CALL_NO_KW_BUILTIN_FAST); + instr->op.code = CALL_NO_KW_BUILTIN_FAST; return 0; } case METH_FASTCALL | METH_KEYWORDS: { - _py_set_opcode(instr, CALL_BUILTIN_FAST_WITH_KEYWORDS); + instr->op.code = CALL_BUILTIN_FAST_WITH_KEYWORDS; return 0; } default: @@ -1785,7 +1785,7 @@ _Py_Specialize_Call(PyObject *callable, _Py_CODEUNIT *instr, int nargs, if (fail) { STAT_INC(CALL, failure); assert(!PyErr_Occurred()); - _py_set_opcode(instr, CALL); + instr->op.code = CALL; cache->counter = adaptive_counter_backoff(cache->counter); } else { @@ -1880,21 +1880,21 @@ _Py_Specialize_BinaryOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, } if (PyUnicode_CheckExact(lhs)) { _Py_CODEUNIT next = instr[INLINE_CACHE_ENTRIES_BINARY_OP + 1]; - bool to_store = (_Py_OPCODE(next) == STORE_FAST || - _Py_OPCODE(next) == STORE_FAST__LOAD_FAST); - if (to_store && locals[_Py_OPARG(next)] == lhs) { - _py_set_opcode(instr, BINARY_OP_INPLACE_ADD_UNICODE); + bool to_store = (next.op.code == STORE_FAST || + next.op.code == STORE_FAST__LOAD_FAST); + if (to_store && locals[next.op.arg] == lhs) { + instr->op.code = BINARY_OP_INPLACE_ADD_UNICODE; goto success; } - _py_set_opcode(instr, BINARY_OP_ADD_UNICODE); + instr->op.code = BINARY_OP_ADD_UNICODE; goto success; } if (PyLong_CheckExact(lhs)) { - _py_set_opcode(instr, BINARY_OP_ADD_INT); + instr->op.code = BINARY_OP_ADD_INT; goto success; } if (PyFloat_CheckExact(lhs)) { - _py_set_opcode(instr, BINARY_OP_ADD_FLOAT); + instr->op.code = BINARY_OP_ADD_FLOAT; goto success; } break; @@ -1904,11 +1904,11 @@ _Py_Specialize_BinaryOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, break; } if (PyLong_CheckExact(lhs)) { - _py_set_opcode(instr, BINARY_OP_MULTIPLY_INT); + instr->op.code = BINARY_OP_MULTIPLY_INT; goto success; } if (PyFloat_CheckExact(lhs)) { - _py_set_opcode(instr, BINARY_OP_MULTIPLY_FLOAT); + instr->op.code = BINARY_OP_MULTIPLY_FLOAT; goto success; } break; @@ -1918,18 +1918,18 @@ _Py_Specialize_BinaryOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, break; } if (PyLong_CheckExact(lhs)) { - _py_set_opcode(instr, BINARY_OP_SUBTRACT_INT); + instr->op.code = BINARY_OP_SUBTRACT_INT; goto success; } if (PyFloat_CheckExact(lhs)) { - _py_set_opcode(instr, BINARY_OP_SUBTRACT_FLOAT); + instr->op.code = BINARY_OP_SUBTRACT_FLOAT; goto success; } break; } SPECIALIZATION_FAIL(BINARY_OP, binary_op_fail_kind(oparg, lhs, rhs)); STAT_INC(BINARY_OP, failure); - _py_set_opcode(instr, BINARY_OP); + instr->op.code = BINARY_OP; cache->counter = adaptive_counter_backoff(cache->counter); return; success: @@ -1981,7 +1981,7 @@ _Py_Specialize_CompareAndBranch(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *inst assert(_PyOpcode_Caches[COMPARE_AND_BRANCH] == INLINE_CACHE_ENTRIES_COMPARE_OP); _PyCompareOpCache *cache = (_PyCompareOpCache *)(instr + 1); #ifndef NDEBUG - int next_opcode = _Py_OPCODE(instr[INLINE_CACHE_ENTRIES_COMPARE_OP + 1]); + int next_opcode = instr[INLINE_CACHE_ENTRIES_COMPARE_OP + 1].op.code; assert(next_opcode == POP_JUMP_IF_FALSE || next_opcode == POP_JUMP_IF_TRUE); #endif if (Py_TYPE(lhs) != Py_TYPE(rhs)) { @@ -1989,12 +1989,12 @@ _Py_Specialize_CompareAndBranch(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *inst goto failure; } if (PyFloat_CheckExact(lhs)) { - _py_set_opcode(instr, COMPARE_AND_BRANCH_FLOAT); + instr->op.code = COMPARE_AND_BRANCH_FLOAT; goto success; } if (PyLong_CheckExact(lhs)) { if (Py_ABS(Py_SIZE(lhs)) <= 1 && Py_ABS(Py_SIZE(rhs)) <= 1) { - _py_set_opcode(instr, COMPARE_AND_BRANCH_INT); + instr->op.code = COMPARE_AND_BRANCH_INT; goto success; } else { @@ -2009,14 +2009,14 @@ _Py_Specialize_CompareAndBranch(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *inst goto failure; } else { - _py_set_opcode(instr, COMPARE_AND_BRANCH_STR); + instr->op.code = COMPARE_AND_BRANCH_STR; goto success; } } SPECIALIZATION_FAIL(COMPARE_AND_BRANCH, compare_op_fail_kind(lhs, rhs)); failure: STAT_INC(COMPARE_AND_BRANCH, failure); - _py_set_opcode(instr, COMPARE_AND_BRANCH); + instr->op.code = COMPARE_AND_BRANCH; cache->counter = adaptive_counter_backoff(cache->counter); return; success: @@ -2051,10 +2051,10 @@ _Py_Specialize_UnpackSequence(PyObject *seq, _Py_CODEUNIT *instr, int oparg) goto failure; } if (PyTuple_GET_SIZE(seq) == 2) { - _py_set_opcode(instr, UNPACK_SEQUENCE_TWO_TUPLE); + instr->op.code = UNPACK_SEQUENCE_TWO_TUPLE; goto success; } - _py_set_opcode(instr, UNPACK_SEQUENCE_TUPLE); + instr->op.code = UNPACK_SEQUENCE_TUPLE; goto success; } if (PyList_CheckExact(seq)) { @@ -2062,13 +2062,13 @@ _Py_Specialize_UnpackSequence(PyObject *seq, _Py_CODEUNIT *instr, int oparg) SPECIALIZATION_FAIL(UNPACK_SEQUENCE, SPEC_FAIL_EXPECTED_ERROR); goto failure; } - _py_set_opcode(instr, UNPACK_SEQUENCE_LIST); + instr->op.code = UNPACK_SEQUENCE_LIST; goto success; } SPECIALIZATION_FAIL(UNPACK_SEQUENCE, unpack_sequence_fail_kind(seq)); failure: STAT_INC(UNPACK_SEQUENCE, failure); - _py_set_opcode(instr, UNPACK_SEQUENCE); + instr->op.code = UNPACK_SEQUENCE; cache->counter = adaptive_counter_backoff(cache->counter); return; success: @@ -2156,28 +2156,28 @@ _Py_Specialize_ForIter(PyObject *iter, _Py_CODEUNIT *instr, int oparg) _PyForIterCache *cache = (_PyForIterCache *)(instr + 1); PyTypeObject *tp = Py_TYPE(iter); _Py_CODEUNIT next = instr[1+INLINE_CACHE_ENTRIES_FOR_ITER]; - int next_op = _PyOpcode_Deopt[_Py_OPCODE(next)]; + int next_op = _PyOpcode_Deopt[next.op.code]; if (tp == &PyListIter_Type) { - _py_set_opcode(instr, FOR_ITER_LIST); + instr->op.code = FOR_ITER_LIST; goto success; } else if (tp == &PyTupleIter_Type) { - _py_set_opcode(instr, FOR_ITER_TUPLE); + instr->op.code = FOR_ITER_TUPLE; goto success; } else if (tp == &PyRangeIter_Type && next_op == STORE_FAST) { - _py_set_opcode(instr, FOR_ITER_RANGE); + instr->op.code = FOR_ITER_RANGE; goto success; } else if (tp == &PyGen_Type && oparg <= SHRT_MAX) { - assert(_Py_OPCODE(instr[oparg + INLINE_CACHE_ENTRIES_FOR_ITER + 1]) == END_FOR); - _py_set_opcode(instr, FOR_ITER_GEN); + assert(instr[oparg + INLINE_CACHE_ENTRIES_FOR_ITER + 1].op.code == END_FOR); + instr->op.code = FOR_ITER_GEN; goto success; } SPECIALIZATION_FAIL(FOR_ITER, _PySpecialization_ClassifyIterator(iter)); STAT_INC(FOR_ITER, failure); - _py_set_opcode(instr, FOR_ITER); + instr->op.code = FOR_ITER; cache->counter = adaptive_counter_backoff(cache->counter); return; success: @@ -2193,13 +2193,13 @@ _Py_Specialize_Send(PyObject *receiver, _Py_CODEUNIT *instr) _PySendCache *cache = (_PySendCache *)(instr + 1); PyTypeObject *tp = Py_TYPE(receiver); if (tp == &PyGen_Type || tp == &PyCoro_Type) { - _py_set_opcode(instr, SEND_GEN); + instr->op.code = SEND_GEN; goto success; } SPECIALIZATION_FAIL(SEND, _PySpecialization_ClassifyIterator(receiver)); STAT_INC(SEND, failure); - _py_set_opcode(instr, SEND); + instr->op.code = SEND; cache->counter = adaptive_counter_backoff(cache->counter); return; success: diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index aa8e14075c8738..c7f52b55a5eb99 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -8,6 +8,7 @@ import contextlib import dataclasses import os +import posixpath import re import sys import typing @@ -17,7 +18,7 @@ HERE = os.path.dirname(__file__) ROOT = os.path.join(HERE, "../..") -THIS = os.path.relpath(__file__, ROOT) +THIS = os.path.relpath(__file__, ROOT).replace(os.path.sep, posixpath.sep) DEFAULT_INPUT = os.path.relpath(os.path.join(ROOT, "Python/bytecodes.c")) DEFAULT_OUTPUT = os.path.relpath(os.path.join(ROOT, "Python/generated_cases.c.h")) @@ -930,7 +931,7 @@ def write_metadata(self) -> None: with open(self.metadata_filename, "w") as f: # Write provenance header f.write(f"// This file is generated by {THIS} --metadata\n") - f.write(f"// from {os.path.relpath(self.filename, ROOT)}\n") + f.write(f"// from {os.path.relpath(self.filename, ROOT).replace(os.path.sep, posixpath.sep)}\n") f.write(f"// Do not edit!\n") # Create formatter; the rest of the code uses this @@ -1009,7 +1010,7 @@ def write_instructions(self) -> None: with open(self.output_filename, "w") as f: # Write provenance header f.write(f"// This file is generated by {THIS}\n") - f.write(f"// from {os.path.relpath(self.filename, ROOT)}\n") + f.write(f"// from {os.path.relpath(self.filename, ROOT).replace(os.path.sep, posixpath.sep)}\n") f.write(f"// Do not edit!\n") # Create formatter; the rest of the code uses this From ed01addb59a554804995303ad3e7bf0c6067737b Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Tue, 21 Feb 2023 00:20:18 +0900 Subject: [PATCH 143/247] gh-101981: Apply HOMEBREW related environment variables (gh-102074) --- .github/workflows/build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index acc8d936774af5..eec11e25a7c7f6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -154,6 +154,9 @@ jobs: 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@v3 From 59e86caca812fc993c5eb7dc8ccd1508ffccba86 Mon Sep 17 00:00:00 2001 From: Tim Hatch Date: Mon, 20 Feb 2023 09:07:03 -0800 Subject: [PATCH 144/247] gh-88233: zipfile: handle extras after a zip64 extra (GH-96161) Previously, any data _after_ the zip64 extra would be removed. With many new tests. Fixes #88233 Automerge-Triggered-By: GH:jaraco --- Lib/test/test_zipfile/test_core.py | 62 +++++++++++++++++++ Lib/zipfile/__init__.py | 2 + ...2-09-05-12-17-34.gh-issue-88233.gff9qJ.rst | 2 + 3 files changed, 66 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2022-09-05-12-17-34.gh-issue-88233.gff9qJ.rst diff --git a/Lib/test/test_zipfile/test_core.py b/Lib/test/test_zipfile/test_core.py index cf41d0e8cb8d53..e23f5c2a8556f2 100644 --- a/Lib/test/test_zipfile/test_core.py +++ b/Lib/test/test_zipfile/test_core.py @@ -3010,5 +3010,67 @@ def test_cli_with_metadata_encoding_extract(self): self.assertIn(name, listing) +class StripExtraTests(unittest.TestCase): + # Note: all of the "z" characters are technically invalid, but up + # to 3 bytes at the end of the extra will be passed through as they + # are too short to encode a valid extra. + + ZIP64_EXTRA = 1 + + def test_no_data(self): + s = struct.Struct(" Date: Mon, 20 Feb 2023 19:21:10 +0000 Subject: [PATCH 145/247] GH-99818: improve the documentation for zipfile.Path and Traversable (GH-101589) Automerge-Triggered-By: GH:FFY00 --- Doc/library/importlib.resources.abc.rst | 5 ++++- Doc/library/zipfile.rst | 5 +++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Doc/library/importlib.resources.abc.rst b/Doc/library/importlib.resources.abc.rst index 7747e89a833c02..2d0f137ffc7996 100644 --- a/Doc/library/importlib.resources.abc.rst +++ b/Doc/library/importlib.resources.abc.rst @@ -89,9 +89,12 @@ .. class:: Traversable - An object with a subset of pathlib.Path methods suitable for + An object with a subset of :class:`pathlib.Path` methods suitable for traversing directories and opening files. + For a representation of the object on the file-system, use + :meth:`importlib.resources.as_file`. + .. versionadded:: 3.9 .. deprecated-removed:: 3.12 3.14 diff --git a/Doc/library/zipfile.rst b/Doc/library/zipfile.rst index 1c516723f04a33..0195abc3a992c1 100644 --- a/Doc/library/zipfile.rst +++ b/Doc/library/zipfile.rst @@ -55,8 +55,9 @@ The module defines the following items: .. class:: Path :noindex: - A pathlib-compatible wrapper for zip files. See section - :ref:`path-objects` for details. + Class that implements a subset of the interface provided by + :class:`pathlib.Path`, including the full + :class:`importlib.resources.abc.Traversable` interface. .. versionadded:: 3.8 From 36854bbb240e417c0df6f0014924fcc899388186 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 20 Feb 2023 16:01:58 -0500 Subject: [PATCH 146/247] gh-101566: Sync with zipp 3.14. (GH-102018) --- Lib/test/test_zipfile/_context.py | 30 ++++ Lib/test/test_zipfile/_func_timeout_compat.py | 8 + Lib/test/test_zipfile/_itertools.py | 29 ++++ Lib/test/test_zipfile/test_path.py | 137 +++++++++++------- Lib/zipfile/_path.py | 63 +++++++- ...-02-17-20-24-15.gh-issue-101566.FjgWBt.rst | 4 + 6 files changed, 215 insertions(+), 56 deletions(-) create mode 100644 Lib/test/test_zipfile/_context.py create mode 100644 Lib/test/test_zipfile/_func_timeout_compat.py create mode 100644 Misc/NEWS.d/next/Library/2023-02-17-20-24-15.gh-issue-101566.FjgWBt.rst diff --git a/Lib/test/test_zipfile/_context.py b/Lib/test/test_zipfile/_context.py new file mode 100644 index 00000000000000..348798aa08d452 --- /dev/null +++ b/Lib/test/test_zipfile/_context.py @@ -0,0 +1,30 @@ +import contextlib +import time + + +class DeadlineExceeded(Exception): + pass + + +class TimedContext(contextlib.ContextDecorator): + """ + A context that will raise DeadlineExceeded if the + max duration is reached during the execution. + + >>> TimedContext(1)(time.sleep)(.1) + >>> TimedContext(0)(time.sleep)(.1) + Traceback (most recent call last): + ... + tests._context.DeadlineExceeded: (..., 0) + """ + + def __init__(self, max_duration: int): + self.max_duration = max_duration + + def __enter__(self): + self.start = time.monotonic() + + def __exit__(self, *err): + duration = time.monotonic() - self.start + if duration > self.max_duration: + raise DeadlineExceeded(duration, self.max_duration) diff --git a/Lib/test/test_zipfile/_func_timeout_compat.py b/Lib/test/test_zipfile/_func_timeout_compat.py new file mode 100644 index 00000000000000..b1f2b2698a8538 --- /dev/null +++ b/Lib/test/test_zipfile/_func_timeout_compat.py @@ -0,0 +1,8 @@ +try: + from func_timeout import func_set_timeout as set_timeout +except ImportError: # pragma: no cover + # provide a fallback that doesn't actually time out + from ._context import TimedContext as set_timeout + + +__all__ = ['set_timeout'] diff --git a/Lib/test/test_zipfile/_itertools.py b/Lib/test/test_zipfile/_itertools.py index 559f3f111b88a3..74f01fe5ba3de2 100644 --- a/Lib/test/test_zipfile/_itertools.py +++ b/Lib/test/test_zipfile/_itertools.py @@ -1,3 +1,32 @@ +import itertools + + +# from jaraco.itertools 6.3.0 +class Counter: + """ + Wrap an iterable in an object that stores the count of items + that pass through it. + + >>> items = Counter(range(20)) + >>> items.count + 0 + >>> values = list(items) + >>> items.count + 20 + """ + + def __init__(self, i): + self.count = 0 + self.iter = zip(itertools.count(1), i) + + def __iter__(self): + return self + + def __next__(self): + self.count, result = next(self.iter) + return result + + # from more_itertools v8.13.0 def always_iterable(obj, base_type=(str, bytes)): if obj is None: diff --git a/Lib/test/test_zipfile/test_path.py b/Lib/test/test_zipfile/test_path.py index 3086fd2080a97d..92fda690b2fc50 100644 --- a/Lib/test/test_zipfile/test_path.py +++ b/Lib/test/test_zipfile/test_path.py @@ -4,36 +4,25 @@ import pathlib import pickle import string -from test.support.script_helper import assert_python_ok +import sys import unittest import zipfile -from ._test_params import parameterize, Invoked from ._functools import compose +from ._itertools import Counter +from ._test_params import parameterize, Invoked +from ._func_timeout_compat import set_timeout from test.support.os_helper import temp_dir -# Poor man's technique to consume a (smallish) iterable. -consume = tuple - - -# from jaraco.itertools 5.0 class jaraco: class itertools: - class Counter: - def __init__(self, i): - self.count = 0 - self._orig_iter = iter(i) + Counter = Counter - def __iter__(self): - return self - def __next__(self): - result = next(self._orig_iter) - self.count += 1 - return result +consume = tuple def add_dirs(zf): @@ -161,10 +150,10 @@ def test_open_encoding_utf16(self): u16 = path.joinpath("16.txt") with u16.open('r', "utf-16") as strm: data = strm.read() - self.assertEqual(data, "This was utf-16") + assert data == "This was utf-16" with u16.open(encoding="utf-16") as strm: data = strm.read() - self.assertEqual(data, "This was utf-16") + assert data == "This was utf-16" def test_open_encoding_errors(self): in_memory_file = io.BytesIO() @@ -177,9 +166,9 @@ def test_open_encoding_errors(self): # encoding= as a positional argument for gh-101144. data = u16.read_text("utf-8", errors="ignore") - self.assertEqual(data, "invalid utf-8: .") + assert data == "invalid utf-8: ." with u16.open("r", "utf-8", errors="surrogateescape") as f: - self.assertEqual(f.read(), "invalid utf-8: \udcff\udcff.") + assert f.read() == "invalid utf-8: \udcff\udcff." # encoding= both positional and keyword is an error; gh-101144. with self.assertRaisesRegex(TypeError, "encoding"): @@ -191,24 +180,21 @@ def test_open_encoding_errors(self): with self.assertRaises(UnicodeDecodeError): f.read() - def test_encoding_warnings(self): + @unittest.skipIf( + not getattr(sys.flags, 'warn_default_encoding', 0), + "Requires warn_default_encoding", + ) + @pass_alpharep + def test_encoding_warnings(self, alpharep): """EncodingWarning must blame the read_text and open calls.""" - code = '''\ -import io, zipfile -with zipfile.ZipFile(io.BytesIO(), "w") as zf: - zf.filename = '' - zf.writestr("path/file.txt", b"Spanish Inquisition") - root = zipfile.Path(zf) - (path,) = root.iterdir() - file_path = path.joinpath("file.txt") - unused = file_path.read_text() # should warn - file_path.open("r").close() # should warn -''' - proc = assert_python_ok('-X', 'warn_default_encoding', '-c', code) - warnings = proc.err.splitlines() - self.assertEqual(len(warnings), 2, proc.err) - self.assertRegex(warnings[0], rb"^:8: EncodingWarning:") - self.assertRegex(warnings[1], rb"^:9: EncodingWarning:") + assert sys.flags.warn_default_encoding + root = zipfile.Path(alpharep) + with self.assertWarns(EncodingWarning) as wc: + root.joinpath("a.txt").read_text() + assert __file__ == wc.filename + with self.assertWarns(EncodingWarning) as wc: + root.joinpath("a.txt").open("r").close() + assert __file__ == wc.filename def test_open_write(self): """ @@ -250,7 +236,8 @@ def test_read(self, alpharep): root = zipfile.Path(alpharep) a, b, g = root.iterdir() assert a.read_text(encoding="utf-8") == "content of a" - a.read_text("utf-8") # No positional arg TypeError per gh-101144. + # Also check positional encoding arg (gh-101144). + assert a.read_text("utf-8") == "content of a" assert a.read_bytes() == b"content of a" @pass_alpharep @@ -275,19 +262,6 @@ def test_traverse_truediv(self, alpharep): e = root / "b" / "d" / "e.txt" assert e.read_text(encoding="utf-8") == "content of e" - @pass_alpharep - def test_traverse_simplediv(self, alpharep): - """ - Disable the __future__.division when testing traversal. - """ - code = compile( - source="zipfile.Path(alpharep) / 'a'", - filename="(test)", - mode="eval", - dont_inherit=True, - ) - eval(code) - @pass_alpharep def test_pathlike_construction(self, alpharep): """ @@ -356,7 +330,7 @@ def test_joinpath_constant_time(self): # Check the file iterated all items assert entries.count == self.HUGE_ZIPFILE_NUM_ENTRIES - # @func_timeout.func_set_timeout(3) + @set_timeout(3) def test_implied_dirs_performance(self): data = ['/'.join(string.ascii_lowercase + str(n)) for n in range(10000)] zipfile.CompleteDirs._implied_dirs(data) @@ -472,6 +446,52 @@ def test_root_unnamed(self, alpharep): assert sub.name == "b" assert sub.parent + @pass_alpharep + def test_match_and_glob(self, alpharep): + root = zipfile.Path(alpharep) + assert not root.match("*.txt") + + assert list(root.glob("b/c.*")) == [zipfile.Path(alpharep, "b/c.txt")] + + files = root.glob("**/*.txt") + assert all(each.match("*.txt") for each in files) + + assert list(root.glob("**/*.txt")) == list(root.rglob("*.txt")) + + def test_glob_empty(self): + root = zipfile.Path(zipfile.ZipFile(io.BytesIO(), 'w')) + with self.assertRaises(ValueError): + root.glob('') + + @pass_alpharep + def test_eq_hash(self, alpharep): + root = zipfile.Path(alpharep) + assert root == zipfile.Path(alpharep) + + assert root != (root / "a.txt") + assert (root / "a.txt") == (root / "a.txt") + + root = zipfile.Path(alpharep) + assert root in {root} + + @pass_alpharep + def test_is_symlink(self, alpharep): + """ + See python/cpython#82102 for symlink support beyond this object. + """ + + root = zipfile.Path(alpharep) + assert not root.is_symlink() + + @pass_alpharep + def test_relative_to(self, alpharep): + root = zipfile.Path(alpharep) + relative = root.joinpath("b", "c.txt").relative_to(root / "b") + assert str(relative) == "c.txt" + + relative = root.joinpath("b", "d", "e.txt").relative_to(root / "b") + assert str(relative) == "d/e.txt" + @pass_alpharep def test_inheritance(self, alpharep): cls = type('PathChild', (zipfile.Path,), {}) @@ -493,3 +513,14 @@ def test_pickle(self, alpharep, path_type, subpath): restored_1 = pickle.loads(saved_1) first, *rest = restored_1.iterdir() assert first.read_text().startswith('content of ') + + @pass_alpharep + def test_extract_orig_with_implied_dirs(self, alpharep): + """ + A zip file wrapped in a Path should extract even with implied dirs. + """ + source_path = self.zipfile_ondisk(alpharep) + zf = zipfile.ZipFile(source_path) + # wrap the zipfile for its side effect + zipfile.Path(zf) + zf.extractall(source_path.parent) diff --git a/Lib/zipfile/_path.py b/Lib/zipfile/_path.py index 7c7a6a0e2c0d32..c2c804e96d6b6c 100644 --- a/Lib/zipfile/_path.py +++ b/Lib/zipfile/_path.py @@ -4,6 +4,8 @@ import itertools import contextlib import pathlib +import re +import fnmatch __all__ = ['Path'] @@ -93,7 +95,7 @@ def _implied_dirs(names): return _dedupe(_difference(as_dirs, names)) def namelist(self): - names = super(CompleteDirs, self).namelist() + names = super().namelist() return names + list(self._implied_dirs(names)) def _name_set(self): @@ -109,6 +111,17 @@ def resolve_dir(self, name): dir_match = name not in names and dirname in names return dirname if dir_match else name + def getinfo(self, name): + """ + Supplement getinfo for implied dirs. + """ + try: + return super().getinfo(name) + except KeyError: + if not name.endswith('/') or name not in self._name_set(): + raise + return zipfile.ZipInfo(filename=name) + @classmethod def make(cls, source): """ @@ -138,13 +151,13 @@ class FastLookup(CompleteDirs): def namelist(self): with contextlib.suppress(AttributeError): return self.__names - self.__names = super(FastLookup, self).namelist() + self.__names = super().namelist() return self.__names def _name_set(self): with contextlib.suppress(AttributeError): return self.__lookup - self.__lookup = super(FastLookup, self)._name_set() + self.__lookup = super()._name_set() return self.__lookup @@ -246,6 +259,18 @@ def __init__(self, root, at=""): self.root = FastLookup.make(root) self.at = at + def __eq__(self, other): + """ + >>> Path(zipfile.ZipFile(io.BytesIO(), 'w')) == 'foo' + False + """ + if self.__class__ is not other.__class__: + return NotImplemented + return (self.root, self.at) == (other.root, other.at) + + def __hash__(self): + return hash((self.root, self.at)) + def open(self, mode='r', *args, pwd=None, **kwargs): """ Open this entry as text or binary following the semantics @@ -316,6 +341,38 @@ def iterdir(self): subs = map(self._next, self.root.namelist()) return filter(self._is_child, subs) + def match(self, path_pattern): + return pathlib.Path(self.at).match(path_pattern) + + def is_symlink(self): + """ + Return whether this path is a symlink. Always false (python/cpython#82102). + """ + return False + + def _descendants(self): + for child in self.iterdir(): + yield child + if child.is_dir(): + yield from child._descendants() + + def glob(self, pattern): + if not pattern: + raise ValueError(f"Unacceptable pattern: {pattern!r}") + + matches = re.compile(fnmatch.translate(pattern)).fullmatch + return ( + child + for child in self._descendants() + if matches(str(child.relative_to(self))) + ) + + def rglob(self, pattern): + return self.glob(f'**/{pattern}') + + def relative_to(self, other, *extra): + return posixpath.relpath(str(self), str(other.joinpath(*extra))) + def __str__(self): return posixpath.join(self.root.filename, self.at) diff --git a/Misc/NEWS.d/next/Library/2023-02-17-20-24-15.gh-issue-101566.FjgWBt.rst b/Misc/NEWS.d/next/Library/2023-02-17-20-24-15.gh-issue-101566.FjgWBt.rst new file mode 100644 index 00000000000000..5fc1a0288a82dc --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-17-20-24-15.gh-issue-101566.FjgWBt.rst @@ -0,0 +1,4 @@ +In zipfile, sync Path with `zipp 3.14 +`_, including +fix for extractall on the underlying zipfile after being wrapped in +``Path``. From 4d3bc89a3f54c4f09756a9b644b3912bf54191a7 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Mon, 20 Feb 2023 21:54:19 +0000 Subject: [PATCH 147/247] gh-102011: use sys.exception() instead of sys.exc_info() in docs where possible (#102012) --- Doc/library/exceptions.rst | 2 +- Doc/library/traceback.rst | 21 ++++++++++---------- Doc/library/types.rst | 2 +- Doc/library/wsgiref.rst | 2 +- Doc/reference/compound_stmts.rst | 33 +++++++++++++++----------------- Doc/reference/datamodel.rst | 1 + 6 files changed, 29 insertions(+), 32 deletions(-) diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst index 1217b817b4e843..4a57e9c8799336 100644 --- a/Doc/library/exceptions.rst +++ b/Doc/library/exceptions.rst @@ -123,7 +123,7 @@ The following exceptions are used mostly as base classes for other exceptions. try: ... except SomeException: - tb = sys.exc_info()[2] + tb = sys.exception().__traceback__ raise OtherException(...).with_traceback(tb) .. method:: add_note(note) diff --git a/Doc/library/traceback.rst b/Doc/library/traceback.rst index 69818baf184d7c..561c85290463ef 100644 --- a/Doc/library/traceback.rst +++ b/Doc/library/traceback.rst @@ -16,9 +16,8 @@ interpreter. .. index:: object: traceback -The module uses traceback objects --- this is the object type that is stored in -the :data:`sys.last_traceback` variable and returned as the third item from -:func:`sys.exc_info`. +The module uses traceback objects --- these are objects of type :class:`types.TracebackType`, +which are assigned to the ``__traceback__`` field of :class:`BaseException` instances. .. seealso:: @@ -81,7 +80,7 @@ The module defines the following functions: .. function:: print_exc(limit=None, file=None, chain=True) - This is a shorthand for ``print_exception(*sys.exc_info(), limit, file, + This is a shorthand for ``print_exception(sys.exception(), limit, file, chain)``. @@ -444,11 +443,11 @@ exception and traceback: try: lumberjack() except IndexError: - exc_type, exc_value, exc_traceback = sys.exc_info() + exc = sys.exception() print("*** print_tb:") - traceback.print_tb(exc_traceback, limit=1, file=sys.stdout) + traceback.print_tb(exc.__traceback__, limit=1, file=sys.stdout) print("*** print_exception:") - traceback.print_exception(exc_value, limit=2, file=sys.stdout) + traceback.print_exception(exc, limit=2, file=sys.stdout) print("*** print_exc:") traceback.print_exc(limit=2, file=sys.stdout) print("*** format_exc, first and last line:") @@ -456,12 +455,12 @@ exception and traceback: print(formatted_lines[0]) print(formatted_lines[-1]) print("*** format_exception:") - print(repr(traceback.format_exception(exc_value))) + print(repr(traceback.format_exception(exc))) print("*** extract_tb:") - print(repr(traceback.extract_tb(exc_traceback))) + print(repr(traceback.extract_tb(exc.__traceback__))) print("*** format_tb:") - print(repr(traceback.format_tb(exc_traceback))) - print("*** tb_lineno:", exc_traceback.tb_lineno) + print(repr(traceback.format_tb(exc.__traceback__))) + print("*** tb_lineno:", exc.__traceback__.tb_lineno) The output for the example would look similar to this: diff --git a/Doc/library/types.rst b/Doc/library/types.rst index cce0ad960edf97..415413c4cd9747 100644 --- a/Doc/library/types.rst +++ b/Doc/library/types.rst @@ -320,7 +320,7 @@ Standard names are defined for the following types: .. class:: TracebackType(tb_next, tb_frame, tb_lasti, tb_lineno) - The type of traceback objects such as found in ``sys.exc_info()[2]``. + The type of traceback objects such as found in ``sys.exception().__traceback__``. See :ref:`the language reference ` for details of the available attributes and operations, and guidance on creating tracebacks diff --git a/Doc/library/wsgiref.rst b/Doc/library/wsgiref.rst index 75dea466335163..39a4c1ba142338 100644 --- a/Doc/library/wsgiref.rst +++ b/Doc/library/wsgiref.rst @@ -674,7 +674,7 @@ input, output, and error streams. This method is a WSGI application to generate an error page for the user. It is only invoked if an error occurs before headers are sent to the client. - This method can access the current error information using ``sys.exc_info()``, + This method can access the current error using ``sys.exception()``, and should pass that information to *start_response* when calling it (as described in the "Error Handling" section of :pep:`3333`). diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index d5cb2899c231b4..52bb0b22b042f6 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -301,31 +301,28 @@ keeping all locals in that frame alive until the next garbage collection occurs. object: traceback Before an :keyword:`!except` clause's suite is executed, -details about the exception are -stored in the :mod:`sys` module and can be accessed via :func:`sys.exc_info`. -:func:`sys.exc_info` returns a 3-tuple consisting of the exception class, the -exception instance and a traceback object (see section :ref:`types`) identifying -the point in the program where the exception occurred. The details about the -exception accessed via :func:`sys.exc_info` are restored to their previous values -when leaving an exception handler:: - - >>> print(sys.exc_info()) - (None, None, None) +the exception is stored in the :mod:`sys` module, where it can be accessed +from within the body of the :keyword:`!except` clause by calling +:func:`sys.exception`. When leaving an exception handler, the exception +stored in the :mod:`sys` module is reset to its previous value:: + + >>> print(sys.exception()) + None >>> try: ... raise TypeError ... except: - ... print(sys.exc_info()) + ... print(repr(sys.exception())) ... try: ... raise ValueError ... except: - ... print(sys.exc_info()) - ... print(sys.exc_info()) + ... print(repr(sys.exception())) + ... print(repr(sys.exception())) ... - (, TypeError(), ) - (, ValueError(), ) - (, TypeError(), ) - >>> print(sys.exc_info()) - (None, None, None) + TypeError() + ValueError() + TypeError() + >>> print(sys.exception()) + None .. index:: diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index e4e471e50079ae..f447bbb1216d52 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -1122,6 +1122,7 @@ Internal types 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 From 022b44f2546c44183e4df7b67e3e64502fae9143 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Mon, 20 Feb 2023 22:16:09 +0000 Subject: [PATCH 148/247] gh-102056: Fix a few bugs in error handling of exception printing code (#102078) --- Lib/test/test_threading.py | 31 +++++++++++++++++++ ...-02-20-15-18-33.gh-issue-102056.uHKuwH.rst | 1 + Python/pythonrun.c | 23 +++++++++----- 3 files changed, 48 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-20-15-18-33.gh-issue-102056.uHKuwH.rst diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 7fea2d38673eff..a39a267b403d83 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -1523,6 +1523,37 @@ def run(): self.assertEqual(out, b'') self.assertNotIn("Unhandled exception", err.decode()) + def test_print_exception_gh_102056(self): + # This used to crash. See gh-102056. + script = r"""if True: + import time + import threading + import _thread + + def f(): + try: + f() + except RecursionError: + f() + + def g(): + try: + raise ValueError() + except* ValueError: + f() + + def h(): + time.sleep(1) + _thread.interrupt_main() + + t = threading.Thread(target=h) + t.start() + g() + t.join() + """ + + assert_python_failure("-c", script) + def test_bare_raise_in_brand_new_thread(self): def bare_raise(): raise diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-20-15-18-33.gh-issue-102056.uHKuwH.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-20-15-18-33.gh-issue-102056.uHKuwH.rst new file mode 100644 index 00000000000000..78cd525b365fe5 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-20-15-18-33.gh-issue-102056.uHKuwH.rst @@ -0,0 +1 @@ +Fix error handling bugs in interpreter's exception printing code, which could cause a crash on infinite recursion. diff --git a/Python/pythonrun.c b/Python/pythonrun.c index ce993ea8796cb7..34a44dd92847ba 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -1246,8 +1246,7 @@ print_chained(struct exception_print_context* ctx, PyObject *value, const char * message, const char *tag) { PyObject *f = ctx->file; - - if (_Py_EnterRecursiveCall(" in print_chained") < 0) { + if (_Py_EnterRecursiveCall(" in print_chained")) { return -1; } bool need_close = ctx->need_close; @@ -1374,7 +1373,9 @@ print_exception_group(struct exception_print_context *ctx, PyObject *value) if (ctx->exception_group_depth == 0) { ctx->exception_group_depth += 1; } - print_exception(ctx, value); + if (print_exception(ctx, value) < 0) { + return -1; + } PyObject *excs = ((PyBaseExceptionGroupObject *)value)->excs; assert(excs && PyTuple_Check(excs)); @@ -1424,7 +1425,7 @@ print_exception_group(struct exception_print_context *ctx, PyObject *value) PyObject *exc = PyTuple_GET_ITEM(excs, i); if (!truncated) { - if (_Py_EnterRecursiveCall(" in print_exception_group") != 0) { + if (_Py_EnterRecursiveCall(" in print_exception_group")) { return -1; } int res = print_exception_recursive(ctx, exc); @@ -1477,22 +1478,30 @@ print_exception_group(struct exception_print_context *ctx, PyObject *value) static int print_exception_recursive(struct exception_print_context *ctx, PyObject *value) { + if (_Py_EnterRecursiveCall(" in print_exception_recursive")) { + return -1; + } if (ctx->seen != NULL) { /* Exception chaining */ if (print_exception_cause_and_context(ctx, value) < 0) { - return -1; + goto error; } } if (!_PyBaseExceptionGroup_Check(value)) { if (print_exception(ctx, value) < 0) { - return -1; + goto error; } } else if (print_exception_group(ctx, value) < 0) { - return -1; + goto error; } assert(!PyErr_Occurred()); + + _Py_LeaveRecursiveCall(); return 0; +error: + _Py_LeaveRecursiveCall(); + return -1; } #define PyErr_MAX_GROUP_WIDTH 15 From 6f25657b83d7a680a97849490f6e973b3a695e1a Mon Sep 17 00:00:00 2001 From: Gihwan Kim Date: Tue, 21 Feb 2023 12:10:29 +0900 Subject: [PATCH 149/247] gh-101961 fileinput.hookcompressed should not set the encoding value for the binary mode (gh-102068) --- Lib/fileinput.py | 2 +- Lib/test/test_fileinput.py | 39 +++++++++++++------ Misc/ACKS | 1 + ...-02-21-10-05-33.gh-issue-101961.7e56jh.rst | 2 + 4 files changed, 32 insertions(+), 12 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-02-21-10-05-33.gh-issue-101961.7e56jh.rst diff --git a/Lib/fileinput.py b/Lib/fileinput.py index e234dc9ea65f15..1b25f28f3d3432 100644 --- a/Lib/fileinput.py +++ b/Lib/fileinput.py @@ -399,7 +399,7 @@ def isstdin(self): def hook_compressed(filename, mode, *, encoding=None, errors=None): - if encoding is None: # EncodingWarning is emitted in FileInput() already. + if encoding is None and "b" not in mode: # EncodingWarning is emitted in FileInput() already. encoding = "locale" ext = os.path.splitext(filename)[1] if ext == '.gz': diff --git a/Lib/test/test_fileinput.py b/Lib/test/test_fileinput.py index ac20c74baa09e2..786d9186634305 100644 --- a/Lib/test/test_fileinput.py +++ b/Lib/test/test_fileinput.py @@ -855,29 +855,29 @@ def setUp(self): self.fake_open = InvocationRecorder() def test_empty_string(self): - self.do_test_use_builtin_open("", 1) + self.do_test_use_builtin_open_text("", "r") def test_no_ext(self): - self.do_test_use_builtin_open("abcd", 2) + self.do_test_use_builtin_open_text("abcd", "r") @unittest.skipUnless(gzip, "Requires gzip and zlib") def test_gz_ext_fake(self): original_open = gzip.open gzip.open = self.fake_open try: - result = fileinput.hook_compressed("test.gz", "3") + result = fileinput.hook_compressed("test.gz", "r") finally: gzip.open = original_open self.assertEqual(self.fake_open.invocation_count, 1) - self.assertEqual(self.fake_open.last_invocation, (("test.gz", "3"), {})) + self.assertEqual(self.fake_open.last_invocation, (("test.gz", "r"), {})) @unittest.skipUnless(gzip, "Requires gzip and zlib") def test_gz_with_encoding_fake(self): original_open = gzip.open gzip.open = lambda filename, mode: io.BytesIO(b'Ex-binary string') try: - result = fileinput.hook_compressed("test.gz", "3", encoding="utf-8") + result = fileinput.hook_compressed("test.gz", "r", encoding="utf-8") finally: gzip.open = original_open self.assertEqual(list(result), ['Ex-binary string']) @@ -887,23 +887,40 @@ def test_bz2_ext_fake(self): original_open = bz2.BZ2File bz2.BZ2File = self.fake_open try: - result = fileinput.hook_compressed("test.bz2", "4") + result = fileinput.hook_compressed("test.bz2", "r") finally: bz2.BZ2File = original_open self.assertEqual(self.fake_open.invocation_count, 1) - self.assertEqual(self.fake_open.last_invocation, (("test.bz2", "4"), {})) + self.assertEqual(self.fake_open.last_invocation, (("test.bz2", "r"), {})) def test_blah_ext(self): - self.do_test_use_builtin_open("abcd.blah", "5") + self.do_test_use_builtin_open_binary("abcd.blah", "rb") def test_gz_ext_builtin(self): - self.do_test_use_builtin_open("abcd.Gz", "6") + self.do_test_use_builtin_open_binary("abcd.Gz", "rb") def test_bz2_ext_builtin(self): - self.do_test_use_builtin_open("abcd.Bz2", "7") + self.do_test_use_builtin_open_binary("abcd.Bz2", "rb") - def do_test_use_builtin_open(self, filename, mode): + def test_binary_mode_encoding(self): + self.do_test_use_builtin_open_binary("abcd", "rb") + + def test_text_mode_encoding(self): + self.do_test_use_builtin_open_text("abcd", "r") + + def do_test_use_builtin_open_binary(self, filename, mode): + original_open = self.replace_builtin_open(self.fake_open) + try: + result = fileinput.hook_compressed(filename, mode) + finally: + self.replace_builtin_open(original_open) + + self.assertEqual(self.fake_open.invocation_count, 1) + self.assertEqual(self.fake_open.last_invocation, + ((filename, mode), {'encoding': None, 'errors': None})) + + def do_test_use_builtin_open_text(self, filename, mode): original_open = self.replace_builtin_open(self.fake_open) try: result = fileinput.hook_compressed(filename, mode) diff --git a/Misc/ACKS b/Misc/ACKS index ca92608868f23f..a9d4e453c7b59c 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -927,6 +927,7 @@ Tyler Kieft Mads Kiilerich Jason Killen Derek D. Kim +Gihwan Kim Jan Kim Taek Joo Kim Sam Kimbrel diff --git a/Misc/NEWS.d/next/Library/2023-02-21-10-05-33.gh-issue-101961.7e56jh.rst b/Misc/NEWS.d/next/Library/2023-02-21-10-05-33.gh-issue-101961.7e56jh.rst new file mode 100644 index 00000000000000..a3d4119e7cbdce --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-21-10-05-33.gh-issue-101961.7e56jh.rst @@ -0,0 +1,2 @@ +For the binary mode, :func:`fileinput.hookcompressed` doesn't set the ``encoding`` value +even if the value is ``None``. Patch by Gihwan Kim. From 02d9f1504beebd98dea807f4b8761d3675a500d0 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Tue, 21 Feb 2023 09:15:49 +0100 Subject: [PATCH 150/247] gh-101578: Amend exception docs (#102057) Co-authored-by: C.A.M. Gerlach --- Doc/c-api/exceptions.rst | 45 ++++++++++++++++++++-------------------- Doc/data/refcounts.dat | 2 ++ 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst index e735f8db402363..8836c973f1f579 100644 --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -438,7 +438,9 @@ Querying the error indicator .. c:function:: void PyErr_Fetch(PyObject **ptype, PyObject **pvalue, PyObject **ptraceback) - As of 3.12, this function is deprecated. Use :c:func:`PyErr_GetRaisedException` instead. + .. deprecated:: 3.12 + + Use :c:func:`PyErr_GetRaisedException` instead. Retrieve the error indicator into three variables whose addresses are passed. If the error indicator is not set, set all three variables to ``NULL``. If it is @@ -447,8 +449,10 @@ Querying the error indicator .. note:: - This function is normally only used by code that needs to catch exceptions or - by code that needs to save and restore the error indicator temporarily, e.g.:: + This function is normally only used by legacy code that needs to catch + exceptions or save and restore the error indicator temporarily. + + For example:: { PyObject *type, *value, *traceback; @@ -459,15 +463,17 @@ Querying the error indicator PyErr_Restore(type, value, traceback); } - .. deprecated:: 3.12 - .. c:function:: void PyErr_Restore(PyObject *type, PyObject *value, PyObject *traceback) - As of 3.12, this function is deprecated. Use :c:func:`PyErr_SetRaisedException` instead. + .. deprecated:: 3.12 + + Use :c:func:`PyErr_SetRaisedException` instead. - Set the error indicator from the three objects. If the error indicator is - already set, it is cleared first. If the objects are ``NULL``, the error + Set the error indicator from the three objects, + *type*, *value*, and *traceback*, + clearing the existing exception if one is set. + If the objects are ``NULL``, the error indicator is cleared. Do not pass a ``NULL`` type and non-``NULL`` value or traceback. The exception type should be a class. Do not pass an invalid exception type or value. (Violating these rules will cause subtle problems @@ -478,18 +484,17 @@ Querying the error indicator .. note:: - This function is normally only used by code that needs to save and restore the - error indicator temporarily. Use :c:func:`PyErr_Fetch` to save the current - error indicator. - - .. deprecated:: 3.12 + This function is normally only used by legacy code that needs to + save and restore the error indicator temporarily. + Use :c:func:`PyErr_Fetch` to save the current error indicator. .. c:function:: void PyErr_NormalizeException(PyObject **exc, PyObject **val, PyObject **tb) - As of 3.12, this function is deprecated. - Use :c:func:`PyErr_GetRaisedException` instead of :c:func:`PyErr_Fetch` to avoid - any possible de-normalization. + .. deprecated:: 3.12 + + Use :c:func:`PyErr_GetRaisedException` instead, + to avoid any possible de-normalization. Under certain circumstances, the values returned by :c:func:`PyErr_Fetch` below can be "unnormalized", meaning that ``*exc`` is a class object but ``*val`` is @@ -507,8 +512,6 @@ Querying the error indicator PyException_SetTraceback(val, tb); } - .. deprecated:: 3.12 - .. c:function:: PyObject* PyErr_GetHandledException(void) @@ -756,14 +759,12 @@ Exception Objects .. c:function:: PyObject* PyException_GetArgs(PyObject *ex) - Return args of the given exception as a new reference, - as accessible from Python through :attr:`args`. + Return :attr:`~BaseException.args` of exception *ex*. .. c:function:: void PyException_SetArgs(PyObject *ex, PyObject *args) - Set the args of the given exception, - as accessible from Python through :attr:`args`. + Set :attr:`~BaseException.args` of exception *ex* to *args*. .. _unicodeexceptions: diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat index ed2958f8cd2205..ee64ffdc91662e 100644 --- a/Doc/data/refcounts.dat +++ b/Doc/data/refcounts.dat @@ -839,6 +839,8 @@ PyEval_EvalFrameEx:int:throwflag:: PyEval_MergeCompilerFlags:int::: PyEval_MergeCompilerFlags:PyCompilerFlags*:cf:: +PyException_GetArgs:PyObject*::+1: + PyException_GetCause:PyObject*::+1: PyException_GetCause:PyObject*:ex:0: From 0f7a9725300e750947159409abbf5722a4a79f8b Mon Sep 17 00:00:00 2001 From: Owain Davies <116417456+OTheDev@users.noreply.github.com> Date: Tue, 21 Feb 2023 17:03:50 +0700 Subject: [PATCH 151/247] gh-101965: Fix usage of Py_EnterRecursiveCall return value in _bisectmodule.c (GH-101966) Closes #101965 Automerge-Triggered-By: GH:erlend-aasland --- Modules/_bisectmodule.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/_bisectmodule.c b/Modules/_bisectmodule.c index 9ceb3ae46fe56d..d3bec535ee512d 100644 --- a/Modules/_bisectmodule.c +++ b/Modules/_bisectmodule.c @@ -66,7 +66,7 @@ internal_bisect_right(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t if (sq_item == NULL) { return -1; } - if (Py_EnterRecursiveCall("in _bisect.bisect_right") < 0) { + if (Py_EnterRecursiveCall("in _bisect.bisect_right")) { return -1; } PyTypeObject *tp = Py_TYPE(item); @@ -246,7 +246,7 @@ internal_bisect_left(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t h if (sq_item == NULL) { return -1; } - if (Py_EnterRecursiveCall("in _bisect.bisect_left") < 0) { + if (Py_EnterRecursiveCall("in _bisect.bisect_left")) { return -1; } PyTypeObject *tp = Py_TYPE(item); From 350ba7c07f8983537883e093c5c623287a2a22e5 Mon Sep 17 00:00:00 2001 From: Owain Davies <116417456+OTheDev@users.noreply.github.com> Date: Tue, 21 Feb 2023 17:24:33 +0700 Subject: [PATCH 152/247] gh-101777: Make `PriorityQueue` docs slightly clearer (#102026) Adjust wording slightly, and use min(entries) instead of sorted(list(entries))[0] as an example. --- Doc/library/queue.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/queue.rst b/Doc/library/queue.rst index c67f15e953bccc..46b8e9b18a3c1f 100644 --- a/Doc/library/queue.rst +++ b/Doc/library/queue.rst @@ -57,8 +57,8 @@ The :mod:`queue` module defines the following classes and exceptions: *maxsize* is less than or equal to zero, the queue size is infinite. The lowest valued entries are retrieved first (the lowest valued entry is the - one returned by ``sorted(list(entries))[0]``). A typical pattern for entries - is a tuple in the form: ``(priority_number, data)``. + one that would be returned by ``min(entries)``). A typical pattern for + entries is a tuple in the form: ``(priority_number, data)``. If the *data* elements are not comparable, the data can be wrapped in a class that ignores the data item and only compares the priority number:: From b40dd71241f092d90c192ebff5d58cbd7e84dc52 Mon Sep 17 00:00:00 2001 From: ram vikram singh Date: Tue, 21 Feb 2023 16:04:56 +0530 Subject: [PATCH 153/247] gh-100556: Improve clarity of `or` docs (#100589) Co-authored-by: Hugo van Kemenade --- Doc/library/stdtypes.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 98bda6ded30f79..41947d66c15134 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -84,8 +84,8 @@ These are the Boolean operations, ordered by ascending priority: +-------------+---------------------------------+-------+ | Operation | Result | Notes | +=============+=================================+=======+ -| ``x or y`` | if *x* is false, then *y*, else | \(1) | -| | *x* | | +| ``x or y`` | if *x* is true, then *x*, else | \(1) | +| | *y* | | +-------------+---------------------------------+-------+ | ``x and y`` | if *x* is false, then *x*, else | \(2) | | | *y* | | From 7346a381be1bc5911924fcf298408e6909f3949f Mon Sep 17 00:00:00 2001 From: Owain Davies <116417456+OTheDev@users.noreply.github.com> Date: Tue, 21 Feb 2023 18:58:47 +0700 Subject: [PATCH 154/247] gh-101903: Remove obsolete undefs for previously removed macros Py_EnterRecursiveCall and Py_LeaveRecursiveCall (#101923) --- Python/ceval.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index b85231a1df8a95..001bdb15c0f755 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3068,15 +3068,11 @@ maybe_dtrace_line(_PyInterpreterFrame *frame, /* Implement Py_EnterRecursiveCall() and Py_LeaveRecursiveCall() as functions for the limited API. */ -#undef Py_EnterRecursiveCall - int Py_EnterRecursiveCall(const char *where) { return _Py_EnterRecursiveCall(where); } -#undef Py_LeaveRecursiveCall - void Py_LeaveRecursiveCall(void) { _Py_LeaveRecursiveCall(); From c2b85a95a50687a2e5d1873e17266370876e77e9 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Tue, 21 Feb 2023 15:11:31 +0000 Subject: [PATCH 155/247] gh-102008: simplify test_except_star by using sys.exception() instead of sys.exc_info() (#102009) --- Lib/test/test_except_star.py | 44 ++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/Lib/test/test_except_star.py b/Lib/test/test_except_star.py index 9de72dbd5a3264..c5167c5bba38af 100644 --- a/Lib/test/test_except_star.py +++ b/Lib/test/test_except_star.py @@ -208,44 +208,38 @@ def assertMetadataNotEqual(self, e1, e2): class TestExceptStarSplitSemantics(ExceptStarTest): def doSplitTestNamed(self, exc, T, match_template, rest_template): - initial_exc_info = sys.exc_info() - exc_info = match = rest = None + initial_sys_exception = sys.exception() + sys_exception = match = rest = None try: try: raise exc except* T as e: - exc_info = sys.exc_info() + sys_exception = sys.exception() match = e except BaseException as e: rest = e - if match_template: - self.assertEqual(exc_info[1], match) - else: - self.assertIsNone(exc_info) + self.assertEqual(sys_exception, match) self.assertExceptionIsLike(match, match_template) self.assertExceptionIsLike(rest, rest_template) - self.assertEqual(sys.exc_info(), initial_exc_info) + self.assertEqual(sys.exception(), initial_sys_exception) def doSplitTestUnnamed(self, exc, T, match_template, rest_template): - initial_exc_info = sys.exc_info() - exc_info = match = rest = None + initial_sys_exception = sys.exception() + sys_exception = match = rest = None try: try: raise exc except* T: - exc_info = sys.exc_info() - match = sys.exc_info()[1] + sys_exception = match = sys.exception() else: if rest_template: self.fail("Exception not raised") except BaseException as e: rest = e self.assertExceptionIsLike(match, match_template) - if match_template: - self.assertEqual(exc_info[0], type(match_template)) self.assertExceptionIsLike(rest, rest_template) - self.assertEqual(sys.exc_info(), initial_exc_info) + self.assertEqual(sys.exception(), initial_sys_exception) def doSplitTestInExceptHandler(self, exc, T, match_template, rest_template): try: @@ -409,11 +403,11 @@ def test_multiple_matches_unnamed(self): try: raise ExceptionGroup("mmu", [OSError("os"), BlockingIOError("io")]) except* BlockingIOError: - e = sys.exc_info()[1] + e = sys.exception() self.assertExceptionIsLike(e, ExceptionGroup("mmu", [BlockingIOError("io")])) except* OSError: - e = sys.exc_info()[1] + e = sys.exception() self.assertExceptionIsLike(e, ExceptionGroup("mmu", [OSError("os")])) else: @@ -434,7 +428,7 @@ def test_first_match_wins_unnamed(self): try: raise ExceptionGroup("fstu", [BlockingIOError("io")]) except* OSError: - e = sys.exc_info()[1] + e = sys.exception() self.assertExceptionIsLike(e, ExceptionGroup("fstu", [BlockingIOError("io")])) except* BlockingIOError: @@ -452,7 +446,7 @@ def test_nested_except_stars(self): pass else: self.fail("Exception not raised") - e = sys.exc_info()[1] + e = sys.exception() self.assertExceptionIsLike(e, ExceptionGroup("n", [BlockingIOError("io")])) else: @@ -766,7 +760,7 @@ def test_raise_unnamed(self): try: raise orig except* OSError: - e = sys.exc_info()[1] + e = sys.exception() raise TypeError(3) from e except ExceptionGroup as e: exc = e @@ -821,7 +815,7 @@ def test_raise_handle_all_raise_one_unnamed(self): try: raise orig except* (TypeError, ValueError) as e: - e = sys.exc_info()[1] + e = sys.exception() raise SyntaxError(3) from e except ExceptionGroup as e: exc = e @@ -882,10 +876,10 @@ def test_raise_handle_all_raise_two_unnamed(self): try: raise orig except* TypeError: - e = sys.exc_info()[1] + e = sys.exception() raise SyntaxError(3) from e except* ValueError: - e = sys.exc_info()[1] + e = sys.exception() raise SyntaxError(4) from e except ExceptionGroup as e: exc = e @@ -982,7 +976,7 @@ def derive(self, excs): class TestExceptStarCleanup(ExceptStarTest): - def test_exc_info_restored(self): + def test_sys_exception_restored(self): try: try: raise ValueError(42) @@ -997,7 +991,7 @@ def test_exc_info_restored(self): self.assertExceptionIsLike(exc, ZeroDivisionError('division by zero')) self.assertExceptionIsLike(exc.__context__, ValueError(42)) - self.assertEqual(sys.exc_info(), (None, None, None)) + self.assertEqual(sys.exception(), None) class TestExceptStar_WeirdLeafExceptions(ExceptStarTest): From 0d4c7fcd4f078708a5ac6499af378ce5ee8eb211 Mon Sep 17 00:00:00 2001 From: Vo Hoang Long Date: Tue, 21 Feb 2023 22:14:41 +0700 Subject: [PATCH 156/247] gh-101936: Update the default value of fp from io.StringIO to io.BytesIO (gh-102100) Co-authored-by: Long Vo --- Lib/test/test_urllib2.py | 1 + Lib/urllib/error.py | 2 +- Misc/ACKS | 1 + .../next/Library/2023-02-21-07-15-41.gh-issue-101936.QVOxHH.rst | 2 ++ 4 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2023-02-21-07-15-41.gh-issue-101936.QVOxHH.rst diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py index 498c0382d2137b..633d596ac3de3f 100644 --- a/Lib/test/test_urllib2.py +++ b/Lib/test/test_urllib2.py @@ -1827,6 +1827,7 @@ def test_HTTPError_interface(self): def test_gh_98778(self): x = urllib.error.HTTPError("url", 405, "METHOD NOT ALLOWED", None, None) self.assertEqual(getattr(x, "__notes__", ()), ()) + self.assertIsInstance(x.fp.read(), bytes) def test_parse_proxy(self): parse_proxy_test_cases = [ diff --git a/Lib/urllib/error.py b/Lib/urllib/error.py index feec0e7f848e46..a9cd1ecadd63f2 100644 --- a/Lib/urllib/error.py +++ b/Lib/urllib/error.py @@ -43,7 +43,7 @@ def __init__(self, url, code, msg, hdrs, fp): self.fp = fp self.filename = url if fp is None: - fp = io.StringIO() + fp = io.BytesIO() self.__super_init(fp, hdrs, url, code) def __str__(self): diff --git a/Misc/ACKS b/Misc/ACKS index a9d4e453c7b59c..ab19d69b0133d2 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1898,6 +1898,7 @@ Kurt Vile Norman Vine Pauli Virtanen Frank Visser +Long Vo Johannes Vogel Michael Vogt Radu Voicilas diff --git a/Misc/NEWS.d/next/Library/2023-02-21-07-15-41.gh-issue-101936.QVOxHH.rst b/Misc/NEWS.d/next/Library/2023-02-21-07-15-41.gh-issue-101936.QVOxHH.rst new file mode 100644 index 00000000000000..55841da44b1146 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-21-07-15-41.gh-issue-101936.QVOxHH.rst @@ -0,0 +1,2 @@ +The default value of ``fp`` becomes :class:`io.BytesIO` if :exc:`~urllib.error.HTTPError` +is initialized without a designated ``fp`` parameter. Patch by Long Vo. From d5c7954d0c3ff874d2d27d33dcc207bb7356f328 Mon Sep 17 00:00:00 2001 From: Hyunkyun Moon Date: Wed, 22 Feb 2023 02:39:00 +0900 Subject: [PATCH 157/247] gh-95672 fix typo SkitTest to SkipTest (gh-102119) Co-authored-by: HyunKyun Moon --- Lib/test/test_fcntl.py | 2 +- Lib/test/test_subprocess.py | 2 +- Misc/ACKS | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_fcntl.py b/Lib/test/test_fcntl.py index 113c7802821dd4..26de67d924272a 100644 --- a/Lib/test/test_fcntl.py +++ b/Lib/test/test_fcntl.py @@ -202,7 +202,7 @@ def test_fcntl_f_pipesize(self): pipesize_default = fcntl.fcntl(test_pipe_w, fcntl.F_GETPIPE_SZ) pipesize = pipesize_default // 2 # A new value to detect change. if pipesize < 512: # the POSIX minimum - raise unittest.SkitTest( + raise unittest.SkipTest( 'default pipesize too small to perform test.') fcntl.fcntl(test_pipe_w, fcntl.F_SETPIPE_SZ, pipesize) self.assertEqual(fcntl.fcntl(test_pipe_w, fcntl.F_GETPIPE_SZ), diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index abd0dd8b25699b..727b0e6dc578c2 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -718,7 +718,7 @@ def test_pipesizes(self): os.close(test_pipe_w) pipesize = pipesize_default // 2 if pipesize < 512: # the POSIX minimum - raise unittest.SkitTest( + raise unittest.SkipTest( 'default pipesize too small to perform test.') p = subprocess.Popen( [sys.executable, "-c", diff --git a/Misc/ACKS b/Misc/ACKS index ab19d69b0133d2..33dbf4e989d96a 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1229,6 +1229,7 @@ The Dragon De Monsyne Bastien Montagne Skip Montanaro Peter Moody +HyunKyun Moon Alan D. Moore Nicolai Moore Paul Moore From 3ba7743b0696202e9caa283a0be253fd26a5cfbd Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Tue, 21 Feb 2023 20:21:24 -0500 Subject: [PATCH 158/247] gh-99942: python.pc on android/cygwin should link to libpython per configure.ac (GH-100356) In commit 254b309c801f82509597e3d7d4be56885ef94c11 a previous change to avoid linking to libpython was partially reverted for Android (and later Cygwin as well), to add back the link flags. This was applied to distutils and to python-config.sh, but not to python.pc. Add it back to python.pc as well. Automerge-Triggered-By: GH:gpshead --- .../next/Build/2022-12-20-01-06-17.gh-issue-99942.lbmzYj.rst | 2 ++ Misc/python.pc.in | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Build/2022-12-20-01-06-17.gh-issue-99942.lbmzYj.rst diff --git a/Misc/NEWS.d/next/Build/2022-12-20-01-06-17.gh-issue-99942.lbmzYj.rst b/Misc/NEWS.d/next/Build/2022-12-20-01-06-17.gh-issue-99942.lbmzYj.rst new file mode 100644 index 00000000000000..63a640a9cdf733 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2022-12-20-01-06-17.gh-issue-99942.lbmzYj.rst @@ -0,0 +1,2 @@ +On Android, python.pc now correctly reports the library to link to, the same +as python-config.sh. diff --git a/Misc/python.pc.in b/Misc/python.pc.in index 87e04decc2a2d5..027dba38585a89 100644 --- a/Misc/python.pc.in +++ b/Misc/python.pc.in @@ -9,5 +9,5 @@ Description: Build a C extension for Python Requires: Version: @VERSION@ Libs.private: @LIBS@ -Libs: +Libs: -L${libdir} @LIBPYTHON@ Cflags: -I${includedir}/python@VERSION@@ABIFLAGS@ From 8d46c7ed5e83e22d55fe4f4e6e873d87f340c1dc Mon Sep 17 00:00:00 2001 From: somebody <98094921+UndoneStudios@users.noreply.github.com> Date: Wed, 22 Feb 2023 14:11:30 +0400 Subject: [PATCH 159/247] gh-102135: Update turtle docs to rename wikipedia demo to rosette (#102137) --- Doc/library/turtle.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/library/turtle.rst b/Doc/library/turtle.rst index 5add61c759ea8e..b7eb569c18a6c4 100644 --- a/Doc/library/turtle.rst +++ b/Doc/library/turtle.rst @@ -2444,6 +2444,9 @@ The demo scripts are: | planet_and_moon| simulation of | compound shapes, | | | gravitational system | :class:`Vec2D` | +----------------+------------------------------+-----------------------+ +| rosette | a pattern from the wikipedia | :func:`clone`, | +| | article on turtle graphics | :func:`undo` | ++----------------+------------------------------+-----------------------+ | round_dance | dancing turtles rotating | compound shapes, clone| | | pairwise in opposite | shapesize, tilt, | | | direction | get_shapepoly, update | @@ -2457,9 +2460,6 @@ The demo scripts are: | two_canvases | simple design | turtles on two | | | | canvases | +----------------+------------------------------+-----------------------+ -| wikipedia | a pattern from the wikipedia | :func:`clone`, | -| | article on turtle graphics | :func:`undo` | -+----------------+------------------------------+-----------------------+ | yinyang | another elementary example | :func:`circle` | +----------------+------------------------------+-----------------------+ From 7c106a443f8cf1111947a425eed11ecf9e615ce3 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 22 Feb 2023 11:11:57 +0000 Subject: [PATCH 160/247] GH-100982: Restrict `FOR_ITER_RANGE` to a single instruction to allow instrumentation. (GH-101985) --- ...-02-17-10-12-13.gh-issue-100982.mJGJQw.rst | 2 ++ Python/bytecodes.c | 24 +++++++------------ Python/generated_cases.c.h | 22 ++++++++--------- Python/specialize.c | 4 +--- 4 files changed, 22 insertions(+), 30 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-17-10-12-13.gh-issue-100982.mJGJQw.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-17-10-12-13.gh-issue-100982.mJGJQw.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-17-10-12-13.gh-issue-100982.mJGJQw.rst new file mode 100644 index 00000000000000..53bbc860c53f37 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-17-10-12-13.gh-issue-100982.mJGJQw.rst @@ -0,0 +1,2 @@ +Restrict the scope of the :opcode:`FOR_ITER_RANGE` instruction to the scope of the +original :opcode:`FOR_ITER` instruction, to allow instrumentation. diff --git a/Python/bytecodes.c b/Python/bytecodes.c index c5959f2f994fdc..ad68c794fe7acb 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2178,35 +2178,27 @@ dummy_func( // Common case: no jump, leave it to the code generator } - // This is slightly different, when the loop isn't terminated we - // jump over the immediately following STORE_FAST instruction. - inst(FOR_ITER_RANGE, (unused/1, iter -- iter, unused)) { + inst(FOR_ITER_RANGE, (unused/1, iter -- iter, next)) { assert(cframe.use_tracing == 0); _PyRangeIterObject *r = (_PyRangeIterObject *)iter; DEOPT_IF(Py_TYPE(r) != &PyRangeIter_Type, FOR_ITER); STAT_INC(FOR_ITER, hit); - _Py_CODEUNIT next = next_instr[INLINE_CACHE_ENTRIES_FOR_ITER]; - assert(_PyOpcode_Deopt[next.op.code] == STORE_FAST); if (r->len <= 0) { STACK_SHRINK(1); Py_DECREF(r); // Jump over END_FOR instruction. JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1); + DISPATCH(); } - else { - long value = r->start; - r->start = value + r->step; - r->len--; - if (_PyLong_AssignValue(&GETLOCAL(next.op.arg), value) < 0) { - goto error; - } - // The STORE_FAST is already done. - JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + 1); + long value = r->start; + r->start = value + r->step; + r->len--; + next = PyLong_FromLong(value); + if (next == NULL) { + goto error; } - DISPATCH(); } - // This is *not* a super-instruction, unique in the family. inst(FOR_ITER_GEN, (unused/1, iter -- iter, unused)) { assert(cframe.use_tracing == 0); PyGenObject *gen = (PyGenObject *)iter; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 487e63d855d14a..2987adc3bba566 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2756,28 +2756,28 @@ TARGET(FOR_ITER_RANGE) { PyObject *iter = PEEK(1); + PyObject *next; assert(cframe.use_tracing == 0); _PyRangeIterObject *r = (_PyRangeIterObject *)iter; DEOPT_IF(Py_TYPE(r) != &PyRangeIter_Type, FOR_ITER); STAT_INC(FOR_ITER, hit); - _Py_CODEUNIT next = next_instr[INLINE_CACHE_ENTRIES_FOR_ITER]; - assert(_PyOpcode_Deopt[next.op.code] == STORE_FAST); if (r->len <= 0) { STACK_SHRINK(1); Py_DECREF(r); // Jump over END_FOR instruction. JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1); + DISPATCH(); } - else { - long value = r->start; - r->start = value + r->step; - r->len--; - if (_PyLong_AssignValue(&GETLOCAL(next.op.arg), value) < 0) { - goto error; - } - // The STORE_FAST is already done. - JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + 1); + long value = r->start; + r->start = value + r->step; + r->len--; + next = PyLong_FromLong(value); + if (next == NULL) { + goto error; } + STACK_GROW(1); + POKE(1, next); + JUMPBY(1); DISPATCH(); } diff --git a/Python/specialize.c b/Python/specialize.c index c9555f8ad4dccb..3405d2b0ab0680 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -2155,8 +2155,6 @@ _Py_Specialize_ForIter(PyObject *iter, _Py_CODEUNIT *instr, int oparg) assert(_PyOpcode_Caches[FOR_ITER] == INLINE_CACHE_ENTRIES_FOR_ITER); _PyForIterCache *cache = (_PyForIterCache *)(instr + 1); PyTypeObject *tp = Py_TYPE(iter); - _Py_CODEUNIT next = instr[1+INLINE_CACHE_ENTRIES_FOR_ITER]; - int next_op = _PyOpcode_Deopt[next.op.code]; if (tp == &PyListIter_Type) { instr->op.code = FOR_ITER_LIST; goto success; @@ -2165,7 +2163,7 @@ _Py_Specialize_ForIter(PyObject *iter, _Py_CODEUNIT *instr, int oparg) instr->op.code = FOR_ITER_TUPLE; goto success; } - else if (tp == &PyRangeIter_Type && next_op == STORE_FAST) { + else if (tp == &PyRangeIter_Type) { instr->op.code = FOR_ITER_RANGE; goto success; } From 592f65fdb551f64a2db7849500e5df2291637f25 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Wed, 22 Feb 2023 22:10:01 +0300 Subject: [PATCH 161/247] Few coverage nitpicks for the cmath module (#102067) - partial tests for cosh/sinh overflows (L535 and L771). I doubt both ||-ed conditions could be tested. - removed inaccessible case in sqrt (L832): ax=ay=0 is handled above (L823) because fabs() is exact. Also added test (checked with mpmath and gmpy2) for second condition on that line. - some trivial tests for isclose (cover all conditions on L1217-1218) - add comment for uncovered L1018 Co-authored-by: Mark Dickinson --- Lib/test/cmath_testcases.txt | 3 +++ Lib/test/test_cmath.py | 8 ++++++++ Modules/cmathmodule.c | 4 ++-- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Lib/test/cmath_testcases.txt b/Lib/test/cmath_testcases.txt index dd7e458ddcb7b1..0165e17634f41c 100644 --- a/Lib/test/cmath_testcases.txt +++ b/Lib/test/cmath_testcases.txt @@ -1536,6 +1536,7 @@ sqrt0141 sqrt -1.797e+308 -9.9999999999999999e+306 -> 3.7284476432057307e+152 -1 sqrt0150 sqrt 1.7976931348623157e+308 0.0 -> 1.3407807929942596355e+154 0.0 sqrt0151 sqrt 2.2250738585072014e-308 0.0 -> 1.4916681462400413487e-154 0.0 sqrt0152 sqrt 5e-324 0.0 -> 2.2227587494850774834e-162 0.0 +sqrt0153 sqrt 5e-324 1.0 -> 0.7071067811865476 0.7071067811865476 -- special values sqrt1000 sqrt 0.0 0.0 -> 0.0 0.0 @@ -1744,6 +1745,7 @@ cosh0023 cosh 2.218885944363501 2.0015727395883687 -> -1.94294321081968 4.129026 -- large real part cosh0030 cosh 710.5 2.3519999999999999 -> -1.2967465239355998e+308 1.3076707908857333e+308 cosh0031 cosh -710.5 0.69999999999999996 -> 1.4085466381392499e+308 -1.1864024666450239e+308 +cosh0032 cosh 720.0 0.0 -> inf 0.0 overflow -- Additional real values (mpmath) cosh0050 cosh 1e-150 0.0 -> 1.0 0.0 @@ -1853,6 +1855,7 @@ sinh0023 sinh 0.043713693678420068 0.22512549887532657 -> 0.042624198673416713 0 -- large real part sinh0030 sinh 710.5 -2.3999999999999999 -> -1.3579970564885919e+308 -1.24394470907798e+308 sinh0031 sinh -710.5 0.80000000000000004 -> -1.2830671601735164e+308 1.3210954193997678e+308 +sinh0032 sinh 720.0 0.0 -> inf 0.0 overflow -- Additional real values (mpmath) sinh0050 sinh 1e-100 0.0 -> 1.00000000000000002e-100 0.0 diff --git a/Lib/test/test_cmath.py b/Lib/test/test_cmath.py index 9fa08dc4ff3fa7..cd2c6939105d40 100644 --- a/Lib/test/test_cmath.py +++ b/Lib/test/test_cmath.py @@ -607,6 +607,14 @@ def test_complex_near_zero(self): self.assertIsClose(0.001-0.001j, 0.001+0.001j, abs_tol=2e-03) self.assertIsNotClose(0.001-0.001j, 0.001+0.001j, abs_tol=1e-03) + def test_complex_special(self): + self.assertIsNotClose(INF, INF*1j) + self.assertIsNotClose(INF*1j, INF) + self.assertIsNotClose(INF, -INF) + self.assertIsNotClose(-INF, INF) + self.assertIsNotClose(0, INF) + self.assertIsNotClose(0, INF*1j) + if __name__ == "__main__": unittest.main() diff --git a/Modules/cmathmodule.c b/Modules/cmathmodule.c index 53e34061d53773..b4f7e5424b4ccf 100644 --- a/Modules/cmathmodule.c +++ b/Modules/cmathmodule.c @@ -829,7 +829,7 @@ cmath_sqrt_impl(PyObject *module, Py_complex z) ax = fabs(z.real); ay = fabs(z.imag); - if (ax < DBL_MIN && ay < DBL_MIN && (ax > 0. || ay > 0.)) { + if (ax < DBL_MIN && ay < DBL_MIN) { /* here we catch cases where hypot(ax, ay) is subnormal */ ax = ldexp(ax, CM_SCALE_UP); s = ldexp(sqrt(ax + hypot(ax, ldexp(ay, CM_SCALE_UP))), @@ -1013,7 +1013,7 @@ cmath_phase_impl(PyObject *module, Py_complex z) double phi; errno = 0; - phi = c_atan2(z); + phi = c_atan2(z); /* should not cause any exception */ if (errno != 0) return math_error(); else From 96bf24380e44dfa1516d65480250995e737c0cb9 Mon Sep 17 00:00:00 2001 From: Owain Davies <116417456+OTheDev@users.noreply.github.com> Date: Thu, 23 Feb 2023 03:21:38 +0700 Subject: [PATCH 162/247] GH-101777: `queue.rst`: use 2 spaces after a period to be consistent. (#102143) --- Doc/library/queue.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Doc/library/queue.rst b/Doc/library/queue.rst index 46b8e9b18a3c1f..b2b787c5a8260c 100644 --- a/Doc/library/queue.rst +++ b/Doc/library/queue.rst @@ -15,7 +15,7 @@ module implements all the required locking semantics. The module implements three types of queue, which differ only in the order in which the entries are retrieved. In a :abbr:`FIFO (first-in, first-out)` -queue, the first tasks added are the first retrieved. In a +queue, the first tasks added are the first retrieved. In a :abbr:`LIFO (last-in, first-out)` queue, the most recently added entry is the first retrieved (operating like a stack). With a priority queue, the entries are kept sorted (using the :mod:`heapq` module) and the @@ -57,7 +57,7 @@ The :mod:`queue` module defines the following classes and exceptions: *maxsize* is less than or equal to zero, the queue size is infinite. The lowest valued entries are retrieved first (the lowest valued entry is the - one that would be returned by ``min(entries)``). A typical pattern for + one that would be returned by ``min(entries)``). A typical pattern for entries is a tuple in the form: ``(priority_number, data)``. If the *data* elements are not comparable, the data can be wrapped in a class @@ -127,8 +127,8 @@ provide the public methods described below. .. method:: Queue.put(item, block=True, timeout=None) - Put *item* into the queue. If optional args *block* is true and *timeout* is - ``None`` (the default), block if necessary until a free slot is available. If + Put *item* into the queue. If optional args *block* is true and *timeout* is + ``None`` (the default), block if necessary until a free slot is available. If *timeout* is a positive number, it blocks at most *timeout* seconds and raises the :exc:`Full` exception if no free slot was available within that time. Otherwise (*block* is false), put an item on the queue if a free slot is @@ -143,7 +143,7 @@ provide the public methods described below. .. method:: Queue.get(block=True, timeout=None) - Remove and return an item from the queue. If optional args *block* is true and + Remove and return an item from the queue. If optional args *block* is true and *timeout* is ``None`` (the default), block if necessary until an item is available. If *timeout* is a positive number, it blocks at most *timeout* seconds and raises the :exc:`Empty` exception if no item was available within that time. @@ -152,7 +152,7 @@ provide the public methods described below. Prior to 3.0 on POSIX systems, and for all versions on Windows, if *block* is true and *timeout* is ``None``, this operation goes into - an uninterruptible wait on an underlying lock. This means that no exceptions + an uninterruptible wait on an underlying lock. This means that no exceptions can occur, and in particular a SIGINT will not trigger a :exc:`KeyboardInterrupt`. @@ -184,7 +184,7 @@ fully processed by daemon consumer threads. The count of unfinished tasks goes up whenever an item is added to the queue. The count goes down whenever a consumer thread calls :meth:`task_done` to - indicate that the item was retrieved and all work on it is complete. When the + indicate that the item was retrieved and all work on it is complete. When the count of unfinished tasks drops to zero, :meth:`join` unblocks. @@ -227,7 +227,7 @@ SimpleQueue Objects .. method:: SimpleQueue.empty() - Return ``True`` if the queue is empty, ``False`` otherwise. If empty() + Return ``True`` if the queue is empty, ``False`` otherwise. If empty() returns ``False`` it doesn't guarantee that a subsequent call to get() will not block. From fcadc7e405141847ab10daf5cff16be880083a24 Mon Sep 17 00:00:00 2001 From: Jonathan Protzenko Date: Wed, 22 Feb 2023 13:18:43 -0800 Subject: [PATCH 163/247] gh-99108: Import MD5 and SHA1 from HACL* (#102089) Replaces our fallback non-OpenSSL MD5 and SHA1 implementations with those from HACL* as we've already done with SHA2. --- Makefile.pre.in | 14 +- ...3-02-17-10-42-48.gh-issue-99108.MKA8-f.rst | 2 + Modules/Setup | 4 +- Modules/Setup.stdlib.in | 4 +- Modules/_hacl/Hacl_Hash_MD5.c | 1472 +++++++++++++++++ Modules/_hacl/Hacl_Hash_MD5.h | 65 + Modules/_hacl/Hacl_Hash_SHA1.c | 508 ++++++ Modules/_hacl/Hacl_Hash_SHA1.h | 65 + Modules/_hacl/Hacl_Streaming_SHA2.c | 172 +- Modules/_hacl/Hacl_Streaming_SHA2.h | 67 +- Modules/_hacl/Hacl_Streaming_Types.h | 59 + .../include/krml/FStar_UInt128_Verified.h | 3 +- .../include/krml/FStar_UInt_8_16_32_64.h | 4 +- Modules/_hacl/include/krml/internal/target.h | 4 + Modules/_hacl/internal/Hacl_Hash_MD5.h | 61 + Modules/_hacl/internal/Hacl_Hash_SHA1.h | 61 + Modules/_hacl/python_hacl_namespaces.h | 17 + Modules/_hacl/refresh.sh | 9 +- Modules/md5module.c | 304 +--- Modules/sha1module.c | 282 +--- PCbuild/pythoncore.vcxproj | 2 + PCbuild/pythoncore.vcxproj.filters | 6 + configure | 4 +- configure.ac | 8 +- 24 files changed, 2498 insertions(+), 699 deletions(-) create mode 100644 Misc/NEWS.d/next/Security/2023-02-17-10-42-48.gh-issue-99108.MKA8-f.rst create mode 100644 Modules/_hacl/Hacl_Hash_MD5.c create mode 100644 Modules/_hacl/Hacl_Hash_MD5.h create mode 100644 Modules/_hacl/Hacl_Hash_SHA1.c create mode 100644 Modules/_hacl/Hacl_Hash_SHA1.h create mode 100644 Modules/_hacl/Hacl_Streaming_Types.h create mode 100644 Modules/_hacl/internal/Hacl_Hash_MD5.h create mode 100644 Modules/_hacl/internal/Hacl_Hash_SHA1.h diff --git a/Makefile.pre.in b/Makefile.pre.in index b28c6067a535b8..b12a1bc060af90 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -578,17 +578,21 @@ LIBEXPAT_HEADERS= \ LIBHACL_SHA2_OBJS= \ Modules/_hacl/Hacl_Streaming_SHA2.o -LIBHACL_SHA2_HEADERS= \ - Modules/_hacl/Hacl_Streaming_SHA2.h \ +LIBHACL_HEADERS= \ Modules/_hacl/include/krml/FStar_UInt128_Verified.h \ Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h \ Modules/_hacl/include/krml/fstar_uint128_struct_endianness.h \ Modules/_hacl/include/krml/internal/target.h \ Modules/_hacl/include/krml/lowstar_endianness.h \ Modules/_hacl/include/krml/types.h \ - Modules/_hacl/internal/Hacl_SHA2_Generic.h \ + Modules/_hacl/Hacl_Streaming_Types.h \ Modules/_hacl/python_hacl_namespaces.h +LIBHACL_SHA2_HEADERS= \ + Modules/_hacl/Hacl_Streaming_SHA2.h \ + Modules/_hacl/internal/Hacl_SHA2_Generic.h \ + $(LIBHACL_HEADERS) + ######################################################################### # Rules @@ -2635,8 +2639,8 @@ MODULE__DECIMAL_DEPS=$(srcdir)/Modules/_decimal/docstrings.h @LIBMPDEC_INTERNAL@ MODULE__ELEMENTTREE_DEPS=$(srcdir)/Modules/pyexpat.c @LIBEXPAT_INTERNAL@ MODULE__HASHLIB_DEPS=$(srcdir)/Modules/hashlib.h MODULE__IO_DEPS=$(srcdir)/Modules/_io/_iomodule.h -MODULE__MD5_DEPS=$(srcdir)/Modules/hashlib.h -MODULE__SHA1_DEPS=$(srcdir)/Modules/hashlib.h +MODULE__MD5_DEPS=$(srcdir)/Modules/hashlib.h $(LIBHACL_HEADERS) Modules/_hacl/Hacl_Hash_MD5.h Modules/_hacl/Hacl_Hash_MD5.c +MODULE__SHA1_DEPS=$(srcdir)/Modules/hashlib.h $(LIBHACL_HEADERS) Modules/_hacl/Hacl_Hash_SHA1.h Modules/_hacl/Hacl_Hash_SHA1.c MODULE__SHA2_DEPS=$(srcdir)/Modules/hashlib.h $(LIBHACL_SHA2_HEADERS) $(LIBHACL_SHA2_A) MODULE__SHA3_DEPS=$(srcdir)/Modules/_sha3/sha3.c $(srcdir)/Modules/_sha3/sha3.h $(srcdir)/Modules/hashlib.h MODULE__SOCKET_DEPS=$(srcdir)/Modules/socketmodule.h $(srcdir)/Modules/addrinfo.h $(srcdir)/Modules/getaddrinfo.c $(srcdir)/Modules/getnameinfo.c diff --git a/Misc/NEWS.d/next/Security/2023-02-17-10-42-48.gh-issue-99108.MKA8-f.rst b/Misc/NEWS.d/next/Security/2023-02-17-10-42-48.gh-issue-99108.MKA8-f.rst new file mode 100644 index 00000000000000..723d8a43a09f9e --- /dev/null +++ b/Misc/NEWS.d/next/Security/2023-02-17-10-42-48.gh-issue-99108.MKA8-f.rst @@ -0,0 +1,2 @@ +Replace builtin hashlib implementations of MD5 and SHA1 with verified ones +from the HACL* project. diff --git a/Modules/Setup b/Modules/Setup index 1d5183bc2df118..f9fa26cac9e233 100644 --- a/Modules/Setup +++ b/Modules/Setup @@ -163,8 +163,8 @@ PYTHONPATH=$(COREPYTHONPATH) # hashing builtins #_blake2 _blake2/blake2module.c _blake2/blake2b_impl.c _blake2/blake2s_impl.c -#_md5 md5module.c -#_sha1 sha1module.c +#_md5 md5module.c -I$(srcdir)/Modules/_hacl/include _hacl/libHacl_Hash_MD5.c +#_sha1 sha1module.c -I$(srcdir)/Modules/_hacl/include _hacl/libHacl_Hash_SHA1.c #_sha2 sha2module.c -I$(srcdir)/Modules/_hacl/include Modules/_hacl/libHacl_Streaming_SHA2.a #_sha3 _sha3/sha3module.c diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index 8f5e14a4e80e22..d33cd82239995f 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -77,8 +77,8 @@ @MODULE_READLINE_TRUE@readline readline.c # hashing builtins, can be disabled with --without-builtin-hashlib-hashes -@MODULE__MD5_TRUE@_md5 md5module.c -@MODULE__SHA1_TRUE@_sha1 sha1module.c +@MODULE__MD5_TRUE@_md5 md5module.c -I$(srcdir)/Modules/_hacl/include _hacl/Hacl_Hash_MD5.c -D_BSD_SOURCE -D_DEFAULT_SOURCE +@MODULE__SHA1_TRUE@_sha1 sha1module.c -I$(srcdir)/Modules/_hacl/include _hacl/Hacl_Hash_SHA1.c -D_BSD_SOURCE -D_DEFAULT_SOURCE @MODULE__SHA2_TRUE@_sha2 sha2module.c -I$(srcdir)/Modules/_hacl/include Modules/_hacl/libHacl_Streaming_SHA2.a @MODULE__SHA3_TRUE@_sha3 _sha3/sha3module.c @MODULE__BLAKE2_TRUE@_blake2 _blake2/blake2module.c _blake2/blake2b_impl.c _blake2/blake2s_impl.c diff --git a/Modules/_hacl/Hacl_Hash_MD5.c b/Modules/_hacl/Hacl_Hash_MD5.c new file mode 100644 index 00000000000000..2c613066d9f682 --- /dev/null +++ b/Modules/_hacl/Hacl_Hash_MD5.c @@ -0,0 +1,1472 @@ +/* MIT License + * + * Copyright (c) 2016-2022 INRIA, CMU and Microsoft Corporation + * Copyright (c) 2022-2023 HACL* Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +#include "internal/Hacl_Hash_MD5.h" + +static uint32_t +_h0[4U] = + { (uint32_t)0x67452301U, (uint32_t)0xefcdab89U, (uint32_t)0x98badcfeU, (uint32_t)0x10325476U }; + +static uint32_t +_t[64U] = + { + (uint32_t)0xd76aa478U, (uint32_t)0xe8c7b756U, (uint32_t)0x242070dbU, (uint32_t)0xc1bdceeeU, + (uint32_t)0xf57c0fafU, (uint32_t)0x4787c62aU, (uint32_t)0xa8304613U, (uint32_t)0xfd469501U, + (uint32_t)0x698098d8U, (uint32_t)0x8b44f7afU, (uint32_t)0xffff5bb1U, (uint32_t)0x895cd7beU, + (uint32_t)0x6b901122U, (uint32_t)0xfd987193U, (uint32_t)0xa679438eU, (uint32_t)0x49b40821U, + (uint32_t)0xf61e2562U, (uint32_t)0xc040b340U, (uint32_t)0x265e5a51U, (uint32_t)0xe9b6c7aaU, + (uint32_t)0xd62f105dU, (uint32_t)0x02441453U, (uint32_t)0xd8a1e681U, (uint32_t)0xe7d3fbc8U, + (uint32_t)0x21e1cde6U, (uint32_t)0xc33707d6U, (uint32_t)0xf4d50d87U, (uint32_t)0x455a14edU, + (uint32_t)0xa9e3e905U, (uint32_t)0xfcefa3f8U, (uint32_t)0x676f02d9U, (uint32_t)0x8d2a4c8aU, + (uint32_t)0xfffa3942U, (uint32_t)0x8771f681U, (uint32_t)0x6d9d6122U, (uint32_t)0xfde5380cU, + (uint32_t)0xa4beea44U, (uint32_t)0x4bdecfa9U, (uint32_t)0xf6bb4b60U, (uint32_t)0xbebfbc70U, + (uint32_t)0x289b7ec6U, (uint32_t)0xeaa127faU, (uint32_t)0xd4ef3085U, (uint32_t)0x4881d05U, + (uint32_t)0xd9d4d039U, (uint32_t)0xe6db99e5U, (uint32_t)0x1fa27cf8U, (uint32_t)0xc4ac5665U, + (uint32_t)0xf4292244U, (uint32_t)0x432aff97U, (uint32_t)0xab9423a7U, (uint32_t)0xfc93a039U, + (uint32_t)0x655b59c3U, (uint32_t)0x8f0ccc92U, (uint32_t)0xffeff47dU, (uint32_t)0x85845dd1U, + (uint32_t)0x6fa87e4fU, (uint32_t)0xfe2ce6e0U, (uint32_t)0xa3014314U, (uint32_t)0x4e0811a1U, + (uint32_t)0xf7537e82U, (uint32_t)0xbd3af235U, (uint32_t)0x2ad7d2bbU, (uint32_t)0xeb86d391U + }; + +void Hacl_Hash_Core_MD5_legacy_init(uint32_t *s) +{ + KRML_MAYBE_FOR4(i, (uint32_t)0U, (uint32_t)4U, (uint32_t)1U, s[i] = _h0[i];); +} + +static void legacy_update(uint32_t *abcd, uint8_t *x) +{ + uint32_t aa = abcd[0U]; + uint32_t bb = abcd[1U]; + uint32_t cc = abcd[2U]; + uint32_t dd = abcd[3U]; + uint32_t va = abcd[0U]; + uint32_t vb0 = abcd[1U]; + uint32_t vc0 = abcd[2U]; + uint32_t vd0 = abcd[3U]; + uint8_t *b0 = x; + uint32_t u = load32_le(b0); + uint32_t xk = u; + uint32_t ti0 = _t[0U]; + uint32_t + v = + vb0 + + + ((va + ((vb0 & vc0) | (~vb0 & vd0)) + xk + ti0) + << (uint32_t)7U + | (va + ((vb0 & vc0) | (~vb0 & vd0)) + xk + ti0) >> (uint32_t)25U); + abcd[0U] = v; + uint32_t va0 = abcd[3U]; + uint32_t vb1 = abcd[0U]; + uint32_t vc1 = abcd[1U]; + uint32_t vd1 = abcd[2U]; + uint8_t *b1 = x + (uint32_t)4U; + uint32_t u0 = load32_le(b1); + uint32_t xk0 = u0; + uint32_t ti1 = _t[1U]; + uint32_t + v0 = + vb1 + + + ((va0 + ((vb1 & vc1) | (~vb1 & vd1)) + xk0 + ti1) + << (uint32_t)12U + | (va0 + ((vb1 & vc1) | (~vb1 & vd1)) + xk0 + ti1) >> (uint32_t)20U); + abcd[3U] = v0; + uint32_t va1 = abcd[2U]; + uint32_t vb2 = abcd[3U]; + uint32_t vc2 = abcd[0U]; + uint32_t vd2 = abcd[1U]; + uint8_t *b2 = x + (uint32_t)8U; + uint32_t u1 = load32_le(b2); + uint32_t xk1 = u1; + uint32_t ti2 = _t[2U]; + uint32_t + v1 = + vb2 + + + ((va1 + ((vb2 & vc2) | (~vb2 & vd2)) + xk1 + ti2) + << (uint32_t)17U + | (va1 + ((vb2 & vc2) | (~vb2 & vd2)) + xk1 + ti2) >> (uint32_t)15U); + abcd[2U] = v1; + uint32_t va2 = abcd[1U]; + uint32_t vb3 = abcd[2U]; + uint32_t vc3 = abcd[3U]; + uint32_t vd3 = abcd[0U]; + uint8_t *b3 = x + (uint32_t)12U; + uint32_t u2 = load32_le(b3); + uint32_t xk2 = u2; + uint32_t ti3 = _t[3U]; + uint32_t + v2 = + vb3 + + + ((va2 + ((vb3 & vc3) | (~vb3 & vd3)) + xk2 + ti3) + << (uint32_t)22U + | (va2 + ((vb3 & vc3) | (~vb3 & vd3)) + xk2 + ti3) >> (uint32_t)10U); + abcd[1U] = v2; + uint32_t va3 = abcd[0U]; + uint32_t vb4 = abcd[1U]; + uint32_t vc4 = abcd[2U]; + uint32_t vd4 = abcd[3U]; + uint8_t *b4 = x + (uint32_t)16U; + uint32_t u3 = load32_le(b4); + uint32_t xk3 = u3; + uint32_t ti4 = _t[4U]; + uint32_t + v3 = + vb4 + + + ((va3 + ((vb4 & vc4) | (~vb4 & vd4)) + xk3 + ti4) + << (uint32_t)7U + | (va3 + ((vb4 & vc4) | (~vb4 & vd4)) + xk3 + ti4) >> (uint32_t)25U); + abcd[0U] = v3; + uint32_t va4 = abcd[3U]; + uint32_t vb5 = abcd[0U]; + uint32_t vc5 = abcd[1U]; + uint32_t vd5 = abcd[2U]; + uint8_t *b5 = x + (uint32_t)20U; + uint32_t u4 = load32_le(b5); + uint32_t xk4 = u4; + uint32_t ti5 = _t[5U]; + uint32_t + v4 = + vb5 + + + ((va4 + ((vb5 & vc5) | (~vb5 & vd5)) + xk4 + ti5) + << (uint32_t)12U + | (va4 + ((vb5 & vc5) | (~vb5 & vd5)) + xk4 + ti5) >> (uint32_t)20U); + abcd[3U] = v4; + uint32_t va5 = abcd[2U]; + uint32_t vb6 = abcd[3U]; + uint32_t vc6 = abcd[0U]; + uint32_t vd6 = abcd[1U]; + uint8_t *b6 = x + (uint32_t)24U; + uint32_t u5 = load32_le(b6); + uint32_t xk5 = u5; + uint32_t ti6 = _t[6U]; + uint32_t + v5 = + vb6 + + + ((va5 + ((vb6 & vc6) | (~vb6 & vd6)) + xk5 + ti6) + << (uint32_t)17U + | (va5 + ((vb6 & vc6) | (~vb6 & vd6)) + xk5 + ti6) >> (uint32_t)15U); + abcd[2U] = v5; + uint32_t va6 = abcd[1U]; + uint32_t vb7 = abcd[2U]; + uint32_t vc7 = abcd[3U]; + uint32_t vd7 = abcd[0U]; + uint8_t *b7 = x + (uint32_t)28U; + uint32_t u6 = load32_le(b7); + uint32_t xk6 = u6; + uint32_t ti7 = _t[7U]; + uint32_t + v6 = + vb7 + + + ((va6 + ((vb7 & vc7) | (~vb7 & vd7)) + xk6 + ti7) + << (uint32_t)22U + | (va6 + ((vb7 & vc7) | (~vb7 & vd7)) + xk6 + ti7) >> (uint32_t)10U); + abcd[1U] = v6; + uint32_t va7 = abcd[0U]; + uint32_t vb8 = abcd[1U]; + uint32_t vc8 = abcd[2U]; + uint32_t vd8 = abcd[3U]; + uint8_t *b8 = x + (uint32_t)32U; + uint32_t u7 = load32_le(b8); + uint32_t xk7 = u7; + uint32_t ti8 = _t[8U]; + uint32_t + v7 = + vb8 + + + ((va7 + ((vb8 & vc8) | (~vb8 & vd8)) + xk7 + ti8) + << (uint32_t)7U + | (va7 + ((vb8 & vc8) | (~vb8 & vd8)) + xk7 + ti8) >> (uint32_t)25U); + abcd[0U] = v7; + uint32_t va8 = abcd[3U]; + uint32_t vb9 = abcd[0U]; + uint32_t vc9 = abcd[1U]; + uint32_t vd9 = abcd[2U]; + uint8_t *b9 = x + (uint32_t)36U; + uint32_t u8 = load32_le(b9); + uint32_t xk8 = u8; + uint32_t ti9 = _t[9U]; + uint32_t + v8 = + vb9 + + + ((va8 + ((vb9 & vc9) | (~vb9 & vd9)) + xk8 + ti9) + << (uint32_t)12U + | (va8 + ((vb9 & vc9) | (~vb9 & vd9)) + xk8 + ti9) >> (uint32_t)20U); + abcd[3U] = v8; + uint32_t va9 = abcd[2U]; + uint32_t vb10 = abcd[3U]; + uint32_t vc10 = abcd[0U]; + uint32_t vd10 = abcd[1U]; + uint8_t *b10 = x + (uint32_t)40U; + uint32_t u9 = load32_le(b10); + uint32_t xk9 = u9; + uint32_t ti10 = _t[10U]; + uint32_t + v9 = + vb10 + + + ((va9 + ((vb10 & vc10) | (~vb10 & vd10)) + xk9 + ti10) + << (uint32_t)17U + | (va9 + ((vb10 & vc10) | (~vb10 & vd10)) + xk9 + ti10) >> (uint32_t)15U); + abcd[2U] = v9; + uint32_t va10 = abcd[1U]; + uint32_t vb11 = abcd[2U]; + uint32_t vc11 = abcd[3U]; + uint32_t vd11 = abcd[0U]; + uint8_t *b11 = x + (uint32_t)44U; + uint32_t u10 = load32_le(b11); + uint32_t xk10 = u10; + uint32_t ti11 = _t[11U]; + uint32_t + v10 = + vb11 + + + ((va10 + ((vb11 & vc11) | (~vb11 & vd11)) + xk10 + ti11) + << (uint32_t)22U + | (va10 + ((vb11 & vc11) | (~vb11 & vd11)) + xk10 + ti11) >> (uint32_t)10U); + abcd[1U] = v10; + uint32_t va11 = abcd[0U]; + uint32_t vb12 = abcd[1U]; + uint32_t vc12 = abcd[2U]; + uint32_t vd12 = abcd[3U]; + uint8_t *b12 = x + (uint32_t)48U; + uint32_t u11 = load32_le(b12); + uint32_t xk11 = u11; + uint32_t ti12 = _t[12U]; + uint32_t + v11 = + vb12 + + + ((va11 + ((vb12 & vc12) | (~vb12 & vd12)) + xk11 + ti12) + << (uint32_t)7U + | (va11 + ((vb12 & vc12) | (~vb12 & vd12)) + xk11 + ti12) >> (uint32_t)25U); + abcd[0U] = v11; + uint32_t va12 = abcd[3U]; + uint32_t vb13 = abcd[0U]; + uint32_t vc13 = abcd[1U]; + uint32_t vd13 = abcd[2U]; + uint8_t *b13 = x + (uint32_t)52U; + uint32_t u12 = load32_le(b13); + uint32_t xk12 = u12; + uint32_t ti13 = _t[13U]; + uint32_t + v12 = + vb13 + + + ((va12 + ((vb13 & vc13) | (~vb13 & vd13)) + xk12 + ti13) + << (uint32_t)12U + | (va12 + ((vb13 & vc13) | (~vb13 & vd13)) + xk12 + ti13) >> (uint32_t)20U); + abcd[3U] = v12; + uint32_t va13 = abcd[2U]; + uint32_t vb14 = abcd[3U]; + uint32_t vc14 = abcd[0U]; + uint32_t vd14 = abcd[1U]; + uint8_t *b14 = x + (uint32_t)56U; + uint32_t u13 = load32_le(b14); + uint32_t xk13 = u13; + uint32_t ti14 = _t[14U]; + uint32_t + v13 = + vb14 + + + ((va13 + ((vb14 & vc14) | (~vb14 & vd14)) + xk13 + ti14) + << (uint32_t)17U + | (va13 + ((vb14 & vc14) | (~vb14 & vd14)) + xk13 + ti14) >> (uint32_t)15U); + abcd[2U] = v13; + uint32_t va14 = abcd[1U]; + uint32_t vb15 = abcd[2U]; + uint32_t vc15 = abcd[3U]; + uint32_t vd15 = abcd[0U]; + uint8_t *b15 = x + (uint32_t)60U; + uint32_t u14 = load32_le(b15); + uint32_t xk14 = u14; + uint32_t ti15 = _t[15U]; + uint32_t + v14 = + vb15 + + + ((va14 + ((vb15 & vc15) | (~vb15 & vd15)) + xk14 + ti15) + << (uint32_t)22U + | (va14 + ((vb15 & vc15) | (~vb15 & vd15)) + xk14 + ti15) >> (uint32_t)10U); + abcd[1U] = v14; + uint32_t va15 = abcd[0U]; + uint32_t vb16 = abcd[1U]; + uint32_t vc16 = abcd[2U]; + uint32_t vd16 = abcd[3U]; + uint8_t *b16 = x + (uint32_t)4U; + uint32_t u15 = load32_le(b16); + uint32_t xk15 = u15; + uint32_t ti16 = _t[16U]; + uint32_t + v15 = + vb16 + + + ((va15 + ((vb16 & vd16) | (vc16 & ~vd16)) + xk15 + ti16) + << (uint32_t)5U + | (va15 + ((vb16 & vd16) | (vc16 & ~vd16)) + xk15 + ti16) >> (uint32_t)27U); + abcd[0U] = v15; + uint32_t va16 = abcd[3U]; + uint32_t vb17 = abcd[0U]; + uint32_t vc17 = abcd[1U]; + uint32_t vd17 = abcd[2U]; + uint8_t *b17 = x + (uint32_t)24U; + uint32_t u16 = load32_le(b17); + uint32_t xk16 = u16; + uint32_t ti17 = _t[17U]; + uint32_t + v16 = + vb17 + + + ((va16 + ((vb17 & vd17) | (vc17 & ~vd17)) + xk16 + ti17) + << (uint32_t)9U + | (va16 + ((vb17 & vd17) | (vc17 & ~vd17)) + xk16 + ti17) >> (uint32_t)23U); + abcd[3U] = v16; + uint32_t va17 = abcd[2U]; + uint32_t vb18 = abcd[3U]; + uint32_t vc18 = abcd[0U]; + uint32_t vd18 = abcd[1U]; + uint8_t *b18 = x + (uint32_t)44U; + uint32_t u17 = load32_le(b18); + uint32_t xk17 = u17; + uint32_t ti18 = _t[18U]; + uint32_t + v17 = + vb18 + + + ((va17 + ((vb18 & vd18) | (vc18 & ~vd18)) + xk17 + ti18) + << (uint32_t)14U + | (va17 + ((vb18 & vd18) | (vc18 & ~vd18)) + xk17 + ti18) >> (uint32_t)18U); + abcd[2U] = v17; + uint32_t va18 = abcd[1U]; + uint32_t vb19 = abcd[2U]; + uint32_t vc19 = abcd[3U]; + uint32_t vd19 = abcd[0U]; + uint8_t *b19 = x; + uint32_t u18 = load32_le(b19); + uint32_t xk18 = u18; + uint32_t ti19 = _t[19U]; + uint32_t + v18 = + vb19 + + + ((va18 + ((vb19 & vd19) | (vc19 & ~vd19)) + xk18 + ti19) + << (uint32_t)20U + | (va18 + ((vb19 & vd19) | (vc19 & ~vd19)) + xk18 + ti19) >> (uint32_t)12U); + abcd[1U] = v18; + uint32_t va19 = abcd[0U]; + uint32_t vb20 = abcd[1U]; + uint32_t vc20 = abcd[2U]; + uint32_t vd20 = abcd[3U]; + uint8_t *b20 = x + (uint32_t)20U; + uint32_t u19 = load32_le(b20); + uint32_t xk19 = u19; + uint32_t ti20 = _t[20U]; + uint32_t + v19 = + vb20 + + + ((va19 + ((vb20 & vd20) | (vc20 & ~vd20)) + xk19 + ti20) + << (uint32_t)5U + | (va19 + ((vb20 & vd20) | (vc20 & ~vd20)) + xk19 + ti20) >> (uint32_t)27U); + abcd[0U] = v19; + uint32_t va20 = abcd[3U]; + uint32_t vb21 = abcd[0U]; + uint32_t vc21 = abcd[1U]; + uint32_t vd21 = abcd[2U]; + uint8_t *b21 = x + (uint32_t)40U; + uint32_t u20 = load32_le(b21); + uint32_t xk20 = u20; + uint32_t ti21 = _t[21U]; + uint32_t + v20 = + vb21 + + + ((va20 + ((vb21 & vd21) | (vc21 & ~vd21)) + xk20 + ti21) + << (uint32_t)9U + | (va20 + ((vb21 & vd21) | (vc21 & ~vd21)) + xk20 + ti21) >> (uint32_t)23U); + abcd[3U] = v20; + uint32_t va21 = abcd[2U]; + uint32_t vb22 = abcd[3U]; + uint32_t vc22 = abcd[0U]; + uint32_t vd22 = abcd[1U]; + uint8_t *b22 = x + (uint32_t)60U; + uint32_t u21 = load32_le(b22); + uint32_t xk21 = u21; + uint32_t ti22 = _t[22U]; + uint32_t + v21 = + vb22 + + + ((va21 + ((vb22 & vd22) | (vc22 & ~vd22)) + xk21 + ti22) + << (uint32_t)14U + | (va21 + ((vb22 & vd22) | (vc22 & ~vd22)) + xk21 + ti22) >> (uint32_t)18U); + abcd[2U] = v21; + uint32_t va22 = abcd[1U]; + uint32_t vb23 = abcd[2U]; + uint32_t vc23 = abcd[3U]; + uint32_t vd23 = abcd[0U]; + uint8_t *b23 = x + (uint32_t)16U; + uint32_t u22 = load32_le(b23); + uint32_t xk22 = u22; + uint32_t ti23 = _t[23U]; + uint32_t + v22 = + vb23 + + + ((va22 + ((vb23 & vd23) | (vc23 & ~vd23)) + xk22 + ti23) + << (uint32_t)20U + | (va22 + ((vb23 & vd23) | (vc23 & ~vd23)) + xk22 + ti23) >> (uint32_t)12U); + abcd[1U] = v22; + uint32_t va23 = abcd[0U]; + uint32_t vb24 = abcd[1U]; + uint32_t vc24 = abcd[2U]; + uint32_t vd24 = abcd[3U]; + uint8_t *b24 = x + (uint32_t)36U; + uint32_t u23 = load32_le(b24); + uint32_t xk23 = u23; + uint32_t ti24 = _t[24U]; + uint32_t + v23 = + vb24 + + + ((va23 + ((vb24 & vd24) | (vc24 & ~vd24)) + xk23 + ti24) + << (uint32_t)5U + | (va23 + ((vb24 & vd24) | (vc24 & ~vd24)) + xk23 + ti24) >> (uint32_t)27U); + abcd[0U] = v23; + uint32_t va24 = abcd[3U]; + uint32_t vb25 = abcd[0U]; + uint32_t vc25 = abcd[1U]; + uint32_t vd25 = abcd[2U]; + uint8_t *b25 = x + (uint32_t)56U; + uint32_t u24 = load32_le(b25); + uint32_t xk24 = u24; + uint32_t ti25 = _t[25U]; + uint32_t + v24 = + vb25 + + + ((va24 + ((vb25 & vd25) | (vc25 & ~vd25)) + xk24 + ti25) + << (uint32_t)9U + | (va24 + ((vb25 & vd25) | (vc25 & ~vd25)) + xk24 + ti25) >> (uint32_t)23U); + abcd[3U] = v24; + uint32_t va25 = abcd[2U]; + uint32_t vb26 = abcd[3U]; + uint32_t vc26 = abcd[0U]; + uint32_t vd26 = abcd[1U]; + uint8_t *b26 = x + (uint32_t)12U; + uint32_t u25 = load32_le(b26); + uint32_t xk25 = u25; + uint32_t ti26 = _t[26U]; + uint32_t + v25 = + vb26 + + + ((va25 + ((vb26 & vd26) | (vc26 & ~vd26)) + xk25 + ti26) + << (uint32_t)14U + | (va25 + ((vb26 & vd26) | (vc26 & ~vd26)) + xk25 + ti26) >> (uint32_t)18U); + abcd[2U] = v25; + uint32_t va26 = abcd[1U]; + uint32_t vb27 = abcd[2U]; + uint32_t vc27 = abcd[3U]; + uint32_t vd27 = abcd[0U]; + uint8_t *b27 = x + (uint32_t)32U; + uint32_t u26 = load32_le(b27); + uint32_t xk26 = u26; + uint32_t ti27 = _t[27U]; + uint32_t + v26 = + vb27 + + + ((va26 + ((vb27 & vd27) | (vc27 & ~vd27)) + xk26 + ti27) + << (uint32_t)20U + | (va26 + ((vb27 & vd27) | (vc27 & ~vd27)) + xk26 + ti27) >> (uint32_t)12U); + abcd[1U] = v26; + uint32_t va27 = abcd[0U]; + uint32_t vb28 = abcd[1U]; + uint32_t vc28 = abcd[2U]; + uint32_t vd28 = abcd[3U]; + uint8_t *b28 = x + (uint32_t)52U; + uint32_t u27 = load32_le(b28); + uint32_t xk27 = u27; + uint32_t ti28 = _t[28U]; + uint32_t + v27 = + vb28 + + + ((va27 + ((vb28 & vd28) | (vc28 & ~vd28)) + xk27 + ti28) + << (uint32_t)5U + | (va27 + ((vb28 & vd28) | (vc28 & ~vd28)) + xk27 + ti28) >> (uint32_t)27U); + abcd[0U] = v27; + uint32_t va28 = abcd[3U]; + uint32_t vb29 = abcd[0U]; + uint32_t vc29 = abcd[1U]; + uint32_t vd29 = abcd[2U]; + uint8_t *b29 = x + (uint32_t)8U; + uint32_t u28 = load32_le(b29); + uint32_t xk28 = u28; + uint32_t ti29 = _t[29U]; + uint32_t + v28 = + vb29 + + + ((va28 + ((vb29 & vd29) | (vc29 & ~vd29)) + xk28 + ti29) + << (uint32_t)9U + | (va28 + ((vb29 & vd29) | (vc29 & ~vd29)) + xk28 + ti29) >> (uint32_t)23U); + abcd[3U] = v28; + uint32_t va29 = abcd[2U]; + uint32_t vb30 = abcd[3U]; + uint32_t vc30 = abcd[0U]; + uint32_t vd30 = abcd[1U]; + uint8_t *b30 = x + (uint32_t)28U; + uint32_t u29 = load32_le(b30); + uint32_t xk29 = u29; + uint32_t ti30 = _t[30U]; + uint32_t + v29 = + vb30 + + + ((va29 + ((vb30 & vd30) | (vc30 & ~vd30)) + xk29 + ti30) + << (uint32_t)14U + | (va29 + ((vb30 & vd30) | (vc30 & ~vd30)) + xk29 + ti30) >> (uint32_t)18U); + abcd[2U] = v29; + uint32_t va30 = abcd[1U]; + uint32_t vb31 = abcd[2U]; + uint32_t vc31 = abcd[3U]; + uint32_t vd31 = abcd[0U]; + uint8_t *b31 = x + (uint32_t)48U; + uint32_t u30 = load32_le(b31); + uint32_t xk30 = u30; + uint32_t ti31 = _t[31U]; + uint32_t + v30 = + vb31 + + + ((va30 + ((vb31 & vd31) | (vc31 & ~vd31)) + xk30 + ti31) + << (uint32_t)20U + | (va30 + ((vb31 & vd31) | (vc31 & ~vd31)) + xk30 + ti31) >> (uint32_t)12U); + abcd[1U] = v30; + uint32_t va31 = abcd[0U]; + uint32_t vb32 = abcd[1U]; + uint32_t vc32 = abcd[2U]; + uint32_t vd32 = abcd[3U]; + uint8_t *b32 = x + (uint32_t)20U; + uint32_t u31 = load32_le(b32); + uint32_t xk31 = u31; + uint32_t ti32 = _t[32U]; + uint32_t + v31 = + vb32 + + + ((va31 + (vb32 ^ (vc32 ^ vd32)) + xk31 + ti32) + << (uint32_t)4U + | (va31 + (vb32 ^ (vc32 ^ vd32)) + xk31 + ti32) >> (uint32_t)28U); + abcd[0U] = v31; + uint32_t va32 = abcd[3U]; + uint32_t vb33 = abcd[0U]; + uint32_t vc33 = abcd[1U]; + uint32_t vd33 = abcd[2U]; + uint8_t *b33 = x + (uint32_t)32U; + uint32_t u32 = load32_le(b33); + uint32_t xk32 = u32; + uint32_t ti33 = _t[33U]; + uint32_t + v32 = + vb33 + + + ((va32 + (vb33 ^ (vc33 ^ vd33)) + xk32 + ti33) + << (uint32_t)11U + | (va32 + (vb33 ^ (vc33 ^ vd33)) + xk32 + ti33) >> (uint32_t)21U); + abcd[3U] = v32; + uint32_t va33 = abcd[2U]; + uint32_t vb34 = abcd[3U]; + uint32_t vc34 = abcd[0U]; + uint32_t vd34 = abcd[1U]; + uint8_t *b34 = x + (uint32_t)44U; + uint32_t u33 = load32_le(b34); + uint32_t xk33 = u33; + uint32_t ti34 = _t[34U]; + uint32_t + v33 = + vb34 + + + ((va33 + (vb34 ^ (vc34 ^ vd34)) + xk33 + ti34) + << (uint32_t)16U + | (va33 + (vb34 ^ (vc34 ^ vd34)) + xk33 + ti34) >> (uint32_t)16U); + abcd[2U] = v33; + uint32_t va34 = abcd[1U]; + uint32_t vb35 = abcd[2U]; + uint32_t vc35 = abcd[3U]; + uint32_t vd35 = abcd[0U]; + uint8_t *b35 = x + (uint32_t)56U; + uint32_t u34 = load32_le(b35); + uint32_t xk34 = u34; + uint32_t ti35 = _t[35U]; + uint32_t + v34 = + vb35 + + + ((va34 + (vb35 ^ (vc35 ^ vd35)) + xk34 + ti35) + << (uint32_t)23U + | (va34 + (vb35 ^ (vc35 ^ vd35)) + xk34 + ti35) >> (uint32_t)9U); + abcd[1U] = v34; + uint32_t va35 = abcd[0U]; + uint32_t vb36 = abcd[1U]; + uint32_t vc36 = abcd[2U]; + uint32_t vd36 = abcd[3U]; + uint8_t *b36 = x + (uint32_t)4U; + uint32_t u35 = load32_le(b36); + uint32_t xk35 = u35; + uint32_t ti36 = _t[36U]; + uint32_t + v35 = + vb36 + + + ((va35 + (vb36 ^ (vc36 ^ vd36)) + xk35 + ti36) + << (uint32_t)4U + | (va35 + (vb36 ^ (vc36 ^ vd36)) + xk35 + ti36) >> (uint32_t)28U); + abcd[0U] = v35; + uint32_t va36 = abcd[3U]; + uint32_t vb37 = abcd[0U]; + uint32_t vc37 = abcd[1U]; + uint32_t vd37 = abcd[2U]; + uint8_t *b37 = x + (uint32_t)16U; + uint32_t u36 = load32_le(b37); + uint32_t xk36 = u36; + uint32_t ti37 = _t[37U]; + uint32_t + v36 = + vb37 + + + ((va36 + (vb37 ^ (vc37 ^ vd37)) + xk36 + ti37) + << (uint32_t)11U + | (va36 + (vb37 ^ (vc37 ^ vd37)) + xk36 + ti37) >> (uint32_t)21U); + abcd[3U] = v36; + uint32_t va37 = abcd[2U]; + uint32_t vb38 = abcd[3U]; + uint32_t vc38 = abcd[0U]; + uint32_t vd38 = abcd[1U]; + uint8_t *b38 = x + (uint32_t)28U; + uint32_t u37 = load32_le(b38); + uint32_t xk37 = u37; + uint32_t ti38 = _t[38U]; + uint32_t + v37 = + vb38 + + + ((va37 + (vb38 ^ (vc38 ^ vd38)) + xk37 + ti38) + << (uint32_t)16U + | (va37 + (vb38 ^ (vc38 ^ vd38)) + xk37 + ti38) >> (uint32_t)16U); + abcd[2U] = v37; + uint32_t va38 = abcd[1U]; + uint32_t vb39 = abcd[2U]; + uint32_t vc39 = abcd[3U]; + uint32_t vd39 = abcd[0U]; + uint8_t *b39 = x + (uint32_t)40U; + uint32_t u38 = load32_le(b39); + uint32_t xk38 = u38; + uint32_t ti39 = _t[39U]; + uint32_t + v38 = + vb39 + + + ((va38 + (vb39 ^ (vc39 ^ vd39)) + xk38 + ti39) + << (uint32_t)23U + | (va38 + (vb39 ^ (vc39 ^ vd39)) + xk38 + ti39) >> (uint32_t)9U); + abcd[1U] = v38; + uint32_t va39 = abcd[0U]; + uint32_t vb40 = abcd[1U]; + uint32_t vc40 = abcd[2U]; + uint32_t vd40 = abcd[3U]; + uint8_t *b40 = x + (uint32_t)52U; + uint32_t u39 = load32_le(b40); + uint32_t xk39 = u39; + uint32_t ti40 = _t[40U]; + uint32_t + v39 = + vb40 + + + ((va39 + (vb40 ^ (vc40 ^ vd40)) + xk39 + ti40) + << (uint32_t)4U + | (va39 + (vb40 ^ (vc40 ^ vd40)) + xk39 + ti40) >> (uint32_t)28U); + abcd[0U] = v39; + uint32_t va40 = abcd[3U]; + uint32_t vb41 = abcd[0U]; + uint32_t vc41 = abcd[1U]; + uint32_t vd41 = abcd[2U]; + uint8_t *b41 = x; + uint32_t u40 = load32_le(b41); + uint32_t xk40 = u40; + uint32_t ti41 = _t[41U]; + uint32_t + v40 = + vb41 + + + ((va40 + (vb41 ^ (vc41 ^ vd41)) + xk40 + ti41) + << (uint32_t)11U + | (va40 + (vb41 ^ (vc41 ^ vd41)) + xk40 + ti41) >> (uint32_t)21U); + abcd[3U] = v40; + uint32_t va41 = abcd[2U]; + uint32_t vb42 = abcd[3U]; + uint32_t vc42 = abcd[0U]; + uint32_t vd42 = abcd[1U]; + uint8_t *b42 = x + (uint32_t)12U; + uint32_t u41 = load32_le(b42); + uint32_t xk41 = u41; + uint32_t ti42 = _t[42U]; + uint32_t + v41 = + vb42 + + + ((va41 + (vb42 ^ (vc42 ^ vd42)) + xk41 + ti42) + << (uint32_t)16U + | (va41 + (vb42 ^ (vc42 ^ vd42)) + xk41 + ti42) >> (uint32_t)16U); + abcd[2U] = v41; + uint32_t va42 = abcd[1U]; + uint32_t vb43 = abcd[2U]; + uint32_t vc43 = abcd[3U]; + uint32_t vd43 = abcd[0U]; + uint8_t *b43 = x + (uint32_t)24U; + uint32_t u42 = load32_le(b43); + uint32_t xk42 = u42; + uint32_t ti43 = _t[43U]; + uint32_t + v42 = + vb43 + + + ((va42 + (vb43 ^ (vc43 ^ vd43)) + xk42 + ti43) + << (uint32_t)23U + | (va42 + (vb43 ^ (vc43 ^ vd43)) + xk42 + ti43) >> (uint32_t)9U); + abcd[1U] = v42; + uint32_t va43 = abcd[0U]; + uint32_t vb44 = abcd[1U]; + uint32_t vc44 = abcd[2U]; + uint32_t vd44 = abcd[3U]; + uint8_t *b44 = x + (uint32_t)36U; + uint32_t u43 = load32_le(b44); + uint32_t xk43 = u43; + uint32_t ti44 = _t[44U]; + uint32_t + v43 = + vb44 + + + ((va43 + (vb44 ^ (vc44 ^ vd44)) + xk43 + ti44) + << (uint32_t)4U + | (va43 + (vb44 ^ (vc44 ^ vd44)) + xk43 + ti44) >> (uint32_t)28U); + abcd[0U] = v43; + uint32_t va44 = abcd[3U]; + uint32_t vb45 = abcd[0U]; + uint32_t vc45 = abcd[1U]; + uint32_t vd45 = abcd[2U]; + uint8_t *b45 = x + (uint32_t)48U; + uint32_t u44 = load32_le(b45); + uint32_t xk44 = u44; + uint32_t ti45 = _t[45U]; + uint32_t + v44 = + vb45 + + + ((va44 + (vb45 ^ (vc45 ^ vd45)) + xk44 + ti45) + << (uint32_t)11U + | (va44 + (vb45 ^ (vc45 ^ vd45)) + xk44 + ti45) >> (uint32_t)21U); + abcd[3U] = v44; + uint32_t va45 = abcd[2U]; + uint32_t vb46 = abcd[3U]; + uint32_t vc46 = abcd[0U]; + uint32_t vd46 = abcd[1U]; + uint8_t *b46 = x + (uint32_t)60U; + uint32_t u45 = load32_le(b46); + uint32_t xk45 = u45; + uint32_t ti46 = _t[46U]; + uint32_t + v45 = + vb46 + + + ((va45 + (vb46 ^ (vc46 ^ vd46)) + xk45 + ti46) + << (uint32_t)16U + | (va45 + (vb46 ^ (vc46 ^ vd46)) + xk45 + ti46) >> (uint32_t)16U); + abcd[2U] = v45; + uint32_t va46 = abcd[1U]; + uint32_t vb47 = abcd[2U]; + uint32_t vc47 = abcd[3U]; + uint32_t vd47 = abcd[0U]; + uint8_t *b47 = x + (uint32_t)8U; + uint32_t u46 = load32_le(b47); + uint32_t xk46 = u46; + uint32_t ti47 = _t[47U]; + uint32_t + v46 = + vb47 + + + ((va46 + (vb47 ^ (vc47 ^ vd47)) + xk46 + ti47) + << (uint32_t)23U + | (va46 + (vb47 ^ (vc47 ^ vd47)) + xk46 + ti47) >> (uint32_t)9U); + abcd[1U] = v46; + uint32_t va47 = abcd[0U]; + uint32_t vb48 = abcd[1U]; + uint32_t vc48 = abcd[2U]; + uint32_t vd48 = abcd[3U]; + uint8_t *b48 = x; + uint32_t u47 = load32_le(b48); + uint32_t xk47 = u47; + uint32_t ti48 = _t[48U]; + uint32_t + v47 = + vb48 + + + ((va47 + (vc48 ^ (vb48 | ~vd48)) + xk47 + ti48) + << (uint32_t)6U + | (va47 + (vc48 ^ (vb48 | ~vd48)) + xk47 + ti48) >> (uint32_t)26U); + abcd[0U] = v47; + uint32_t va48 = abcd[3U]; + uint32_t vb49 = abcd[0U]; + uint32_t vc49 = abcd[1U]; + uint32_t vd49 = abcd[2U]; + uint8_t *b49 = x + (uint32_t)28U; + uint32_t u48 = load32_le(b49); + uint32_t xk48 = u48; + uint32_t ti49 = _t[49U]; + uint32_t + v48 = + vb49 + + + ((va48 + (vc49 ^ (vb49 | ~vd49)) + xk48 + ti49) + << (uint32_t)10U + | (va48 + (vc49 ^ (vb49 | ~vd49)) + xk48 + ti49) >> (uint32_t)22U); + abcd[3U] = v48; + uint32_t va49 = abcd[2U]; + uint32_t vb50 = abcd[3U]; + uint32_t vc50 = abcd[0U]; + uint32_t vd50 = abcd[1U]; + uint8_t *b50 = x + (uint32_t)56U; + uint32_t u49 = load32_le(b50); + uint32_t xk49 = u49; + uint32_t ti50 = _t[50U]; + uint32_t + v49 = + vb50 + + + ((va49 + (vc50 ^ (vb50 | ~vd50)) + xk49 + ti50) + << (uint32_t)15U + | (va49 + (vc50 ^ (vb50 | ~vd50)) + xk49 + ti50) >> (uint32_t)17U); + abcd[2U] = v49; + uint32_t va50 = abcd[1U]; + uint32_t vb51 = abcd[2U]; + uint32_t vc51 = abcd[3U]; + uint32_t vd51 = abcd[0U]; + uint8_t *b51 = x + (uint32_t)20U; + uint32_t u50 = load32_le(b51); + uint32_t xk50 = u50; + uint32_t ti51 = _t[51U]; + uint32_t + v50 = + vb51 + + + ((va50 + (vc51 ^ (vb51 | ~vd51)) + xk50 + ti51) + << (uint32_t)21U + | (va50 + (vc51 ^ (vb51 | ~vd51)) + xk50 + ti51) >> (uint32_t)11U); + abcd[1U] = v50; + uint32_t va51 = abcd[0U]; + uint32_t vb52 = abcd[1U]; + uint32_t vc52 = abcd[2U]; + uint32_t vd52 = abcd[3U]; + uint8_t *b52 = x + (uint32_t)48U; + uint32_t u51 = load32_le(b52); + uint32_t xk51 = u51; + uint32_t ti52 = _t[52U]; + uint32_t + v51 = + vb52 + + + ((va51 + (vc52 ^ (vb52 | ~vd52)) + xk51 + ti52) + << (uint32_t)6U + | (va51 + (vc52 ^ (vb52 | ~vd52)) + xk51 + ti52) >> (uint32_t)26U); + abcd[0U] = v51; + uint32_t va52 = abcd[3U]; + uint32_t vb53 = abcd[0U]; + uint32_t vc53 = abcd[1U]; + uint32_t vd53 = abcd[2U]; + uint8_t *b53 = x + (uint32_t)12U; + uint32_t u52 = load32_le(b53); + uint32_t xk52 = u52; + uint32_t ti53 = _t[53U]; + uint32_t + v52 = + vb53 + + + ((va52 + (vc53 ^ (vb53 | ~vd53)) + xk52 + ti53) + << (uint32_t)10U + | (va52 + (vc53 ^ (vb53 | ~vd53)) + xk52 + ti53) >> (uint32_t)22U); + abcd[3U] = v52; + uint32_t va53 = abcd[2U]; + uint32_t vb54 = abcd[3U]; + uint32_t vc54 = abcd[0U]; + uint32_t vd54 = abcd[1U]; + uint8_t *b54 = x + (uint32_t)40U; + uint32_t u53 = load32_le(b54); + uint32_t xk53 = u53; + uint32_t ti54 = _t[54U]; + uint32_t + v53 = + vb54 + + + ((va53 + (vc54 ^ (vb54 | ~vd54)) + xk53 + ti54) + << (uint32_t)15U + | (va53 + (vc54 ^ (vb54 | ~vd54)) + xk53 + ti54) >> (uint32_t)17U); + abcd[2U] = v53; + uint32_t va54 = abcd[1U]; + uint32_t vb55 = abcd[2U]; + uint32_t vc55 = abcd[3U]; + uint32_t vd55 = abcd[0U]; + uint8_t *b55 = x + (uint32_t)4U; + uint32_t u54 = load32_le(b55); + uint32_t xk54 = u54; + uint32_t ti55 = _t[55U]; + uint32_t + v54 = + vb55 + + + ((va54 + (vc55 ^ (vb55 | ~vd55)) + xk54 + ti55) + << (uint32_t)21U + | (va54 + (vc55 ^ (vb55 | ~vd55)) + xk54 + ti55) >> (uint32_t)11U); + abcd[1U] = v54; + uint32_t va55 = abcd[0U]; + uint32_t vb56 = abcd[1U]; + uint32_t vc56 = abcd[2U]; + uint32_t vd56 = abcd[3U]; + uint8_t *b56 = x + (uint32_t)32U; + uint32_t u55 = load32_le(b56); + uint32_t xk55 = u55; + uint32_t ti56 = _t[56U]; + uint32_t + v55 = + vb56 + + + ((va55 + (vc56 ^ (vb56 | ~vd56)) + xk55 + ti56) + << (uint32_t)6U + | (va55 + (vc56 ^ (vb56 | ~vd56)) + xk55 + ti56) >> (uint32_t)26U); + abcd[0U] = v55; + uint32_t va56 = abcd[3U]; + uint32_t vb57 = abcd[0U]; + uint32_t vc57 = abcd[1U]; + uint32_t vd57 = abcd[2U]; + uint8_t *b57 = x + (uint32_t)60U; + uint32_t u56 = load32_le(b57); + uint32_t xk56 = u56; + uint32_t ti57 = _t[57U]; + uint32_t + v56 = + vb57 + + + ((va56 + (vc57 ^ (vb57 | ~vd57)) + xk56 + ti57) + << (uint32_t)10U + | (va56 + (vc57 ^ (vb57 | ~vd57)) + xk56 + ti57) >> (uint32_t)22U); + abcd[3U] = v56; + uint32_t va57 = abcd[2U]; + uint32_t vb58 = abcd[3U]; + uint32_t vc58 = abcd[0U]; + uint32_t vd58 = abcd[1U]; + uint8_t *b58 = x + (uint32_t)24U; + uint32_t u57 = load32_le(b58); + uint32_t xk57 = u57; + uint32_t ti58 = _t[58U]; + uint32_t + v57 = + vb58 + + + ((va57 + (vc58 ^ (vb58 | ~vd58)) + xk57 + ti58) + << (uint32_t)15U + | (va57 + (vc58 ^ (vb58 | ~vd58)) + xk57 + ti58) >> (uint32_t)17U); + abcd[2U] = v57; + uint32_t va58 = abcd[1U]; + uint32_t vb59 = abcd[2U]; + uint32_t vc59 = abcd[3U]; + uint32_t vd59 = abcd[0U]; + uint8_t *b59 = x + (uint32_t)52U; + uint32_t u58 = load32_le(b59); + uint32_t xk58 = u58; + uint32_t ti59 = _t[59U]; + uint32_t + v58 = + vb59 + + + ((va58 + (vc59 ^ (vb59 | ~vd59)) + xk58 + ti59) + << (uint32_t)21U + | (va58 + (vc59 ^ (vb59 | ~vd59)) + xk58 + ti59) >> (uint32_t)11U); + abcd[1U] = v58; + uint32_t va59 = abcd[0U]; + uint32_t vb60 = abcd[1U]; + uint32_t vc60 = abcd[2U]; + uint32_t vd60 = abcd[3U]; + uint8_t *b60 = x + (uint32_t)16U; + uint32_t u59 = load32_le(b60); + uint32_t xk59 = u59; + uint32_t ti60 = _t[60U]; + uint32_t + v59 = + vb60 + + + ((va59 + (vc60 ^ (vb60 | ~vd60)) + xk59 + ti60) + << (uint32_t)6U + | (va59 + (vc60 ^ (vb60 | ~vd60)) + xk59 + ti60) >> (uint32_t)26U); + abcd[0U] = v59; + uint32_t va60 = abcd[3U]; + uint32_t vb61 = abcd[0U]; + uint32_t vc61 = abcd[1U]; + uint32_t vd61 = abcd[2U]; + uint8_t *b61 = x + (uint32_t)44U; + uint32_t u60 = load32_le(b61); + uint32_t xk60 = u60; + uint32_t ti61 = _t[61U]; + uint32_t + v60 = + vb61 + + + ((va60 + (vc61 ^ (vb61 | ~vd61)) + xk60 + ti61) + << (uint32_t)10U + | (va60 + (vc61 ^ (vb61 | ~vd61)) + xk60 + ti61) >> (uint32_t)22U); + abcd[3U] = v60; + uint32_t va61 = abcd[2U]; + uint32_t vb62 = abcd[3U]; + uint32_t vc62 = abcd[0U]; + uint32_t vd62 = abcd[1U]; + uint8_t *b62 = x + (uint32_t)8U; + uint32_t u61 = load32_le(b62); + uint32_t xk61 = u61; + uint32_t ti62 = _t[62U]; + uint32_t + v61 = + vb62 + + + ((va61 + (vc62 ^ (vb62 | ~vd62)) + xk61 + ti62) + << (uint32_t)15U + | (va61 + (vc62 ^ (vb62 | ~vd62)) + xk61 + ti62) >> (uint32_t)17U); + abcd[2U] = v61; + uint32_t va62 = abcd[1U]; + uint32_t vb = abcd[2U]; + uint32_t vc = abcd[3U]; + uint32_t vd = abcd[0U]; + uint8_t *b63 = x + (uint32_t)36U; + uint32_t u62 = load32_le(b63); + uint32_t xk62 = u62; + uint32_t ti = _t[63U]; + uint32_t + v62 = + vb + + + ((va62 + (vc ^ (vb | ~vd)) + xk62 + ti) + << (uint32_t)21U + | (va62 + (vc ^ (vb | ~vd)) + xk62 + ti) >> (uint32_t)11U); + abcd[1U] = v62; + uint32_t a = abcd[0U]; + uint32_t b = abcd[1U]; + uint32_t c = abcd[2U]; + uint32_t d = abcd[3U]; + abcd[0U] = a + aa; + abcd[1U] = b + bb; + abcd[2U] = c + cc; + abcd[3U] = d + dd; +} + +static void legacy_pad(uint64_t len, uint8_t *dst) +{ + uint8_t *dst1 = dst; + dst1[0U] = (uint8_t)0x80U; + uint8_t *dst2 = dst + (uint32_t)1U; + for + (uint32_t + i = (uint32_t)0U; + i + < ((uint32_t)128U - ((uint32_t)9U + (uint32_t)(len % (uint64_t)(uint32_t)64U))) % (uint32_t)64U; + i++) + { + dst2[i] = (uint8_t)0U; + } + uint8_t + *dst3 = + dst + + + (uint32_t)1U + + + ((uint32_t)128U - ((uint32_t)9U + (uint32_t)(len % (uint64_t)(uint32_t)64U))) + % (uint32_t)64U; + store64_le(dst3, len << (uint32_t)3U); +} + +void Hacl_Hash_Core_MD5_legacy_finish(uint32_t *s, uint8_t *dst) +{ + KRML_MAYBE_FOR4(i, + (uint32_t)0U, + (uint32_t)4U, + (uint32_t)1U, + store32_le(dst + i * (uint32_t)4U, s[i]);); +} + +void Hacl_Hash_MD5_legacy_update_multi(uint32_t *s, uint8_t *blocks, uint32_t n_blocks) +{ + for (uint32_t i = (uint32_t)0U; i < n_blocks; i++) + { + uint32_t sz = (uint32_t)64U; + uint8_t *block = blocks + sz * i; + legacy_update(s, block); + } +} + +void +Hacl_Hash_MD5_legacy_update_last( + uint32_t *s, + uint64_t prev_len, + uint8_t *input, + uint32_t input_len +) +{ + uint32_t blocks_n = input_len / (uint32_t)64U; + uint32_t blocks_len = blocks_n * (uint32_t)64U; + uint8_t *blocks = input; + uint32_t rest_len = input_len - blocks_len; + uint8_t *rest = input + blocks_len; + Hacl_Hash_MD5_legacy_update_multi(s, blocks, blocks_n); + uint64_t total_input_len = prev_len + (uint64_t)input_len; + uint32_t + pad_len = + (uint32_t)1U + + + ((uint32_t)128U - ((uint32_t)9U + (uint32_t)(total_input_len % (uint64_t)(uint32_t)64U))) + % (uint32_t)64U + + (uint32_t)8U; + uint32_t tmp_len = rest_len + pad_len; + uint8_t tmp_twoblocks[128U] = { 0U }; + uint8_t *tmp = tmp_twoblocks; + uint8_t *tmp_rest = tmp; + uint8_t *tmp_pad = tmp + rest_len; + memcpy(tmp_rest, rest, rest_len * sizeof (uint8_t)); + legacy_pad(total_input_len, tmp_pad); + Hacl_Hash_MD5_legacy_update_multi(s, tmp, tmp_len / (uint32_t)64U); +} + +void Hacl_Hash_MD5_legacy_hash(uint8_t *input, uint32_t input_len, uint8_t *dst) +{ + uint32_t + s[4U] = + { (uint32_t)0x67452301U, (uint32_t)0xefcdab89U, (uint32_t)0x98badcfeU, (uint32_t)0x10325476U }; + uint32_t blocks_n0 = input_len / (uint32_t)64U; + uint32_t blocks_n1; + if (input_len % (uint32_t)64U == (uint32_t)0U && blocks_n0 > (uint32_t)0U) + { + blocks_n1 = blocks_n0 - (uint32_t)1U; + } + else + { + blocks_n1 = blocks_n0; + } + uint32_t blocks_len0 = blocks_n1 * (uint32_t)64U; + uint8_t *blocks0 = input; + uint32_t rest_len0 = input_len - blocks_len0; + uint8_t *rest0 = input + blocks_len0; + uint32_t blocks_n = blocks_n1; + uint32_t blocks_len = blocks_len0; + uint8_t *blocks = blocks0; + uint32_t rest_len = rest_len0; + uint8_t *rest = rest0; + Hacl_Hash_MD5_legacy_update_multi(s, blocks, blocks_n); + Hacl_Hash_MD5_legacy_update_last(s, (uint64_t)blocks_len, rest, rest_len); + Hacl_Hash_Core_MD5_legacy_finish(s, dst); +} + +Hacl_Streaming_MD_state_32 *Hacl_Streaming_MD5_legacy_create_in(void) +{ + uint8_t *buf = (uint8_t *)KRML_HOST_CALLOC((uint32_t)64U, sizeof (uint8_t)); + uint32_t *block_state = (uint32_t *)KRML_HOST_CALLOC((uint32_t)4U, sizeof (uint32_t)); + Hacl_Streaming_MD_state_32 + s = { .block_state = block_state, .buf = buf, .total_len = (uint64_t)(uint32_t)0U }; + Hacl_Streaming_MD_state_32 + *p = (Hacl_Streaming_MD_state_32 *)KRML_HOST_MALLOC(sizeof (Hacl_Streaming_MD_state_32)); + p[0U] = s; + Hacl_Hash_Core_MD5_legacy_init(block_state); + return p; +} + +void Hacl_Streaming_MD5_legacy_init(Hacl_Streaming_MD_state_32 *s) +{ + Hacl_Streaming_MD_state_32 scrut = *s; + uint8_t *buf = scrut.buf; + uint32_t *block_state = scrut.block_state; + Hacl_Hash_Core_MD5_legacy_init(block_state); + Hacl_Streaming_MD_state_32 + tmp = { .block_state = block_state, .buf = buf, .total_len = (uint64_t)(uint32_t)0U }; + s[0U] = tmp; +} + +/** +0 = success, 1 = max length exceeded +*/ +uint32_t +Hacl_Streaming_MD5_legacy_update(Hacl_Streaming_MD_state_32 *p, uint8_t *data, uint32_t len) +{ + Hacl_Streaming_MD_state_32 s = *p; + uint64_t total_len = s.total_len; + if ((uint64_t)len > (uint64_t)2305843009213693951U - total_len) + { + return (uint32_t)1U; + } + uint32_t sz; + if (total_len % (uint64_t)(uint32_t)64U == (uint64_t)0U && total_len > (uint64_t)0U) + { + sz = (uint32_t)64U; + } + else + { + sz = (uint32_t)(total_len % (uint64_t)(uint32_t)64U); + } + if (len <= (uint32_t)64U - sz) + { + Hacl_Streaming_MD_state_32 s1 = *p; + uint32_t *block_state1 = s1.block_state; + uint8_t *buf = s1.buf; + uint64_t total_len1 = s1.total_len; + uint32_t sz1; + if (total_len1 % (uint64_t)(uint32_t)64U == (uint64_t)0U && total_len1 > (uint64_t)0U) + { + sz1 = (uint32_t)64U; + } + else + { + sz1 = (uint32_t)(total_len1 % (uint64_t)(uint32_t)64U); + } + uint8_t *buf2 = buf + sz1; + memcpy(buf2, data, len * sizeof (uint8_t)); + uint64_t total_len2 = total_len1 + (uint64_t)len; + *p + = + ( + (Hacl_Streaming_MD_state_32){ + .block_state = block_state1, + .buf = buf, + .total_len = total_len2 + } + ); + } + else if (sz == (uint32_t)0U) + { + Hacl_Streaming_MD_state_32 s1 = *p; + uint32_t *block_state1 = s1.block_state; + uint8_t *buf = s1.buf; + uint64_t total_len1 = s1.total_len; + uint32_t sz1; + if (total_len1 % (uint64_t)(uint32_t)64U == (uint64_t)0U && total_len1 > (uint64_t)0U) + { + sz1 = (uint32_t)64U; + } + else + { + sz1 = (uint32_t)(total_len1 % (uint64_t)(uint32_t)64U); + } + if (!(sz1 == (uint32_t)0U)) + { + Hacl_Hash_MD5_legacy_update_multi(block_state1, buf, (uint32_t)1U); + } + uint32_t ite; + if ((uint64_t)len % (uint64_t)(uint32_t)64U == (uint64_t)0U && (uint64_t)len > (uint64_t)0U) + { + ite = (uint32_t)64U; + } + else + { + ite = (uint32_t)((uint64_t)len % (uint64_t)(uint32_t)64U); + } + uint32_t n_blocks = (len - ite) / (uint32_t)64U; + uint32_t data1_len = n_blocks * (uint32_t)64U; + uint32_t data2_len = len - data1_len; + uint8_t *data1 = data; + uint8_t *data2 = data + data1_len; + Hacl_Hash_MD5_legacy_update_multi(block_state1, data1, data1_len / (uint32_t)64U); + uint8_t *dst = buf; + memcpy(dst, data2, data2_len * sizeof (uint8_t)); + *p + = + ( + (Hacl_Streaming_MD_state_32){ + .block_state = block_state1, + .buf = buf, + .total_len = total_len1 + (uint64_t)len + } + ); + } + else + { + uint32_t diff = (uint32_t)64U - sz; + uint8_t *data1 = data; + uint8_t *data2 = data + diff; + Hacl_Streaming_MD_state_32 s1 = *p; + uint32_t *block_state10 = s1.block_state; + uint8_t *buf0 = s1.buf; + uint64_t total_len10 = s1.total_len; + uint32_t sz10; + if (total_len10 % (uint64_t)(uint32_t)64U == (uint64_t)0U && total_len10 > (uint64_t)0U) + { + sz10 = (uint32_t)64U; + } + else + { + sz10 = (uint32_t)(total_len10 % (uint64_t)(uint32_t)64U); + } + uint8_t *buf2 = buf0 + sz10; + memcpy(buf2, data1, diff * sizeof (uint8_t)); + uint64_t total_len2 = total_len10 + (uint64_t)diff; + *p + = + ( + (Hacl_Streaming_MD_state_32){ + .block_state = block_state10, + .buf = buf0, + .total_len = total_len2 + } + ); + Hacl_Streaming_MD_state_32 s10 = *p; + uint32_t *block_state1 = s10.block_state; + uint8_t *buf = s10.buf; + uint64_t total_len1 = s10.total_len; + uint32_t sz1; + if (total_len1 % (uint64_t)(uint32_t)64U == (uint64_t)0U && total_len1 > (uint64_t)0U) + { + sz1 = (uint32_t)64U; + } + else + { + sz1 = (uint32_t)(total_len1 % (uint64_t)(uint32_t)64U); + } + if (!(sz1 == (uint32_t)0U)) + { + Hacl_Hash_MD5_legacy_update_multi(block_state1, buf, (uint32_t)1U); + } + uint32_t ite; + if + ( + (uint64_t)(len - diff) + % (uint64_t)(uint32_t)64U + == (uint64_t)0U + && (uint64_t)(len - diff) > (uint64_t)0U + ) + { + ite = (uint32_t)64U; + } + else + { + ite = (uint32_t)((uint64_t)(len - diff) % (uint64_t)(uint32_t)64U); + } + uint32_t n_blocks = (len - diff - ite) / (uint32_t)64U; + uint32_t data1_len = n_blocks * (uint32_t)64U; + uint32_t data2_len = len - diff - data1_len; + uint8_t *data11 = data2; + uint8_t *data21 = data2 + data1_len; + Hacl_Hash_MD5_legacy_update_multi(block_state1, data11, data1_len / (uint32_t)64U); + uint8_t *dst = buf; + memcpy(dst, data21, data2_len * sizeof (uint8_t)); + *p + = + ( + (Hacl_Streaming_MD_state_32){ + .block_state = block_state1, + .buf = buf, + .total_len = total_len1 + (uint64_t)(len - diff) + } + ); + } + return (uint32_t)0U; +} + +void Hacl_Streaming_MD5_legacy_finish(Hacl_Streaming_MD_state_32 *p, uint8_t *dst) +{ + Hacl_Streaming_MD_state_32 scrut = *p; + uint32_t *block_state = scrut.block_state; + uint8_t *buf_ = scrut.buf; + uint64_t total_len = scrut.total_len; + uint32_t r; + if (total_len % (uint64_t)(uint32_t)64U == (uint64_t)0U && total_len > (uint64_t)0U) + { + r = (uint32_t)64U; + } + else + { + r = (uint32_t)(total_len % (uint64_t)(uint32_t)64U); + } + uint8_t *buf_1 = buf_; + uint32_t tmp_block_state[4U] = { 0U }; + memcpy(tmp_block_state, block_state, (uint32_t)4U * sizeof (uint32_t)); + uint32_t ite; + if (r % (uint32_t)64U == (uint32_t)0U && r > (uint32_t)0U) + { + ite = (uint32_t)64U; + } + else + { + ite = r % (uint32_t)64U; + } + uint8_t *buf_last = buf_1 + r - ite; + uint8_t *buf_multi = buf_1; + Hacl_Hash_MD5_legacy_update_multi(tmp_block_state, buf_multi, (uint32_t)0U); + uint64_t prev_len_last = total_len - (uint64_t)r; + Hacl_Hash_MD5_legacy_update_last(tmp_block_state, prev_len_last, buf_last, r); + Hacl_Hash_Core_MD5_legacy_finish(tmp_block_state, dst); +} + +void Hacl_Streaming_MD5_legacy_free(Hacl_Streaming_MD_state_32 *s) +{ + Hacl_Streaming_MD_state_32 scrut = *s; + uint8_t *buf = scrut.buf; + uint32_t *block_state = scrut.block_state; + KRML_HOST_FREE(block_state); + KRML_HOST_FREE(buf); + KRML_HOST_FREE(s); +} + +Hacl_Streaming_MD_state_32 *Hacl_Streaming_MD5_legacy_copy(Hacl_Streaming_MD_state_32 *s0) +{ + Hacl_Streaming_MD_state_32 scrut = *s0; + uint32_t *block_state0 = scrut.block_state; + uint8_t *buf0 = scrut.buf; + uint64_t total_len0 = scrut.total_len; + uint8_t *buf = (uint8_t *)KRML_HOST_CALLOC((uint32_t)64U, sizeof (uint8_t)); + memcpy(buf, buf0, (uint32_t)64U * sizeof (uint8_t)); + uint32_t *block_state = (uint32_t *)KRML_HOST_CALLOC((uint32_t)4U, sizeof (uint32_t)); + memcpy(block_state, block_state0, (uint32_t)4U * sizeof (uint32_t)); + Hacl_Streaming_MD_state_32 + s = { .block_state = block_state, .buf = buf, .total_len = total_len0 }; + Hacl_Streaming_MD_state_32 + *p = (Hacl_Streaming_MD_state_32 *)KRML_HOST_MALLOC(sizeof (Hacl_Streaming_MD_state_32)); + p[0U] = s; + return p; +} + +void Hacl_Streaming_MD5_legacy_hash(uint8_t *input, uint32_t input_len, uint8_t *dst) +{ + Hacl_Hash_MD5_legacy_hash(input, input_len, dst); +} + diff --git a/Modules/_hacl/Hacl_Hash_MD5.h b/Modules/_hacl/Hacl_Hash_MD5.h new file mode 100644 index 00000000000000..015e3668751b75 --- /dev/null +++ b/Modules/_hacl/Hacl_Hash_MD5.h @@ -0,0 +1,65 @@ +/* MIT License + * + * Copyright (c) 2016-2022 INRIA, CMU and Microsoft Corporation + * Copyright (c) 2022-2023 HACL* Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +#ifndef __Hacl_Hash_MD5_H +#define __Hacl_Hash_MD5_H + +#if defined(__cplusplus) +extern "C" { +#endif + +#include +#include "krml/types.h" +#include "krml/lowstar_endianness.h" +#include "krml/internal/target.h" + +#include "Hacl_Streaming_Types.h" + +typedef Hacl_Streaming_MD_state_32 Hacl_Streaming_MD5_state; + +Hacl_Streaming_MD_state_32 *Hacl_Streaming_MD5_legacy_create_in(void); + +void Hacl_Streaming_MD5_legacy_init(Hacl_Streaming_MD_state_32 *s); + +/** +0 = success, 1 = max length exceeded +*/ +uint32_t +Hacl_Streaming_MD5_legacy_update(Hacl_Streaming_MD_state_32 *p, uint8_t *data, uint32_t len); + +void Hacl_Streaming_MD5_legacy_finish(Hacl_Streaming_MD_state_32 *p, uint8_t *dst); + +void Hacl_Streaming_MD5_legacy_free(Hacl_Streaming_MD_state_32 *s); + +Hacl_Streaming_MD_state_32 *Hacl_Streaming_MD5_legacy_copy(Hacl_Streaming_MD_state_32 *s0); + +void Hacl_Streaming_MD5_legacy_hash(uint8_t *input, uint32_t input_len, uint8_t *dst); + +#if defined(__cplusplus) +} +#endif + +#define __Hacl_Hash_MD5_H_DEFINED +#endif diff --git a/Modules/_hacl/Hacl_Hash_SHA1.c b/Modules/_hacl/Hacl_Hash_SHA1.c new file mode 100644 index 00000000000000..e155e338271c56 --- /dev/null +++ b/Modules/_hacl/Hacl_Hash_SHA1.c @@ -0,0 +1,508 @@ +/* MIT License + * + * Copyright (c) 2016-2022 INRIA, CMU and Microsoft Corporation + * Copyright (c) 2022-2023 HACL* Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +#include "internal/Hacl_Hash_SHA1.h" + +static uint32_t +_h0[5U] = + { + (uint32_t)0x67452301U, (uint32_t)0xefcdab89U, (uint32_t)0x98badcfeU, (uint32_t)0x10325476U, + (uint32_t)0xc3d2e1f0U + }; + +void Hacl_Hash_Core_SHA1_legacy_init(uint32_t *s) +{ + KRML_MAYBE_FOR5(i, (uint32_t)0U, (uint32_t)5U, (uint32_t)1U, s[i] = _h0[i];); +} + +static void legacy_update(uint32_t *h, uint8_t *l) +{ + uint32_t ha = h[0U]; + uint32_t hb = h[1U]; + uint32_t hc = h[2U]; + uint32_t hd = h[3U]; + uint32_t he = h[4U]; + uint32_t _w[80U] = { 0U }; + for (uint32_t i = (uint32_t)0U; i < (uint32_t)80U; i++) + { + uint32_t v; + if (i < (uint32_t)16U) + { + uint8_t *b = l + i * (uint32_t)4U; + uint32_t u = load32_be(b); + v = u; + } + else + { + uint32_t wmit3 = _w[i - (uint32_t)3U]; + uint32_t wmit8 = _w[i - (uint32_t)8U]; + uint32_t wmit14 = _w[i - (uint32_t)14U]; + uint32_t wmit16 = _w[i - (uint32_t)16U]; + v = + (wmit3 ^ (wmit8 ^ (wmit14 ^ wmit16))) + << (uint32_t)1U + | (wmit3 ^ (wmit8 ^ (wmit14 ^ wmit16))) >> (uint32_t)31U; + } + _w[i] = v; + } + for (uint32_t i = (uint32_t)0U; i < (uint32_t)80U; i++) + { + uint32_t _a = h[0U]; + uint32_t _b = h[1U]; + uint32_t _c = h[2U]; + uint32_t _d = h[3U]; + uint32_t _e = h[4U]; + uint32_t wmit = _w[i]; + uint32_t ite0; + if (i < (uint32_t)20U) + { + ite0 = (_b & _c) ^ (~_b & _d); + } + else if ((uint32_t)39U < i && i < (uint32_t)60U) + { + ite0 = (_b & _c) ^ ((_b & _d) ^ (_c & _d)); + } + else + { + ite0 = _b ^ (_c ^ _d); + } + uint32_t ite; + if (i < (uint32_t)20U) + { + ite = (uint32_t)0x5a827999U; + } + else if (i < (uint32_t)40U) + { + ite = (uint32_t)0x6ed9eba1U; + } + else if (i < (uint32_t)60U) + { + ite = (uint32_t)0x8f1bbcdcU; + } + else + { + ite = (uint32_t)0xca62c1d6U; + } + uint32_t _T = (_a << (uint32_t)5U | _a >> (uint32_t)27U) + ite0 + _e + ite + wmit; + h[0U] = _T; + h[1U] = _a; + h[2U] = _b << (uint32_t)30U | _b >> (uint32_t)2U; + h[3U] = _c; + h[4U] = _d; + } + for (uint32_t i = (uint32_t)0U; i < (uint32_t)80U; i++) + { + _w[i] = (uint32_t)0U; + } + uint32_t sta = h[0U]; + uint32_t stb = h[1U]; + uint32_t stc = h[2U]; + uint32_t std = h[3U]; + uint32_t ste = h[4U]; + h[0U] = sta + ha; + h[1U] = stb + hb; + h[2U] = stc + hc; + h[3U] = std + hd; + h[4U] = ste + he; +} + +static void legacy_pad(uint64_t len, uint8_t *dst) +{ + uint8_t *dst1 = dst; + dst1[0U] = (uint8_t)0x80U; + uint8_t *dst2 = dst + (uint32_t)1U; + for + (uint32_t + i = (uint32_t)0U; + i + < ((uint32_t)128U - ((uint32_t)9U + (uint32_t)(len % (uint64_t)(uint32_t)64U))) % (uint32_t)64U; + i++) + { + dst2[i] = (uint8_t)0U; + } + uint8_t + *dst3 = + dst + + + (uint32_t)1U + + + ((uint32_t)128U - ((uint32_t)9U + (uint32_t)(len % (uint64_t)(uint32_t)64U))) + % (uint32_t)64U; + store64_be(dst3, len << (uint32_t)3U); +} + +void Hacl_Hash_Core_SHA1_legacy_finish(uint32_t *s, uint8_t *dst) +{ + KRML_MAYBE_FOR5(i, + (uint32_t)0U, + (uint32_t)5U, + (uint32_t)1U, + store32_be(dst + i * (uint32_t)4U, s[i]);); +} + +void Hacl_Hash_SHA1_legacy_update_multi(uint32_t *s, uint8_t *blocks, uint32_t n_blocks) +{ + for (uint32_t i = (uint32_t)0U; i < n_blocks; i++) + { + uint32_t sz = (uint32_t)64U; + uint8_t *block = blocks + sz * i; + legacy_update(s, block); + } +} + +void +Hacl_Hash_SHA1_legacy_update_last( + uint32_t *s, + uint64_t prev_len, + uint8_t *input, + uint32_t input_len +) +{ + uint32_t blocks_n = input_len / (uint32_t)64U; + uint32_t blocks_len = blocks_n * (uint32_t)64U; + uint8_t *blocks = input; + uint32_t rest_len = input_len - blocks_len; + uint8_t *rest = input + blocks_len; + Hacl_Hash_SHA1_legacy_update_multi(s, blocks, blocks_n); + uint64_t total_input_len = prev_len + (uint64_t)input_len; + uint32_t + pad_len = + (uint32_t)1U + + + ((uint32_t)128U - ((uint32_t)9U + (uint32_t)(total_input_len % (uint64_t)(uint32_t)64U))) + % (uint32_t)64U + + (uint32_t)8U; + uint32_t tmp_len = rest_len + pad_len; + uint8_t tmp_twoblocks[128U] = { 0U }; + uint8_t *tmp = tmp_twoblocks; + uint8_t *tmp_rest = tmp; + uint8_t *tmp_pad = tmp + rest_len; + memcpy(tmp_rest, rest, rest_len * sizeof (uint8_t)); + legacy_pad(total_input_len, tmp_pad); + Hacl_Hash_SHA1_legacy_update_multi(s, tmp, tmp_len / (uint32_t)64U); +} + +void Hacl_Hash_SHA1_legacy_hash(uint8_t *input, uint32_t input_len, uint8_t *dst) +{ + uint32_t + s[5U] = + { + (uint32_t)0x67452301U, (uint32_t)0xefcdab89U, (uint32_t)0x98badcfeU, (uint32_t)0x10325476U, + (uint32_t)0xc3d2e1f0U + }; + uint32_t blocks_n0 = input_len / (uint32_t)64U; + uint32_t blocks_n1; + if (input_len % (uint32_t)64U == (uint32_t)0U && blocks_n0 > (uint32_t)0U) + { + blocks_n1 = blocks_n0 - (uint32_t)1U; + } + else + { + blocks_n1 = blocks_n0; + } + uint32_t blocks_len0 = blocks_n1 * (uint32_t)64U; + uint8_t *blocks0 = input; + uint32_t rest_len0 = input_len - blocks_len0; + uint8_t *rest0 = input + blocks_len0; + uint32_t blocks_n = blocks_n1; + uint32_t blocks_len = blocks_len0; + uint8_t *blocks = blocks0; + uint32_t rest_len = rest_len0; + uint8_t *rest = rest0; + Hacl_Hash_SHA1_legacy_update_multi(s, blocks, blocks_n); + Hacl_Hash_SHA1_legacy_update_last(s, (uint64_t)blocks_len, rest, rest_len); + Hacl_Hash_Core_SHA1_legacy_finish(s, dst); +} + +Hacl_Streaming_MD_state_32 *Hacl_Streaming_SHA1_legacy_create_in(void) +{ + uint8_t *buf = (uint8_t *)KRML_HOST_CALLOC((uint32_t)64U, sizeof (uint8_t)); + uint32_t *block_state = (uint32_t *)KRML_HOST_CALLOC((uint32_t)5U, sizeof (uint32_t)); + Hacl_Streaming_MD_state_32 + s = { .block_state = block_state, .buf = buf, .total_len = (uint64_t)(uint32_t)0U }; + Hacl_Streaming_MD_state_32 + *p = (Hacl_Streaming_MD_state_32 *)KRML_HOST_MALLOC(sizeof (Hacl_Streaming_MD_state_32)); + p[0U] = s; + Hacl_Hash_Core_SHA1_legacy_init(block_state); + return p; +} + +void Hacl_Streaming_SHA1_legacy_init(Hacl_Streaming_MD_state_32 *s) +{ + Hacl_Streaming_MD_state_32 scrut = *s; + uint8_t *buf = scrut.buf; + uint32_t *block_state = scrut.block_state; + Hacl_Hash_Core_SHA1_legacy_init(block_state); + Hacl_Streaming_MD_state_32 + tmp = { .block_state = block_state, .buf = buf, .total_len = (uint64_t)(uint32_t)0U }; + s[0U] = tmp; +} + +/** +0 = success, 1 = max length exceeded +*/ +uint32_t +Hacl_Streaming_SHA1_legacy_update(Hacl_Streaming_MD_state_32 *p, uint8_t *data, uint32_t len) +{ + Hacl_Streaming_MD_state_32 s = *p; + uint64_t total_len = s.total_len; + if ((uint64_t)len > (uint64_t)2305843009213693951U - total_len) + { + return (uint32_t)1U; + } + uint32_t sz; + if (total_len % (uint64_t)(uint32_t)64U == (uint64_t)0U && total_len > (uint64_t)0U) + { + sz = (uint32_t)64U; + } + else + { + sz = (uint32_t)(total_len % (uint64_t)(uint32_t)64U); + } + if (len <= (uint32_t)64U - sz) + { + Hacl_Streaming_MD_state_32 s1 = *p; + uint32_t *block_state1 = s1.block_state; + uint8_t *buf = s1.buf; + uint64_t total_len1 = s1.total_len; + uint32_t sz1; + if (total_len1 % (uint64_t)(uint32_t)64U == (uint64_t)0U && total_len1 > (uint64_t)0U) + { + sz1 = (uint32_t)64U; + } + else + { + sz1 = (uint32_t)(total_len1 % (uint64_t)(uint32_t)64U); + } + uint8_t *buf2 = buf + sz1; + memcpy(buf2, data, len * sizeof (uint8_t)); + uint64_t total_len2 = total_len1 + (uint64_t)len; + *p + = + ( + (Hacl_Streaming_MD_state_32){ + .block_state = block_state1, + .buf = buf, + .total_len = total_len2 + } + ); + } + else if (sz == (uint32_t)0U) + { + Hacl_Streaming_MD_state_32 s1 = *p; + uint32_t *block_state1 = s1.block_state; + uint8_t *buf = s1.buf; + uint64_t total_len1 = s1.total_len; + uint32_t sz1; + if (total_len1 % (uint64_t)(uint32_t)64U == (uint64_t)0U && total_len1 > (uint64_t)0U) + { + sz1 = (uint32_t)64U; + } + else + { + sz1 = (uint32_t)(total_len1 % (uint64_t)(uint32_t)64U); + } + if (!(sz1 == (uint32_t)0U)) + { + Hacl_Hash_SHA1_legacy_update_multi(block_state1, buf, (uint32_t)1U); + } + uint32_t ite; + if ((uint64_t)len % (uint64_t)(uint32_t)64U == (uint64_t)0U && (uint64_t)len > (uint64_t)0U) + { + ite = (uint32_t)64U; + } + else + { + ite = (uint32_t)((uint64_t)len % (uint64_t)(uint32_t)64U); + } + uint32_t n_blocks = (len - ite) / (uint32_t)64U; + uint32_t data1_len = n_blocks * (uint32_t)64U; + uint32_t data2_len = len - data1_len; + uint8_t *data1 = data; + uint8_t *data2 = data + data1_len; + Hacl_Hash_SHA1_legacy_update_multi(block_state1, data1, data1_len / (uint32_t)64U); + uint8_t *dst = buf; + memcpy(dst, data2, data2_len * sizeof (uint8_t)); + *p + = + ( + (Hacl_Streaming_MD_state_32){ + .block_state = block_state1, + .buf = buf, + .total_len = total_len1 + (uint64_t)len + } + ); + } + else + { + uint32_t diff = (uint32_t)64U - sz; + uint8_t *data1 = data; + uint8_t *data2 = data + diff; + Hacl_Streaming_MD_state_32 s1 = *p; + uint32_t *block_state10 = s1.block_state; + uint8_t *buf0 = s1.buf; + uint64_t total_len10 = s1.total_len; + uint32_t sz10; + if (total_len10 % (uint64_t)(uint32_t)64U == (uint64_t)0U && total_len10 > (uint64_t)0U) + { + sz10 = (uint32_t)64U; + } + else + { + sz10 = (uint32_t)(total_len10 % (uint64_t)(uint32_t)64U); + } + uint8_t *buf2 = buf0 + sz10; + memcpy(buf2, data1, diff * sizeof (uint8_t)); + uint64_t total_len2 = total_len10 + (uint64_t)diff; + *p + = + ( + (Hacl_Streaming_MD_state_32){ + .block_state = block_state10, + .buf = buf0, + .total_len = total_len2 + } + ); + Hacl_Streaming_MD_state_32 s10 = *p; + uint32_t *block_state1 = s10.block_state; + uint8_t *buf = s10.buf; + uint64_t total_len1 = s10.total_len; + uint32_t sz1; + if (total_len1 % (uint64_t)(uint32_t)64U == (uint64_t)0U && total_len1 > (uint64_t)0U) + { + sz1 = (uint32_t)64U; + } + else + { + sz1 = (uint32_t)(total_len1 % (uint64_t)(uint32_t)64U); + } + if (!(sz1 == (uint32_t)0U)) + { + Hacl_Hash_SHA1_legacy_update_multi(block_state1, buf, (uint32_t)1U); + } + uint32_t ite; + if + ( + (uint64_t)(len - diff) + % (uint64_t)(uint32_t)64U + == (uint64_t)0U + && (uint64_t)(len - diff) > (uint64_t)0U + ) + { + ite = (uint32_t)64U; + } + else + { + ite = (uint32_t)((uint64_t)(len - diff) % (uint64_t)(uint32_t)64U); + } + uint32_t n_blocks = (len - diff - ite) / (uint32_t)64U; + uint32_t data1_len = n_blocks * (uint32_t)64U; + uint32_t data2_len = len - diff - data1_len; + uint8_t *data11 = data2; + uint8_t *data21 = data2 + data1_len; + Hacl_Hash_SHA1_legacy_update_multi(block_state1, data11, data1_len / (uint32_t)64U); + uint8_t *dst = buf; + memcpy(dst, data21, data2_len * sizeof (uint8_t)); + *p + = + ( + (Hacl_Streaming_MD_state_32){ + .block_state = block_state1, + .buf = buf, + .total_len = total_len1 + (uint64_t)(len - diff) + } + ); + } + return (uint32_t)0U; +} + +void Hacl_Streaming_SHA1_legacy_finish(Hacl_Streaming_MD_state_32 *p, uint8_t *dst) +{ + Hacl_Streaming_MD_state_32 scrut = *p; + uint32_t *block_state = scrut.block_state; + uint8_t *buf_ = scrut.buf; + uint64_t total_len = scrut.total_len; + uint32_t r; + if (total_len % (uint64_t)(uint32_t)64U == (uint64_t)0U && total_len > (uint64_t)0U) + { + r = (uint32_t)64U; + } + else + { + r = (uint32_t)(total_len % (uint64_t)(uint32_t)64U); + } + uint8_t *buf_1 = buf_; + uint32_t tmp_block_state[5U] = { 0U }; + memcpy(tmp_block_state, block_state, (uint32_t)5U * sizeof (uint32_t)); + uint32_t ite; + if (r % (uint32_t)64U == (uint32_t)0U && r > (uint32_t)0U) + { + ite = (uint32_t)64U; + } + else + { + ite = r % (uint32_t)64U; + } + uint8_t *buf_last = buf_1 + r - ite; + uint8_t *buf_multi = buf_1; + Hacl_Hash_SHA1_legacy_update_multi(tmp_block_state, buf_multi, (uint32_t)0U); + uint64_t prev_len_last = total_len - (uint64_t)r; + Hacl_Hash_SHA1_legacy_update_last(tmp_block_state, prev_len_last, buf_last, r); + Hacl_Hash_Core_SHA1_legacy_finish(tmp_block_state, dst); +} + +void Hacl_Streaming_SHA1_legacy_free(Hacl_Streaming_MD_state_32 *s) +{ + Hacl_Streaming_MD_state_32 scrut = *s; + uint8_t *buf = scrut.buf; + uint32_t *block_state = scrut.block_state; + KRML_HOST_FREE(block_state); + KRML_HOST_FREE(buf); + KRML_HOST_FREE(s); +} + +Hacl_Streaming_MD_state_32 *Hacl_Streaming_SHA1_legacy_copy(Hacl_Streaming_MD_state_32 *s0) +{ + Hacl_Streaming_MD_state_32 scrut = *s0; + uint32_t *block_state0 = scrut.block_state; + uint8_t *buf0 = scrut.buf; + uint64_t total_len0 = scrut.total_len; + uint8_t *buf = (uint8_t *)KRML_HOST_CALLOC((uint32_t)64U, sizeof (uint8_t)); + memcpy(buf, buf0, (uint32_t)64U * sizeof (uint8_t)); + uint32_t *block_state = (uint32_t *)KRML_HOST_CALLOC((uint32_t)5U, sizeof (uint32_t)); + memcpy(block_state, block_state0, (uint32_t)5U * sizeof (uint32_t)); + Hacl_Streaming_MD_state_32 + s = { .block_state = block_state, .buf = buf, .total_len = total_len0 }; + Hacl_Streaming_MD_state_32 + *p = (Hacl_Streaming_MD_state_32 *)KRML_HOST_MALLOC(sizeof (Hacl_Streaming_MD_state_32)); + p[0U] = s; + return p; +} + +void Hacl_Streaming_SHA1_legacy_hash(uint8_t *input, uint32_t input_len, uint8_t *dst) +{ + Hacl_Hash_SHA1_legacy_hash(input, input_len, dst); +} + diff --git a/Modules/_hacl/Hacl_Hash_SHA1.h b/Modules/_hacl/Hacl_Hash_SHA1.h new file mode 100644 index 00000000000000..5e2ae8e713292d --- /dev/null +++ b/Modules/_hacl/Hacl_Hash_SHA1.h @@ -0,0 +1,65 @@ +/* MIT License + * + * Copyright (c) 2016-2022 INRIA, CMU and Microsoft Corporation + * Copyright (c) 2022-2023 HACL* Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +#ifndef __Hacl_Hash_SHA1_H +#define __Hacl_Hash_SHA1_H + +#if defined(__cplusplus) +extern "C" { +#endif + +#include +#include "krml/types.h" +#include "krml/lowstar_endianness.h" +#include "krml/internal/target.h" + +#include "Hacl_Streaming_Types.h" + +typedef Hacl_Streaming_MD_state_32 Hacl_Streaming_SHA1_state; + +Hacl_Streaming_MD_state_32 *Hacl_Streaming_SHA1_legacy_create_in(void); + +void Hacl_Streaming_SHA1_legacy_init(Hacl_Streaming_MD_state_32 *s); + +/** +0 = success, 1 = max length exceeded +*/ +uint32_t +Hacl_Streaming_SHA1_legacy_update(Hacl_Streaming_MD_state_32 *p, uint8_t *data, uint32_t len); + +void Hacl_Streaming_SHA1_legacy_finish(Hacl_Streaming_MD_state_32 *p, uint8_t *dst); + +void Hacl_Streaming_SHA1_legacy_free(Hacl_Streaming_MD_state_32 *s); + +Hacl_Streaming_MD_state_32 *Hacl_Streaming_SHA1_legacy_copy(Hacl_Streaming_MD_state_32 *s0); + +void Hacl_Streaming_SHA1_legacy_hash(uint8_t *input, uint32_t input_len, uint8_t *dst); + +#if defined(__cplusplus) +} +#endif + +#define __Hacl_Hash_SHA1_H_DEFINED +#endif diff --git a/Modules/_hacl/Hacl_Streaming_SHA2.c b/Modules/_hacl/Hacl_Streaming_SHA2.c index 8169c7a356731e..69c3be8cdf7fd1 100644 --- a/Modules/_hacl/Hacl_Streaming_SHA2.c +++ b/Modules/_hacl/Hacl_Streaming_SHA2.c @@ -477,17 +477,14 @@ static inline void sha384_finish(uint64_t *st, uint8_t *h) Allocate initial state for the SHA2_256 hash. The state is to be freed by calling `free_256`. */ -Hacl_Streaming_SHA2_state_sha2_224 *Hacl_Streaming_SHA2_create_in_256(void) +Hacl_Streaming_MD_state_32 *Hacl_Streaming_SHA2_create_in_256(void) { uint8_t *buf = (uint8_t *)KRML_HOST_CALLOC((uint32_t)64U, sizeof (uint8_t)); uint32_t *block_state = (uint32_t *)KRML_HOST_CALLOC((uint32_t)8U, sizeof (uint32_t)); - Hacl_Streaming_SHA2_state_sha2_224 + Hacl_Streaming_MD_state_32 s = { .block_state = block_state, .buf = buf, .total_len = (uint64_t)(uint32_t)0U }; - Hacl_Streaming_SHA2_state_sha2_224 - *p = - (Hacl_Streaming_SHA2_state_sha2_224 *)KRML_HOST_MALLOC(sizeof ( - Hacl_Streaming_SHA2_state_sha2_224 - )); + Hacl_Streaming_MD_state_32 + *p = (Hacl_Streaming_MD_state_32 *)KRML_HOST_MALLOC(sizeof (Hacl_Streaming_MD_state_32)); p[0U] = s; sha256_init(block_state); return p; @@ -499,10 +496,9 @@ The state is to be freed by calling `free_256`. Cloning the state this way is useful, for instance, if your control-flow diverges and you need to feed more (different) data into the hash in each branch. */ -Hacl_Streaming_SHA2_state_sha2_224 -*Hacl_Streaming_SHA2_copy_256(Hacl_Streaming_SHA2_state_sha2_224 *s0) +Hacl_Streaming_MD_state_32 *Hacl_Streaming_SHA2_copy_256(Hacl_Streaming_MD_state_32 *s0) { - Hacl_Streaming_SHA2_state_sha2_224 scrut = *s0; + Hacl_Streaming_MD_state_32 scrut = *s0; uint32_t *block_state0 = scrut.block_state; uint8_t *buf0 = scrut.buf; uint64_t total_len0 = scrut.total_len; @@ -510,13 +506,10 @@ Hacl_Streaming_SHA2_state_sha2_224 memcpy(buf, buf0, (uint32_t)64U * sizeof (uint8_t)); uint32_t *block_state = (uint32_t *)KRML_HOST_CALLOC((uint32_t)8U, sizeof (uint32_t)); memcpy(block_state, block_state0, (uint32_t)8U * sizeof (uint32_t)); - Hacl_Streaming_SHA2_state_sha2_224 + Hacl_Streaming_MD_state_32 s = { .block_state = block_state, .buf = buf, .total_len = total_len0 }; - Hacl_Streaming_SHA2_state_sha2_224 - *p = - (Hacl_Streaming_SHA2_state_sha2_224 *)KRML_HOST_MALLOC(sizeof ( - Hacl_Streaming_SHA2_state_sha2_224 - )); + Hacl_Streaming_MD_state_32 + *p = (Hacl_Streaming_MD_state_32 *)KRML_HOST_MALLOC(sizeof (Hacl_Streaming_MD_state_32)); p[0U] = s; return p; } @@ -524,21 +517,21 @@ Hacl_Streaming_SHA2_state_sha2_224 /** Reset an existing state to the initial hash state with empty data. */ -void Hacl_Streaming_SHA2_init_256(Hacl_Streaming_SHA2_state_sha2_224 *s) +void Hacl_Streaming_SHA2_init_256(Hacl_Streaming_MD_state_32 *s) { - Hacl_Streaming_SHA2_state_sha2_224 scrut = *s; + Hacl_Streaming_MD_state_32 scrut = *s; uint8_t *buf = scrut.buf; uint32_t *block_state = scrut.block_state; sha256_init(block_state); - Hacl_Streaming_SHA2_state_sha2_224 + Hacl_Streaming_MD_state_32 tmp = { .block_state = block_state, .buf = buf, .total_len = (uint64_t)(uint32_t)0U }; s[0U] = tmp; } static inline uint32_t -update_224_256(Hacl_Streaming_SHA2_state_sha2_224 *p, uint8_t *data, uint32_t len) +update_224_256(Hacl_Streaming_MD_state_32 *p, uint8_t *data, uint32_t len) { - Hacl_Streaming_SHA2_state_sha2_224 s = *p; + Hacl_Streaming_MD_state_32 s = *p; uint64_t total_len = s.total_len; if ((uint64_t)len > (uint64_t)2305843009213693951U - total_len) { @@ -555,7 +548,7 @@ update_224_256(Hacl_Streaming_SHA2_state_sha2_224 *p, uint8_t *data, uint32_t le } if (len <= (uint32_t)64U - sz) { - Hacl_Streaming_SHA2_state_sha2_224 s1 = *p; + Hacl_Streaming_MD_state_32 s1 = *p; uint32_t *block_state1 = s1.block_state; uint8_t *buf = s1.buf; uint64_t total_len1 = s1.total_len; @@ -574,7 +567,7 @@ update_224_256(Hacl_Streaming_SHA2_state_sha2_224 *p, uint8_t *data, uint32_t le *p = ( - (Hacl_Streaming_SHA2_state_sha2_224){ + (Hacl_Streaming_MD_state_32){ .block_state = block_state1, .buf = buf, .total_len = total_len2 @@ -583,7 +576,7 @@ update_224_256(Hacl_Streaming_SHA2_state_sha2_224 *p, uint8_t *data, uint32_t le } else if (sz == (uint32_t)0U) { - Hacl_Streaming_SHA2_state_sha2_224 s1 = *p; + Hacl_Streaming_MD_state_32 s1 = *p; uint32_t *block_state1 = s1.block_state; uint8_t *buf = s1.buf; uint64_t total_len1 = s1.total_len; @@ -620,7 +613,7 @@ update_224_256(Hacl_Streaming_SHA2_state_sha2_224 *p, uint8_t *data, uint32_t le *p = ( - (Hacl_Streaming_SHA2_state_sha2_224){ + (Hacl_Streaming_MD_state_32){ .block_state = block_state1, .buf = buf, .total_len = total_len1 + (uint64_t)len @@ -632,7 +625,7 @@ update_224_256(Hacl_Streaming_SHA2_state_sha2_224 *p, uint8_t *data, uint32_t le uint32_t diff = (uint32_t)64U - sz; uint8_t *data1 = data; uint8_t *data2 = data + diff; - Hacl_Streaming_SHA2_state_sha2_224 s1 = *p; + Hacl_Streaming_MD_state_32 s1 = *p; uint32_t *block_state10 = s1.block_state; uint8_t *buf0 = s1.buf; uint64_t total_len10 = s1.total_len; @@ -651,13 +644,13 @@ update_224_256(Hacl_Streaming_SHA2_state_sha2_224 *p, uint8_t *data, uint32_t le *p = ( - (Hacl_Streaming_SHA2_state_sha2_224){ + (Hacl_Streaming_MD_state_32){ .block_state = block_state10, .buf = buf0, .total_len = total_len2 } ); - Hacl_Streaming_SHA2_state_sha2_224 s10 = *p; + Hacl_Streaming_MD_state_32 s10 = *p; uint32_t *block_state1 = s10.block_state; uint8_t *buf = s10.buf; uint64_t total_len1 = s10.total_len; @@ -700,7 +693,7 @@ update_224_256(Hacl_Streaming_SHA2_state_sha2_224 *p, uint8_t *data, uint32_t le *p = ( - (Hacl_Streaming_SHA2_state_sha2_224){ + (Hacl_Streaming_MD_state_32){ .block_state = block_state1, .buf = buf, .total_len = total_len1 + (uint64_t)(len - diff) @@ -719,7 +712,7 @@ This function is identical to the update function for SHA2_224. */ uint32_t Hacl_Streaming_SHA2_update_256( - Hacl_Streaming_SHA2_state_sha2_224 *p, + Hacl_Streaming_MD_state_32 *p, uint8_t *input, uint32_t input_len ) @@ -733,9 +726,9 @@ valid after a call to `finish_256`, meaning the user may feed more data into the hash via `update_256`. (The finish_256 function operates on an internal copy of the state and therefore does not invalidate the client-held state `p`.) */ -void Hacl_Streaming_SHA2_finish_256(Hacl_Streaming_SHA2_state_sha2_224 *p, uint8_t *dst) +void Hacl_Streaming_SHA2_finish_256(Hacl_Streaming_MD_state_32 *p, uint8_t *dst) { - Hacl_Streaming_SHA2_state_sha2_224 scrut = *p; + Hacl_Streaming_MD_state_32 scrut = *p; uint32_t *block_state = scrut.block_state; uint8_t *buf_ = scrut.buf; uint64_t total_len = scrut.total_len; @@ -773,9 +766,9 @@ Free a state allocated with `create_in_256`. This function is identical to the free function for SHA2_224. */ -void Hacl_Streaming_SHA2_free_256(Hacl_Streaming_SHA2_state_sha2_224 *s) +void Hacl_Streaming_SHA2_free_256(Hacl_Streaming_MD_state_32 *s) { - Hacl_Streaming_SHA2_state_sha2_224 scrut = *s; + Hacl_Streaming_MD_state_32 scrut = *s; uint8_t *buf = scrut.buf; uint32_t *block_state = scrut.block_state; KRML_HOST_FREE(block_state); @@ -802,36 +795,33 @@ void Hacl_Streaming_SHA2_sha256(uint8_t *input, uint32_t input_len, uint8_t *dst sha256_finish(st, rb); } -Hacl_Streaming_SHA2_state_sha2_224 *Hacl_Streaming_SHA2_create_in_224(void) +Hacl_Streaming_MD_state_32 *Hacl_Streaming_SHA2_create_in_224(void) { uint8_t *buf = (uint8_t *)KRML_HOST_CALLOC((uint32_t)64U, sizeof (uint8_t)); uint32_t *block_state = (uint32_t *)KRML_HOST_CALLOC((uint32_t)8U, sizeof (uint32_t)); - Hacl_Streaming_SHA2_state_sha2_224 + Hacl_Streaming_MD_state_32 s = { .block_state = block_state, .buf = buf, .total_len = (uint64_t)(uint32_t)0U }; - Hacl_Streaming_SHA2_state_sha2_224 - *p = - (Hacl_Streaming_SHA2_state_sha2_224 *)KRML_HOST_MALLOC(sizeof ( - Hacl_Streaming_SHA2_state_sha2_224 - )); + Hacl_Streaming_MD_state_32 + *p = (Hacl_Streaming_MD_state_32 *)KRML_HOST_MALLOC(sizeof (Hacl_Streaming_MD_state_32)); p[0U] = s; sha224_init(block_state); return p; } -void Hacl_Streaming_SHA2_init_224(Hacl_Streaming_SHA2_state_sha2_224 *s) +void Hacl_Streaming_SHA2_init_224(Hacl_Streaming_MD_state_32 *s) { - Hacl_Streaming_SHA2_state_sha2_224 scrut = *s; + Hacl_Streaming_MD_state_32 scrut = *s; uint8_t *buf = scrut.buf; uint32_t *block_state = scrut.block_state; sha224_init(block_state); - Hacl_Streaming_SHA2_state_sha2_224 + Hacl_Streaming_MD_state_32 tmp = { .block_state = block_state, .buf = buf, .total_len = (uint64_t)(uint32_t)0U }; s[0U] = tmp; } uint32_t Hacl_Streaming_SHA2_update_224( - Hacl_Streaming_SHA2_state_sha2_224 *p, + Hacl_Streaming_MD_state_32 *p, uint8_t *input, uint32_t input_len ) @@ -844,9 +834,9 @@ Write the resulting hash into `dst`, an array of 28 bytes. The state remains valid after a call to `finish_224`, meaning the user may feed more data into the hash via `update_224`. */ -void Hacl_Streaming_SHA2_finish_224(Hacl_Streaming_SHA2_state_sha2_224 *p, uint8_t *dst) +void Hacl_Streaming_SHA2_finish_224(Hacl_Streaming_MD_state_32 *p, uint8_t *dst) { - Hacl_Streaming_SHA2_state_sha2_224 scrut = *p; + Hacl_Streaming_MD_state_32 scrut = *p; uint32_t *block_state = scrut.block_state; uint8_t *buf_ = scrut.buf; uint64_t total_len = scrut.total_len; @@ -879,7 +869,7 @@ void Hacl_Streaming_SHA2_finish_224(Hacl_Streaming_SHA2_state_sha2_224 *p, uint8 sha224_finish(tmp_block_state, dst); } -void Hacl_Streaming_SHA2_free_224(Hacl_Streaming_SHA2_state_sha2_224 *p) +void Hacl_Streaming_SHA2_free_224(Hacl_Streaming_MD_state_32 *p) { Hacl_Streaming_SHA2_free_256(p); } @@ -903,17 +893,14 @@ void Hacl_Streaming_SHA2_sha224(uint8_t *input, uint32_t input_len, uint8_t *dst sha224_finish(st, rb); } -Hacl_Streaming_SHA2_state_sha2_384 *Hacl_Streaming_SHA2_create_in_512(void) +Hacl_Streaming_MD_state_64 *Hacl_Streaming_SHA2_create_in_512(void) { uint8_t *buf = (uint8_t *)KRML_HOST_CALLOC((uint32_t)128U, sizeof (uint8_t)); uint64_t *block_state = (uint64_t *)KRML_HOST_CALLOC((uint32_t)8U, sizeof (uint64_t)); - Hacl_Streaming_SHA2_state_sha2_384 + Hacl_Streaming_MD_state_64 s = { .block_state = block_state, .buf = buf, .total_len = (uint64_t)(uint32_t)0U }; - Hacl_Streaming_SHA2_state_sha2_384 - *p = - (Hacl_Streaming_SHA2_state_sha2_384 *)KRML_HOST_MALLOC(sizeof ( - Hacl_Streaming_SHA2_state_sha2_384 - )); + Hacl_Streaming_MD_state_64 + *p = (Hacl_Streaming_MD_state_64 *)KRML_HOST_MALLOC(sizeof (Hacl_Streaming_MD_state_64)); p[0U] = s; Hacl_SHA2_Scalar32_sha512_init(block_state); return p; @@ -925,10 +912,9 @@ The state is to be freed by calling `free_512`. Cloning the state this way is useful, for instance, if your control-flow diverges and you need to feed more (different) data into the hash in each branch. */ -Hacl_Streaming_SHA2_state_sha2_384 -*Hacl_Streaming_SHA2_copy_512(Hacl_Streaming_SHA2_state_sha2_384 *s0) +Hacl_Streaming_MD_state_64 *Hacl_Streaming_SHA2_copy_512(Hacl_Streaming_MD_state_64 *s0) { - Hacl_Streaming_SHA2_state_sha2_384 scrut = *s0; + Hacl_Streaming_MD_state_64 scrut = *s0; uint64_t *block_state0 = scrut.block_state; uint8_t *buf0 = scrut.buf; uint64_t total_len0 = scrut.total_len; @@ -936,32 +922,29 @@ Hacl_Streaming_SHA2_state_sha2_384 memcpy(buf, buf0, (uint32_t)128U * sizeof (uint8_t)); uint64_t *block_state = (uint64_t *)KRML_HOST_CALLOC((uint32_t)8U, sizeof (uint64_t)); memcpy(block_state, block_state0, (uint32_t)8U * sizeof (uint64_t)); - Hacl_Streaming_SHA2_state_sha2_384 + Hacl_Streaming_MD_state_64 s = { .block_state = block_state, .buf = buf, .total_len = total_len0 }; - Hacl_Streaming_SHA2_state_sha2_384 - *p = - (Hacl_Streaming_SHA2_state_sha2_384 *)KRML_HOST_MALLOC(sizeof ( - Hacl_Streaming_SHA2_state_sha2_384 - )); + Hacl_Streaming_MD_state_64 + *p = (Hacl_Streaming_MD_state_64 *)KRML_HOST_MALLOC(sizeof (Hacl_Streaming_MD_state_64)); p[0U] = s; return p; } -void Hacl_Streaming_SHA2_init_512(Hacl_Streaming_SHA2_state_sha2_384 *s) +void Hacl_Streaming_SHA2_init_512(Hacl_Streaming_MD_state_64 *s) { - Hacl_Streaming_SHA2_state_sha2_384 scrut = *s; + Hacl_Streaming_MD_state_64 scrut = *s; uint8_t *buf = scrut.buf; uint64_t *block_state = scrut.block_state; Hacl_SHA2_Scalar32_sha512_init(block_state); - Hacl_Streaming_SHA2_state_sha2_384 + Hacl_Streaming_MD_state_64 tmp = { .block_state = block_state, .buf = buf, .total_len = (uint64_t)(uint32_t)0U }; s[0U] = tmp; } static inline uint32_t -update_384_512(Hacl_Streaming_SHA2_state_sha2_384 *p, uint8_t *data, uint32_t len) +update_384_512(Hacl_Streaming_MD_state_64 *p, uint8_t *data, uint32_t len) { - Hacl_Streaming_SHA2_state_sha2_384 s = *p; + Hacl_Streaming_MD_state_64 s = *p; uint64_t total_len = s.total_len; if ((uint64_t)len > (uint64_t)18446744073709551615U - total_len) { @@ -978,7 +961,7 @@ update_384_512(Hacl_Streaming_SHA2_state_sha2_384 *p, uint8_t *data, uint32_t le } if (len <= (uint32_t)128U - sz) { - Hacl_Streaming_SHA2_state_sha2_384 s1 = *p; + Hacl_Streaming_MD_state_64 s1 = *p; uint64_t *block_state1 = s1.block_state; uint8_t *buf = s1.buf; uint64_t total_len1 = s1.total_len; @@ -997,7 +980,7 @@ update_384_512(Hacl_Streaming_SHA2_state_sha2_384 *p, uint8_t *data, uint32_t le *p = ( - (Hacl_Streaming_SHA2_state_sha2_384){ + (Hacl_Streaming_MD_state_64){ .block_state = block_state1, .buf = buf, .total_len = total_len2 @@ -1006,7 +989,7 @@ update_384_512(Hacl_Streaming_SHA2_state_sha2_384 *p, uint8_t *data, uint32_t le } else if (sz == (uint32_t)0U) { - Hacl_Streaming_SHA2_state_sha2_384 s1 = *p; + Hacl_Streaming_MD_state_64 s1 = *p; uint64_t *block_state1 = s1.block_state; uint8_t *buf = s1.buf; uint64_t total_len1 = s1.total_len; @@ -1043,7 +1026,7 @@ update_384_512(Hacl_Streaming_SHA2_state_sha2_384 *p, uint8_t *data, uint32_t le *p = ( - (Hacl_Streaming_SHA2_state_sha2_384){ + (Hacl_Streaming_MD_state_64){ .block_state = block_state1, .buf = buf, .total_len = total_len1 + (uint64_t)len @@ -1055,7 +1038,7 @@ update_384_512(Hacl_Streaming_SHA2_state_sha2_384 *p, uint8_t *data, uint32_t le uint32_t diff = (uint32_t)128U - sz; uint8_t *data1 = data; uint8_t *data2 = data + diff; - Hacl_Streaming_SHA2_state_sha2_384 s1 = *p; + Hacl_Streaming_MD_state_64 s1 = *p; uint64_t *block_state10 = s1.block_state; uint8_t *buf0 = s1.buf; uint64_t total_len10 = s1.total_len; @@ -1074,13 +1057,13 @@ update_384_512(Hacl_Streaming_SHA2_state_sha2_384 *p, uint8_t *data, uint32_t le *p = ( - (Hacl_Streaming_SHA2_state_sha2_384){ + (Hacl_Streaming_MD_state_64){ .block_state = block_state10, .buf = buf0, .total_len = total_len2 } ); - Hacl_Streaming_SHA2_state_sha2_384 s10 = *p; + Hacl_Streaming_MD_state_64 s10 = *p; uint64_t *block_state1 = s10.block_state; uint8_t *buf = s10.buf; uint64_t total_len1 = s10.total_len; @@ -1123,7 +1106,7 @@ update_384_512(Hacl_Streaming_SHA2_state_sha2_384 *p, uint8_t *data, uint32_t le *p = ( - (Hacl_Streaming_SHA2_state_sha2_384){ + (Hacl_Streaming_MD_state_64){ .block_state = block_state1, .buf = buf, .total_len = total_len1 + (uint64_t)(len - diff) @@ -1142,7 +1125,7 @@ This function is identical to the update function for SHA2_384. */ uint32_t Hacl_Streaming_SHA2_update_512( - Hacl_Streaming_SHA2_state_sha2_384 *p, + Hacl_Streaming_MD_state_64 *p, uint8_t *input, uint32_t input_len ) @@ -1156,9 +1139,9 @@ valid after a call to `finish_512`, meaning the user may feed more data into the hash via `update_512`. (The finish_512 function operates on an internal copy of the state and therefore does not invalidate the client-held state `p`.) */ -void Hacl_Streaming_SHA2_finish_512(Hacl_Streaming_SHA2_state_sha2_384 *p, uint8_t *dst) +void Hacl_Streaming_SHA2_finish_512(Hacl_Streaming_MD_state_64 *p, uint8_t *dst) { - Hacl_Streaming_SHA2_state_sha2_384 scrut = *p; + Hacl_Streaming_MD_state_64 scrut = *p; uint64_t *block_state = scrut.block_state; uint8_t *buf_ = scrut.buf; uint64_t total_len = scrut.total_len; @@ -1200,9 +1183,9 @@ Free a state allocated with `create_in_512`. This function is identical to the free function for SHA2_384. */ -void Hacl_Streaming_SHA2_free_512(Hacl_Streaming_SHA2_state_sha2_384 *s) +void Hacl_Streaming_SHA2_free_512(Hacl_Streaming_MD_state_64 *s) { - Hacl_Streaming_SHA2_state_sha2_384 scrut = *s; + Hacl_Streaming_MD_state_64 scrut = *s; uint8_t *buf = scrut.buf; uint64_t *block_state = scrut.block_state; KRML_HOST_FREE(block_state); @@ -1229,36 +1212,33 @@ void Hacl_Streaming_SHA2_sha512(uint8_t *input, uint32_t input_len, uint8_t *dst sha512_finish(st, rb); } -Hacl_Streaming_SHA2_state_sha2_384 *Hacl_Streaming_SHA2_create_in_384(void) +Hacl_Streaming_MD_state_64 *Hacl_Streaming_SHA2_create_in_384(void) { uint8_t *buf = (uint8_t *)KRML_HOST_CALLOC((uint32_t)128U, sizeof (uint8_t)); uint64_t *block_state = (uint64_t *)KRML_HOST_CALLOC((uint32_t)8U, sizeof (uint64_t)); - Hacl_Streaming_SHA2_state_sha2_384 + Hacl_Streaming_MD_state_64 s = { .block_state = block_state, .buf = buf, .total_len = (uint64_t)(uint32_t)0U }; - Hacl_Streaming_SHA2_state_sha2_384 - *p = - (Hacl_Streaming_SHA2_state_sha2_384 *)KRML_HOST_MALLOC(sizeof ( - Hacl_Streaming_SHA2_state_sha2_384 - )); + Hacl_Streaming_MD_state_64 + *p = (Hacl_Streaming_MD_state_64 *)KRML_HOST_MALLOC(sizeof (Hacl_Streaming_MD_state_64)); p[0U] = s; sha384_init(block_state); return p; } -void Hacl_Streaming_SHA2_init_384(Hacl_Streaming_SHA2_state_sha2_384 *s) +void Hacl_Streaming_SHA2_init_384(Hacl_Streaming_MD_state_64 *s) { - Hacl_Streaming_SHA2_state_sha2_384 scrut = *s; + Hacl_Streaming_MD_state_64 scrut = *s; uint8_t *buf = scrut.buf; uint64_t *block_state = scrut.block_state; sha384_init(block_state); - Hacl_Streaming_SHA2_state_sha2_384 + Hacl_Streaming_MD_state_64 tmp = { .block_state = block_state, .buf = buf, .total_len = (uint64_t)(uint32_t)0U }; s[0U] = tmp; } uint32_t Hacl_Streaming_SHA2_update_384( - Hacl_Streaming_SHA2_state_sha2_384 *p, + Hacl_Streaming_MD_state_64 *p, uint8_t *input, uint32_t input_len ) @@ -1271,9 +1251,9 @@ Write the resulting hash into `dst`, an array of 48 bytes. The state remains valid after a call to `finish_384`, meaning the user may feed more data into the hash via `update_384`. */ -void Hacl_Streaming_SHA2_finish_384(Hacl_Streaming_SHA2_state_sha2_384 *p, uint8_t *dst) +void Hacl_Streaming_SHA2_finish_384(Hacl_Streaming_MD_state_64 *p, uint8_t *dst) { - Hacl_Streaming_SHA2_state_sha2_384 scrut = *p; + Hacl_Streaming_MD_state_64 scrut = *p; uint64_t *block_state = scrut.block_state; uint8_t *buf_ = scrut.buf; uint64_t total_len = scrut.total_len; @@ -1310,7 +1290,7 @@ void Hacl_Streaming_SHA2_finish_384(Hacl_Streaming_SHA2_state_sha2_384 *p, uint8 sha384_finish(tmp_block_state, dst); } -void Hacl_Streaming_SHA2_free_384(Hacl_Streaming_SHA2_state_sha2_384 *p) +void Hacl_Streaming_SHA2_free_384(Hacl_Streaming_MD_state_64 *p) { Hacl_Streaming_SHA2_free_512(p); } diff --git a/Modules/_hacl/Hacl_Streaming_SHA2.h b/Modules/_hacl/Hacl_Streaming_SHA2.h index 2c905854f336fd..b58df4c4d121c9 100644 --- a/Modules/_hacl/Hacl_Streaming_SHA2.h +++ b/Modules/_hacl/Hacl_Streaming_SHA2.h @@ -36,33 +36,22 @@ extern "C" { #include "krml/lowstar_endianness.h" #include "krml/internal/target.h" +#include "Hacl_Streaming_Types.h" -typedef struct Hacl_Streaming_SHA2_state_sha2_224_s -{ - uint32_t *block_state; - uint8_t *buf; - uint64_t total_len; -} -Hacl_Streaming_SHA2_state_sha2_224; +typedef Hacl_Streaming_MD_state_32 Hacl_Streaming_SHA2_state_sha2_224; -typedef Hacl_Streaming_SHA2_state_sha2_224 Hacl_Streaming_SHA2_state_sha2_256; +typedef Hacl_Streaming_MD_state_32 Hacl_Streaming_SHA2_state_sha2_256; -typedef struct Hacl_Streaming_SHA2_state_sha2_384_s -{ - uint64_t *block_state; - uint8_t *buf; - uint64_t total_len; -} -Hacl_Streaming_SHA2_state_sha2_384; +typedef Hacl_Streaming_MD_state_64 Hacl_Streaming_SHA2_state_sha2_384; -typedef Hacl_Streaming_SHA2_state_sha2_384 Hacl_Streaming_SHA2_state_sha2_512; +typedef Hacl_Streaming_MD_state_64 Hacl_Streaming_SHA2_state_sha2_512; /** Allocate initial state for the SHA2_256 hash. The state is to be freed by calling `free_256`. */ -Hacl_Streaming_SHA2_state_sha2_224 *Hacl_Streaming_SHA2_create_in_256(void); +Hacl_Streaming_MD_state_32 *Hacl_Streaming_SHA2_create_in_256(void); /** Copies the state passed as argument into a newly allocated state (deep copy). @@ -70,13 +59,12 @@ The state is to be freed by calling `free_256`. Cloning the state this way is useful, for instance, if your control-flow diverges and you need to feed more (different) data into the hash in each branch. */ -Hacl_Streaming_SHA2_state_sha2_224 -*Hacl_Streaming_SHA2_copy_256(Hacl_Streaming_SHA2_state_sha2_224 *s0); +Hacl_Streaming_MD_state_32 *Hacl_Streaming_SHA2_copy_256(Hacl_Streaming_MD_state_32 *s0); /** Reset an existing state to the initial hash state with empty data. */ -void Hacl_Streaming_SHA2_init_256(Hacl_Streaming_SHA2_state_sha2_224 *s); +void Hacl_Streaming_SHA2_init_256(Hacl_Streaming_MD_state_32 *s); /** Feed an arbitrary amount of data into the hash. This function returns 0 for @@ -87,7 +75,7 @@ This function is identical to the update function for SHA2_224. */ uint32_t Hacl_Streaming_SHA2_update_256( - Hacl_Streaming_SHA2_state_sha2_224 *p, + Hacl_Streaming_MD_state_32 *p, uint8_t *input, uint32_t input_len ); @@ -98,27 +86,27 @@ valid after a call to `finish_256`, meaning the user may feed more data into the hash via `update_256`. (The finish_256 function operates on an internal copy of the state and therefore does not invalidate the client-held state `p`.) */ -void Hacl_Streaming_SHA2_finish_256(Hacl_Streaming_SHA2_state_sha2_224 *p, uint8_t *dst); +void Hacl_Streaming_SHA2_finish_256(Hacl_Streaming_MD_state_32 *p, uint8_t *dst); /** Free a state allocated with `create_in_256`. This function is identical to the free function for SHA2_224. */ -void Hacl_Streaming_SHA2_free_256(Hacl_Streaming_SHA2_state_sha2_224 *s); +void Hacl_Streaming_SHA2_free_256(Hacl_Streaming_MD_state_32 *s); /** Hash `input`, of len `input_len`, into `dst`, an array of 32 bytes. */ void Hacl_Streaming_SHA2_sha256(uint8_t *input, uint32_t input_len, uint8_t *dst); -Hacl_Streaming_SHA2_state_sha2_224 *Hacl_Streaming_SHA2_create_in_224(void); +Hacl_Streaming_MD_state_32 *Hacl_Streaming_SHA2_create_in_224(void); -void Hacl_Streaming_SHA2_init_224(Hacl_Streaming_SHA2_state_sha2_224 *s); +void Hacl_Streaming_SHA2_init_224(Hacl_Streaming_MD_state_32 *s); uint32_t Hacl_Streaming_SHA2_update_224( - Hacl_Streaming_SHA2_state_sha2_224 *p, + Hacl_Streaming_MD_state_32 *p, uint8_t *input, uint32_t input_len ); @@ -128,16 +116,16 @@ Write the resulting hash into `dst`, an array of 28 bytes. The state remains valid after a call to `finish_224`, meaning the user may feed more data into the hash via `update_224`. */ -void Hacl_Streaming_SHA2_finish_224(Hacl_Streaming_SHA2_state_sha2_224 *p, uint8_t *dst); +void Hacl_Streaming_SHA2_finish_224(Hacl_Streaming_MD_state_32 *p, uint8_t *dst); -void Hacl_Streaming_SHA2_free_224(Hacl_Streaming_SHA2_state_sha2_224 *p); +void Hacl_Streaming_SHA2_free_224(Hacl_Streaming_MD_state_32 *p); /** Hash `input`, of len `input_len`, into `dst`, an array of 28 bytes. */ void Hacl_Streaming_SHA2_sha224(uint8_t *input, uint32_t input_len, uint8_t *dst); -Hacl_Streaming_SHA2_state_sha2_384 *Hacl_Streaming_SHA2_create_in_512(void); +Hacl_Streaming_MD_state_64 *Hacl_Streaming_SHA2_create_in_512(void); /** Copies the state passed as argument into a newly allocated state (deep copy). @@ -145,10 +133,9 @@ The state is to be freed by calling `free_512`. Cloning the state this way is useful, for instance, if your control-flow diverges and you need to feed more (different) data into the hash in each branch. */ -Hacl_Streaming_SHA2_state_sha2_384 -*Hacl_Streaming_SHA2_copy_512(Hacl_Streaming_SHA2_state_sha2_384 *s0); +Hacl_Streaming_MD_state_64 *Hacl_Streaming_SHA2_copy_512(Hacl_Streaming_MD_state_64 *s0); -void Hacl_Streaming_SHA2_init_512(Hacl_Streaming_SHA2_state_sha2_384 *s); +void Hacl_Streaming_SHA2_init_512(Hacl_Streaming_MD_state_64 *s); /** Feed an arbitrary amount of data into the hash. This function returns 0 for @@ -159,7 +146,7 @@ This function is identical to the update function for SHA2_384. */ uint32_t Hacl_Streaming_SHA2_update_512( - Hacl_Streaming_SHA2_state_sha2_384 *p, + Hacl_Streaming_MD_state_64 *p, uint8_t *input, uint32_t input_len ); @@ -170,27 +157,27 @@ valid after a call to `finish_512`, meaning the user may feed more data into the hash via `update_512`. (The finish_512 function operates on an internal copy of the state and therefore does not invalidate the client-held state `p`.) */ -void Hacl_Streaming_SHA2_finish_512(Hacl_Streaming_SHA2_state_sha2_384 *p, uint8_t *dst); +void Hacl_Streaming_SHA2_finish_512(Hacl_Streaming_MD_state_64 *p, uint8_t *dst); /** Free a state allocated with `create_in_512`. This function is identical to the free function for SHA2_384. */ -void Hacl_Streaming_SHA2_free_512(Hacl_Streaming_SHA2_state_sha2_384 *s); +void Hacl_Streaming_SHA2_free_512(Hacl_Streaming_MD_state_64 *s); /** Hash `input`, of len `input_len`, into `dst`, an array of 64 bytes. */ void Hacl_Streaming_SHA2_sha512(uint8_t *input, uint32_t input_len, uint8_t *dst); -Hacl_Streaming_SHA2_state_sha2_384 *Hacl_Streaming_SHA2_create_in_384(void); +Hacl_Streaming_MD_state_64 *Hacl_Streaming_SHA2_create_in_384(void); -void Hacl_Streaming_SHA2_init_384(Hacl_Streaming_SHA2_state_sha2_384 *s); +void Hacl_Streaming_SHA2_init_384(Hacl_Streaming_MD_state_64 *s); uint32_t Hacl_Streaming_SHA2_update_384( - Hacl_Streaming_SHA2_state_sha2_384 *p, + Hacl_Streaming_MD_state_64 *p, uint8_t *input, uint32_t input_len ); @@ -200,9 +187,9 @@ Write the resulting hash into `dst`, an array of 48 bytes. The state remains valid after a call to `finish_384`, meaning the user may feed more data into the hash via `update_384`. */ -void Hacl_Streaming_SHA2_finish_384(Hacl_Streaming_SHA2_state_sha2_384 *p, uint8_t *dst); +void Hacl_Streaming_SHA2_finish_384(Hacl_Streaming_MD_state_64 *p, uint8_t *dst); -void Hacl_Streaming_SHA2_free_384(Hacl_Streaming_SHA2_state_sha2_384 *p); +void Hacl_Streaming_SHA2_free_384(Hacl_Streaming_MD_state_64 *p); /** Hash `input`, of len `input_len`, into `dst`, an array of 48 bytes. diff --git a/Modules/_hacl/Hacl_Streaming_Types.h b/Modules/_hacl/Hacl_Streaming_Types.h new file mode 100644 index 00000000000000..51057611ca978d --- /dev/null +++ b/Modules/_hacl/Hacl_Streaming_Types.h @@ -0,0 +1,59 @@ +/* MIT License + * + * Copyright (c) 2016-2022 INRIA, CMU and Microsoft Corporation + * Copyright (c) 2022-2023 HACL* Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +#ifndef __Hacl_Streaming_Types_H +#define __Hacl_Streaming_Types_H + +#if defined(__cplusplus) +extern "C" { +#endif + +#include +#include "krml/types.h" +#include "krml/lowstar_endianness.h" +#include "krml/internal/target.h" + +typedef struct Hacl_Streaming_MD_state_32_s +{ + uint32_t *block_state; + uint8_t *buf; + uint64_t total_len; +} +Hacl_Streaming_MD_state_32; + +typedef struct Hacl_Streaming_MD_state_64_s +{ + uint64_t *block_state; + uint8_t *buf; + uint64_t total_len; +} +Hacl_Streaming_MD_state_64; + +#if defined(__cplusplus) +} +#endif + +#define __Hacl_Streaming_Types_H_DEFINED +#endif diff --git a/Modules/_hacl/include/krml/FStar_UInt128_Verified.h b/Modules/_hacl/include/krml/FStar_UInt128_Verified.h index ee160193539e28..3d36d440735530 100644 --- a/Modules/_hacl/include/krml/FStar_UInt128_Verified.h +++ b/Modules/_hacl/include/krml/FStar_UInt128_Verified.h @@ -7,13 +7,12 @@ #ifndef __FStar_UInt128_Verified_H #define __FStar_UInt128_Verified_H - - #include "FStar_UInt_8_16_32_64.h" #include #include #include "krml/types.h" #include "krml/internal/target.h" + static inline uint64_t FStar_UInt128_constant_time_carry(uint64_t a, uint64_t b) { return (a ^ ((a ^ b) | ((a - b) ^ b))) >> (uint32_t)63U; diff --git a/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h b/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h index 965afc836fd12b..a56c7d613498b7 100644 --- a/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h +++ b/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h @@ -7,15 +7,13 @@ #ifndef __FStar_UInt_8_16_32_64_H #define __FStar_UInt_8_16_32_64_H - - - #include #include #include "krml/lowstar_endianness.h" #include "krml/types.h" #include "krml/internal/target.h" + static inline uint64_t FStar_UInt64_eq_mask(uint64_t a, uint64_t b) { uint64_t x = a ^ b; diff --git a/Modules/_hacl/include/krml/internal/target.h b/Modules/_hacl/include/krml/internal/target.h index 9ef59859a554b5..dcbe7007b17be8 100644 --- a/Modules/_hacl/include/krml/internal/target.h +++ b/Modules/_hacl/include/krml/internal/target.h @@ -31,6 +31,10 @@ # define KRML_HOST_FREE free #endif +#ifndef KRML_HOST_IGNORE +# define KRML_HOST_IGNORE(x) (void)(x) +#endif + /* Macros for prettier unrolling of loops */ #define KRML_LOOP1(i, n, x) { \ x \ diff --git a/Modules/_hacl/internal/Hacl_Hash_MD5.h b/Modules/_hacl/internal/Hacl_Hash_MD5.h new file mode 100644 index 00000000000000..87ad4cf228d91b --- /dev/null +++ b/Modules/_hacl/internal/Hacl_Hash_MD5.h @@ -0,0 +1,61 @@ +/* MIT License + * + * Copyright (c) 2016-2022 INRIA, CMU and Microsoft Corporation + * Copyright (c) 2022-2023 HACL* Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +#ifndef __internal_Hacl_Hash_MD5_H +#define __internal_Hacl_Hash_MD5_H + +#if defined(__cplusplus) +extern "C" { +#endif + +#include +#include "krml/types.h" +#include "krml/lowstar_endianness.h" +#include "krml/internal/target.h" + +#include "../Hacl_Hash_MD5.h" + +void Hacl_Hash_Core_MD5_legacy_init(uint32_t *s); + +void Hacl_Hash_Core_MD5_legacy_finish(uint32_t *s, uint8_t *dst); + +void Hacl_Hash_MD5_legacy_update_multi(uint32_t *s, uint8_t *blocks, uint32_t n_blocks); + +void +Hacl_Hash_MD5_legacy_update_last( + uint32_t *s, + uint64_t prev_len, + uint8_t *input, + uint32_t input_len +); + +void Hacl_Hash_MD5_legacy_hash(uint8_t *input, uint32_t input_len, uint8_t *dst); + +#if defined(__cplusplus) +} +#endif + +#define __internal_Hacl_Hash_MD5_H_DEFINED +#endif diff --git a/Modules/_hacl/internal/Hacl_Hash_SHA1.h b/Modules/_hacl/internal/Hacl_Hash_SHA1.h new file mode 100644 index 00000000000000..d2d9df44c6c14c --- /dev/null +++ b/Modules/_hacl/internal/Hacl_Hash_SHA1.h @@ -0,0 +1,61 @@ +/* MIT License + * + * Copyright (c) 2016-2022 INRIA, CMU and Microsoft Corporation + * Copyright (c) 2022-2023 HACL* Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +#ifndef __internal_Hacl_Hash_SHA1_H +#define __internal_Hacl_Hash_SHA1_H + +#if defined(__cplusplus) +extern "C" { +#endif + +#include +#include "krml/types.h" +#include "krml/lowstar_endianness.h" +#include "krml/internal/target.h" + +#include "../Hacl_Hash_SHA1.h" + +void Hacl_Hash_Core_SHA1_legacy_init(uint32_t *s); + +void Hacl_Hash_Core_SHA1_legacy_finish(uint32_t *s, uint8_t *dst); + +void Hacl_Hash_SHA1_legacy_update_multi(uint32_t *s, uint8_t *blocks, uint32_t n_blocks); + +void +Hacl_Hash_SHA1_legacy_update_last( + uint32_t *s, + uint64_t prev_len, + uint8_t *input, + uint32_t input_len +); + +void Hacl_Hash_SHA1_legacy_hash(uint8_t *input, uint32_t input_len, uint8_t *dst); + +#if defined(__cplusplus) +} +#endif + +#define __internal_Hacl_Hash_SHA1_H_DEFINED +#endif diff --git a/Modules/_hacl/python_hacl_namespaces.h b/Modules/_hacl/python_hacl_namespaces.h index ac12f386257b19..ee28f244266b85 100644 --- a/Modules/_hacl/python_hacl_namespaces.h +++ b/Modules/_hacl/python_hacl_namespaces.h @@ -43,4 +43,21 @@ #define Hacl_Streaming_SHA2_sha512 python_hashlib_Hacl_Streaming_SHA2_sha512 #define Hacl_Streaming_SHA2_sha384 python_hashlib_Hacl_Streaming_SHA2_sha384 +#define Hacl_Streaming_MD5_legacy_create_in python_hashlib_Hacl_Streaming_MD5_legacy_create_in +#define Hacl_Streaming_MD5_legacy_init python_hashlib_Hacl_Streaming_MD5_legacy_init +#define Hacl_Streaming_MD5_legacy_update python_hashlib_Hacl_Streaming_MD5_legacy_update +#define Hacl_Streaming_MD5_legacy_finish python_hashlib_Hacl_Streaming_MD5_legacy_finish +#define Hacl_Streaming_MD5_legacy_free python_hashlib_Hacl_Streaming_MD5_legacy_free +#define Hacl_Streaming_MD5_legacy_copy python_hashlib_Hacl_Streaming_MD5_legacy_copy +#define Hacl_Streaming_MD5_legacy_hash python_hashlib_Hacl_Streaming_MD5_legacy_hash + +#define Hacl_Streaming_SHA1_legacy_create_in python_hashlib_Hacl_Streaming_SHA1_legacy_create_in +#define Hacl_Streaming_SHA1_legacy_init python_hashlib_Hacl_Streaming_SHA1_legacy_init +#define Hacl_Streaming_SHA1_legacy_update python_hashlib_Hacl_Streaming_SHA1_legacy_update +#define Hacl_Streaming_SHA1_legacy_finish python_hashlib_Hacl_Streaming_SHA1_legacy_finish +#define Hacl_Streaming_SHA1_legacy_free python_hashlib_Hacl_Streaming_SHA1_legacy_free +#define Hacl_Streaming_SHA1_legacy_copy python_hashlib_Hacl_Streaming_SHA1_legacy_copy +#define Hacl_Streaming_SHA1_legacy_hash python_hashlib_Hacl_Streaming_SHA1_legacy_hash + + #endif // _PYTHON_HACL_NAMESPACES_H diff --git a/Modules/_hacl/refresh.sh b/Modules/_hacl/refresh.sh index dba8cb3972ea17..76b92ec4599102 100755 --- a/Modules/_hacl/refresh.sh +++ b/Modules/_hacl/refresh.sh @@ -22,7 +22,7 @@ fi # Update this when updating to a new version after verifying that the changes # the update brings in are good. -expected_hacl_star_rev=4751fc2b11639f651718abf8522fcc36902ca67c +expected_hacl_star_rev=13e0c6721ac9206c4249ecc1dc04ed617ad1e262 hacl_dir="$(realpath "$1")" cd "$(dirname "$0")" @@ -41,8 +41,15 @@ fi declare -a dist_files dist_files=( Hacl_Streaming_SHA2.h + Hacl_Streaming_Types.h + Hacl_Hash_SHA1.h + internal/Hacl_Hash_SHA1.h + Hacl_Hash_MD5.h + internal/Hacl_Hash_MD5.h internal/Hacl_SHA2_Generic.h Hacl_Streaming_SHA2.c + Hacl_Hash_SHA1.c + Hacl_Hash_MD5.c ) declare -a include_files diff --git a/Modules/md5module.c b/Modules/md5module.c index 48b11e0779f875..df3d6a4a70d789 100644 --- a/Modules/md5module.c +++ b/Modules/md5module.c @@ -43,283 +43,17 @@ typedef long long MD5_INT64; /* 64-bit integer */ #define MD5_BLOCKSIZE 64 #define MD5_DIGESTSIZE 16 -/* The structure for storing MD5 info */ +#include "_hacl/Hacl_Hash_MD5.h" -struct md5_state { - MD5_INT64 length; - MD5_INT32 state[4], curlen; - unsigned char buf[MD5_BLOCKSIZE]; -}; typedef struct { PyObject_HEAD - struct md5_state hash_state; + Hacl_Streaming_MD5_state *hash_state; } MD5object; #include "clinic/md5module.c.h" -/* ------------------------------------------------------------------------ - * - * This code for the MD5 algorithm was noted as public domain. The - * original headers are pasted below. - * - * Several changes have been made to make it more compatible with the - * Python environment and desired interface. - * - */ - -/* LibTomCrypt, modular cryptographic library -- Tom St Denis - * - * LibTomCrypt is a library that provides various cryptographic - * algorithms in a highly modular and flexible manner. - * - * The library is free for all purposes without any express - * guarantee it works. - * - * Tom St Denis, tomstdenis@gmail.com, https://www.libtom.net - */ - -/* rotate the hard way (platform optimizations could be done) */ -#define ROLc(x, y) ( (((unsigned long)(x)<<(unsigned long)((y)&31)) | (((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL) - -/* Endian Neutral macros that work on all platforms */ - -#define STORE32L(x, y) \ - { (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255); \ - (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); } - -#define LOAD32L(x, y) \ - { x = ((unsigned long)((y)[3] & 255)<<24) | \ - ((unsigned long)((y)[2] & 255)<<16) | \ - ((unsigned long)((y)[1] & 255)<<8) | \ - ((unsigned long)((y)[0] & 255)); } - -#define STORE64L(x, y) \ - { (y)[7] = (unsigned char)(((x)>>56)&255); (y)[6] = (unsigned char)(((x)>>48)&255); \ - (y)[5] = (unsigned char)(((x)>>40)&255); (y)[4] = (unsigned char)(((x)>>32)&255); \ - (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255); \ - (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); } - - -/* MD5 macros */ - -#define F(x,y,z) (z ^ (x & (y ^ z))) -#define G(x,y,z) (y ^ (z & (y ^ x))) -#define H(x,y,z) (x^y^z) -#define I(x,y,z) (y^(x|(~z))) - -#define FF(a,b,c,d,M,s,t) \ - a = (a + F(b,c,d) + M + t); a = ROLc(a, s) + b; - -#define GG(a,b,c,d,M,s,t) \ - a = (a + G(b,c,d) + M + t); a = ROLc(a, s) + b; - -#define HH(a,b,c,d,M,s,t) \ - a = (a + H(b,c,d) + M + t); a = ROLc(a, s) + b; - -#define II(a,b,c,d,M,s,t) \ - a = (a + I(b,c,d) + M + t); a = ROLc(a, s) + b; - - -static void md5_compress(struct md5_state *md5, const unsigned char *buf) -{ - MD5_INT32 i, W[16], a, b, c, d; - - assert(md5 != NULL); - assert(buf != NULL); - - /* copy the state into 512-bits into W[0..15] */ - for (i = 0; i < 16; i++) { - LOAD32L(W[i], buf + (4*i)); - } - - /* copy state */ - a = md5->state[0]; - b = md5->state[1]; - c = md5->state[2]; - d = md5->state[3]; - - FF(a,b,c,d,W[0],7,0xd76aa478UL) - FF(d,a,b,c,W[1],12,0xe8c7b756UL) - FF(c,d,a,b,W[2],17,0x242070dbUL) - FF(b,c,d,a,W[3],22,0xc1bdceeeUL) - FF(a,b,c,d,W[4],7,0xf57c0fafUL) - FF(d,a,b,c,W[5],12,0x4787c62aUL) - FF(c,d,a,b,W[6],17,0xa8304613UL) - FF(b,c,d,a,W[7],22,0xfd469501UL) - FF(a,b,c,d,W[8],7,0x698098d8UL) - FF(d,a,b,c,W[9],12,0x8b44f7afUL) - FF(c,d,a,b,W[10],17,0xffff5bb1UL) - FF(b,c,d,a,W[11],22,0x895cd7beUL) - FF(a,b,c,d,W[12],7,0x6b901122UL) - FF(d,a,b,c,W[13],12,0xfd987193UL) - FF(c,d,a,b,W[14],17,0xa679438eUL) - FF(b,c,d,a,W[15],22,0x49b40821UL) - GG(a,b,c,d,W[1],5,0xf61e2562UL) - GG(d,a,b,c,W[6],9,0xc040b340UL) - GG(c,d,a,b,W[11],14,0x265e5a51UL) - GG(b,c,d,a,W[0],20,0xe9b6c7aaUL) - GG(a,b,c,d,W[5],5,0xd62f105dUL) - GG(d,a,b,c,W[10],9,0x02441453UL) - GG(c,d,a,b,W[15],14,0xd8a1e681UL) - GG(b,c,d,a,W[4],20,0xe7d3fbc8UL) - GG(a,b,c,d,W[9],5,0x21e1cde6UL) - GG(d,a,b,c,W[14],9,0xc33707d6UL) - GG(c,d,a,b,W[3],14,0xf4d50d87UL) - GG(b,c,d,a,W[8],20,0x455a14edUL) - GG(a,b,c,d,W[13],5,0xa9e3e905UL) - GG(d,a,b,c,W[2],9,0xfcefa3f8UL) - GG(c,d,a,b,W[7],14,0x676f02d9UL) - GG(b,c,d,a,W[12],20,0x8d2a4c8aUL) - HH(a,b,c,d,W[5],4,0xfffa3942UL) - HH(d,a,b,c,W[8],11,0x8771f681UL) - HH(c,d,a,b,W[11],16,0x6d9d6122UL) - HH(b,c,d,a,W[14],23,0xfde5380cUL) - HH(a,b,c,d,W[1],4,0xa4beea44UL) - HH(d,a,b,c,W[4],11,0x4bdecfa9UL) - HH(c,d,a,b,W[7],16,0xf6bb4b60UL) - HH(b,c,d,a,W[10],23,0xbebfbc70UL) - HH(a,b,c,d,W[13],4,0x289b7ec6UL) - HH(d,a,b,c,W[0],11,0xeaa127faUL) - HH(c,d,a,b,W[3],16,0xd4ef3085UL) - HH(b,c,d,a,W[6],23,0x04881d05UL) - HH(a,b,c,d,W[9],4,0xd9d4d039UL) - HH(d,a,b,c,W[12],11,0xe6db99e5UL) - HH(c,d,a,b,W[15],16,0x1fa27cf8UL) - HH(b,c,d,a,W[2],23,0xc4ac5665UL) - II(a,b,c,d,W[0],6,0xf4292244UL) - II(d,a,b,c,W[7],10,0x432aff97UL) - II(c,d,a,b,W[14],15,0xab9423a7UL) - II(b,c,d,a,W[5],21,0xfc93a039UL) - II(a,b,c,d,W[12],6,0x655b59c3UL) - II(d,a,b,c,W[3],10,0x8f0ccc92UL) - II(c,d,a,b,W[10],15,0xffeff47dUL) - II(b,c,d,a,W[1],21,0x85845dd1UL) - II(a,b,c,d,W[8],6,0x6fa87e4fUL) - II(d,a,b,c,W[15],10,0xfe2ce6e0UL) - II(c,d,a,b,W[6],15,0xa3014314UL) - II(b,c,d,a,W[13],21,0x4e0811a1UL) - II(a,b,c,d,W[4],6,0xf7537e82UL) - II(d,a,b,c,W[11],10,0xbd3af235UL) - II(c,d,a,b,W[2],15,0x2ad7d2bbUL) - II(b,c,d,a,W[9],21,0xeb86d391UL) - - md5->state[0] = md5->state[0] + a; - md5->state[1] = md5->state[1] + b; - md5->state[2] = md5->state[2] + c; - md5->state[3] = md5->state[3] + d; -} - - -/** - Initialize the hash state - @param md5 The hash state you wish to initialize -*/ -static void -md5_init(struct md5_state *md5) -{ - assert(md5 != NULL); - md5->state[0] = 0x67452301UL; - md5->state[1] = 0xefcdab89UL; - md5->state[2] = 0x98badcfeUL; - md5->state[3] = 0x10325476UL; - md5->curlen = 0; - md5->length = 0; -} - -/** - Process a block of memory though the hash - @param md5 The hash state - @param in The data to hash - @param inlen The length of the data (octets) -*/ -static void -md5_process(struct md5_state *md5, const unsigned char *in, Py_ssize_t inlen) -{ - Py_ssize_t n; - - assert(md5 != NULL); - assert(in != NULL); - assert(md5->curlen <= sizeof(md5->buf)); - - while (inlen > 0) { - if (md5->curlen == 0 && inlen >= MD5_BLOCKSIZE) { - md5_compress(md5, in); - md5->length += MD5_BLOCKSIZE * 8; - in += MD5_BLOCKSIZE; - inlen -= MD5_BLOCKSIZE; - } else { - n = Py_MIN(inlen, (Py_ssize_t)(MD5_BLOCKSIZE - md5->curlen)); - memcpy(md5->buf + md5->curlen, in, (size_t)n); - md5->curlen += (MD5_INT32)n; - in += n; - inlen -= n; - if (md5->curlen == MD5_BLOCKSIZE) { - md5_compress(md5, md5->buf); - md5->length += 8*MD5_BLOCKSIZE; - md5->curlen = 0; - } - } - } -} - -/** - Terminate the hash to get the digest - @param md5 The hash state - @param out [out] The destination of the hash (16 bytes) -*/ -static void -md5_done(struct md5_state *md5, unsigned char *out) -{ - int i; - - assert(md5 != NULL); - assert(out != NULL); - assert(md5->curlen < sizeof(md5->buf)); - - /* increase the length of the message */ - md5->length += md5->curlen * 8; - - /* append the '1' bit */ - md5->buf[md5->curlen++] = (unsigned char)0x80; - - /* if the length is currently above 56 bytes we append zeros - * then compress. Then we can fall back to padding zeros and length - * encoding like normal. - */ - if (md5->curlen > 56) { - while (md5->curlen < 64) { - md5->buf[md5->curlen++] = (unsigned char)0; - } - md5_compress(md5, md5->buf); - md5->curlen = 0; - } - - /* pad up to 56 bytes of zeroes */ - while (md5->curlen < 56) { - md5->buf[md5->curlen++] = (unsigned char)0; - } - - /* store length */ - STORE64L(md5->length, md5->buf+56); - md5_compress(md5, md5->buf); - - /* copy output */ - for (i = 0; i < 4; i++) { - STORE32L(md5->state[i], out+(4*i)); - } -} - -/* .Source: /cvs/libtom/libtomcrypt/src/hashes/md5.c,v $ */ -/* .Revision: 1.10 $ */ -/* .Date: 2007/05/12 14:25:28 $ */ - -/* - * End of copied MD5 code. - * - * ------------------------------------------------------------------------ - */ typedef struct { PyTypeObject* md5_type; @@ -350,8 +84,9 @@ MD5_traverse(PyObject *ptr, visitproc visit, void *arg) } static void -MD5_dealloc(PyObject *ptr) +MD5_dealloc(MD5object *ptr) { + Hacl_Streaming_MD5_legacy_free(ptr->hash_state); PyTypeObject *tp = Py_TYPE(ptr); PyObject_GC_UnTrack(ptr); PyObject_GC_Del(ptr); @@ -379,7 +114,7 @@ MD5Type_copy_impl(MD5object *self, PyTypeObject *cls) if ((newobj = newMD5object(st))==NULL) return NULL; - newobj->hash_state = self->hash_state; + newobj->hash_state = Hacl_Streaming_MD5_legacy_copy(self->hash_state); return (PyObject *)newobj; } @@ -394,10 +129,7 @@ MD5Type_digest_impl(MD5object *self) /*[clinic end generated code: output=eb691dc4190a07ec input=bc0c4397c2994be6]*/ { unsigned char digest[MD5_DIGESTSIZE]; - struct md5_state temp; - - temp = self->hash_state; - md5_done(&temp, digest); + Hacl_Streaming_MD5_legacy_finish(self->hash_state, digest); return PyBytes_FromStringAndSize((const char *)digest, MD5_DIGESTSIZE); } @@ -412,15 +144,21 @@ MD5Type_hexdigest_impl(MD5object *self) /*[clinic end generated code: output=17badced1f3ac932 input=b60b19de644798dd]*/ { unsigned char digest[MD5_DIGESTSIZE]; - struct md5_state temp; - - /* Get the raw (binary) digest value */ - temp = self->hash_state; - md5_done(&temp, digest); - + Hacl_Streaming_MD5_legacy_finish(self->hash_state, digest); return _Py_strhex((const char*)digest, MD5_DIGESTSIZE); } +static void update(Hacl_Streaming_MD5_state *state, uint8_t *buf, Py_ssize_t len) { +#if PY_SSIZE_T_MAX > UINT32_MAX + while (len > UINT32_MAX) { + Hacl_Streaming_MD5_legacy_update(state, buf, UINT32_MAX); + len -= UINT32_MAX; + buf += UINT32_MAX; + } +#endif + Hacl_Streaming_MD5_legacy_update(state, buf, (uint32_t) len); +} + /*[clinic input] MD5Type.update @@ -438,7 +176,7 @@ MD5Type_update(MD5object *self, PyObject *obj) GET_BUFFER_VIEW_OR_ERROUT(obj, &buf); - md5_process(&self->hash_state, buf.buf, buf.len); + update(self->hash_state, buf.buf, buf.len); PyBuffer_Release(&buf); Py_RETURN_NONE; @@ -531,7 +269,7 @@ _md5_md5_impl(PyObject *module, PyObject *string, int usedforsecurity) return NULL; } - md5_init(&new->hash_state); + new->hash_state = Hacl_Streaming_MD5_legacy_create_in(); if (PyErr_Occurred()) { Py_DECREF(new); @@ -540,7 +278,7 @@ _md5_md5_impl(PyObject *module, PyObject *string, int usedforsecurity) return NULL; } if (string) { - md5_process(&new->hash_state, buf.buf, buf.len); + update(new->hash_state, buf.buf, buf.len); PyBuffer_Release(&buf); } diff --git a/Modules/sha1module.c b/Modules/sha1module.c index 9153557fbde740..0f50d532acf925 100644 --- a/Modules/sha1module.c +++ b/Modules/sha1module.c @@ -43,260 +43,16 @@ typedef long long SHA1_INT64; /* 64-bit integer */ #define SHA1_BLOCKSIZE 64 #define SHA1_DIGESTSIZE 20 -/* The structure for storing SHA1 info */ - -struct sha1_state { - SHA1_INT64 length; - SHA1_INT32 state[5], curlen; - unsigned char buf[SHA1_BLOCKSIZE]; -}; +#include "_hacl/Hacl_Hash_SHA1.h" typedef struct { PyObject_HEAD - struct sha1_state hash_state; + Hacl_Streaming_SHA1_state *hash_state; } SHA1object; #include "clinic/sha1module.c.h" -/* ------------------------------------------------------------------------ - * - * This code for the SHA1 algorithm was noted as public domain. The - * original headers are pasted below. - * - * Several changes have been made to make it more compatible with the - * Python environment and desired interface. - * - */ - -/* LibTomCrypt, modular cryptographic library -- Tom St Denis - * - * LibTomCrypt is a library that provides various cryptographic - * algorithms in a highly modular and flexible manner. - * - * The library is free for all purposes without any express - * guarantee it works. - * - * Tom St Denis, tomstdenis@gmail.com, https://www.libtom.net - */ - -/* rotate the hard way (platform optimizations could be done) */ -#define ROL(x, y) ( (((unsigned long)(x)<<(unsigned long)((y)&31)) | (((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL) -#define ROLc(x, y) ( (((unsigned long)(x)<<(unsigned long)((y)&31)) | (((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL) - -/* Endian Neutral macros that work on all platforms */ - -#define STORE32H(x, y) \ - { (y)[0] = (unsigned char)(((x)>>24)&255); (y)[1] = (unsigned char)(((x)>>16)&255); \ - (y)[2] = (unsigned char)(((x)>>8)&255); (y)[3] = (unsigned char)((x)&255); } - -#define LOAD32H(x, y) \ - { x = ((unsigned long)((y)[0] & 255)<<24) | \ - ((unsigned long)((y)[1] & 255)<<16) | \ - ((unsigned long)((y)[2] & 255)<<8) | \ - ((unsigned long)((y)[3] & 255)); } - -#define STORE64H(x, y) \ - { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \ - (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \ - (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \ - (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); } - - -/* SHA1 macros */ - -#define F0(x,y,z) (z ^ (x & (y ^ z))) -#define F1(x,y,z) (x ^ y ^ z) -#define F2(x,y,z) ((x & y) | (z & (x | y))) -#define F3(x,y,z) (x ^ y ^ z) - -static void sha1_compress(struct sha1_state *sha1, unsigned char *buf) -{ - SHA1_INT32 a,b,c,d,e,W[80],i; - - /* copy the state into 512-bits into W[0..15] */ - for (i = 0; i < 16; i++) { - LOAD32H(W[i], buf + (4*i)); - } - - /* copy state */ - a = sha1->state[0]; - b = sha1->state[1]; - c = sha1->state[2]; - d = sha1->state[3]; - e = sha1->state[4]; - - /* expand it */ - for (i = 16; i < 80; i++) { - W[i] = ROL(W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16], 1); - } - - /* compress */ - /* round one */ - #define FF_0(a,b,c,d,e,i) e = (ROLc(a, 5) + F0(b,c,d) + e + W[i] + 0x5a827999UL); b = ROLc(b, 30); - #define FF_1(a,b,c,d,e,i) e = (ROLc(a, 5) + F1(b,c,d) + e + W[i] + 0x6ed9eba1UL); b = ROLc(b, 30); - #define FF_2(a,b,c,d,e,i) e = (ROLc(a, 5) + F2(b,c,d) + e + W[i] + 0x8f1bbcdcUL); b = ROLc(b, 30); - #define FF_3(a,b,c,d,e,i) e = (ROLc(a, 5) + F3(b,c,d) + e + W[i] + 0xca62c1d6UL); b = ROLc(b, 30); - - for (i = 0; i < 20; ) { - FF_0(a,b,c,d,e,i++); - FF_0(e,a,b,c,d,i++); - FF_0(d,e,a,b,c,i++); - FF_0(c,d,e,a,b,i++); - FF_0(b,c,d,e,a,i++); - } - - /* round two */ - for (; i < 40; ) { - FF_1(a,b,c,d,e,i++); - FF_1(e,a,b,c,d,i++); - FF_1(d,e,a,b,c,i++); - FF_1(c,d,e,a,b,i++); - FF_1(b,c,d,e,a,i++); - } - - /* round three */ - for (; i < 60; ) { - FF_2(a,b,c,d,e,i++); - FF_2(e,a,b,c,d,i++); - FF_2(d,e,a,b,c,i++); - FF_2(c,d,e,a,b,i++); - FF_2(b,c,d,e,a,i++); - } - - /* round four */ - for (; i < 80; ) { - FF_3(a,b,c,d,e,i++); - FF_3(e,a,b,c,d,i++); - FF_3(d,e,a,b,c,i++); - FF_3(c,d,e,a,b,i++); - FF_3(b,c,d,e,a,i++); - } - - #undef FF_0 - #undef FF_1 - #undef FF_2 - #undef FF_3 - - /* store */ - sha1->state[0] = sha1->state[0] + a; - sha1->state[1] = sha1->state[1] + b; - sha1->state[2] = sha1->state[2] + c; - sha1->state[3] = sha1->state[3] + d; - sha1->state[4] = sha1->state[4] + e; -} - -/** - Initialize the hash state - @param sha1 The hash state you wish to initialize -*/ -static void -sha1_init(struct sha1_state *sha1) -{ - assert(sha1 != NULL); - sha1->state[0] = 0x67452301UL; - sha1->state[1] = 0xefcdab89UL; - sha1->state[2] = 0x98badcfeUL; - sha1->state[3] = 0x10325476UL; - sha1->state[4] = 0xc3d2e1f0UL; - sha1->curlen = 0; - sha1->length = 0; -} - -/** - Process a block of memory though the hash - @param sha1 The hash state - @param in The data to hash - @param inlen The length of the data (octets) -*/ -static void -sha1_process(struct sha1_state *sha1, - const unsigned char *in, Py_ssize_t inlen) -{ - Py_ssize_t n; - - assert(sha1 != NULL); - assert(in != NULL); - assert(sha1->curlen <= sizeof(sha1->buf)); - - while (inlen > 0) { - if (sha1->curlen == 0 && inlen >= SHA1_BLOCKSIZE) { - sha1_compress(sha1, (unsigned char *)in); - sha1->length += SHA1_BLOCKSIZE * 8; - in += SHA1_BLOCKSIZE; - inlen -= SHA1_BLOCKSIZE; - } else { - n = Py_MIN(inlen, (Py_ssize_t)(SHA1_BLOCKSIZE - sha1->curlen)); - memcpy(sha1->buf + sha1->curlen, in, (size_t)n); - sha1->curlen += (SHA1_INT32)n; - in += n; - inlen -= n; - if (sha1->curlen == SHA1_BLOCKSIZE) { - sha1_compress(sha1, sha1->buf); - sha1->length += 8*SHA1_BLOCKSIZE; - sha1->curlen = 0; - } - } - } -} - -/** - Terminate the hash to get the digest - @param sha1 The hash state - @param out [out] The destination of the hash (20 bytes) -*/ -static void -sha1_done(struct sha1_state *sha1, unsigned char *out) -{ - int i; - - assert(sha1 != NULL); - assert(out != NULL); - assert(sha1->curlen < sizeof(sha1->buf)); - - /* increase the length of the message */ - sha1->length += sha1->curlen * 8; - - /* append the '1' bit */ - sha1->buf[sha1->curlen++] = (unsigned char)0x80; - - /* if the length is currently above 56 bytes we append zeros - * then compress. Then we can fall back to padding zeros and length - * encoding like normal. - */ - if (sha1->curlen > 56) { - while (sha1->curlen < 64) { - sha1->buf[sha1->curlen++] = (unsigned char)0; - } - sha1_compress(sha1, sha1->buf); - sha1->curlen = 0; - } - - /* pad up to 56 bytes of zeroes */ - while (sha1->curlen < 56) { - sha1->buf[sha1->curlen++] = (unsigned char)0; - } - - /* store length */ - STORE64H(sha1->length, sha1->buf+56); - sha1_compress(sha1, sha1->buf); - - /* copy output */ - for (i = 0; i < 5; i++) { - STORE32H(sha1->state[i], out+(4*i)); - } -} - - -/* .Source: /cvs/libtom/libtomcrypt/src/hashes/sha1.c,v $ */ -/* .Revision: 1.10 $ */ -/* .Date: 2007/05/12 14:25:28 $ */ - -/* - * End of copied SHA1 code. - * - * ------------------------------------------------------------------------ - */ typedef struct { PyTypeObject* sha1_type; @@ -328,8 +84,9 @@ SHA1_traverse(PyObject *ptr, visitproc visit, void *arg) } static void -SHA1_dealloc(PyObject *ptr) +SHA1_dealloc(SHA1object *ptr) { + Hacl_Streaming_SHA1_legacy_free(ptr->hash_state); PyTypeObject *tp = Py_TYPE(ptr); PyObject_GC_UnTrack(ptr); PyObject_GC_Del(ptr); @@ -357,7 +114,7 @@ SHA1Type_copy_impl(SHA1object *self, PyTypeObject *cls) if ((newobj = newSHA1object(st)) == NULL) return NULL; - newobj->hash_state = self->hash_state; + newobj->hash_state = Hacl_Streaming_SHA1_legacy_copy(self->hash_state); return (PyObject *)newobj; } @@ -372,10 +129,7 @@ SHA1Type_digest_impl(SHA1object *self) /*[clinic end generated code: output=2f05302a7aa2b5cb input=13824b35407444bd]*/ { unsigned char digest[SHA1_DIGESTSIZE]; - struct sha1_state temp; - - temp = self->hash_state; - sha1_done(&temp, digest); + Hacl_Streaming_SHA1_legacy_finish(self->hash_state, digest); return PyBytes_FromStringAndSize((const char *)digest, SHA1_DIGESTSIZE); } @@ -390,15 +144,21 @@ SHA1Type_hexdigest_impl(SHA1object *self) /*[clinic end generated code: output=4161fd71e68c6659 input=97691055c0c74ab0]*/ { unsigned char digest[SHA1_DIGESTSIZE]; - struct sha1_state temp; - - /* Get the raw (binary) digest value */ - temp = self->hash_state; - sha1_done(&temp, digest); - + Hacl_Streaming_SHA1_legacy_finish(self->hash_state, digest); return _Py_strhex((const char *)digest, SHA1_DIGESTSIZE); } +static void update(Hacl_Streaming_SHA1_state *state, uint8_t *buf, Py_ssize_t len) { +#if PY_SSIZE_T_MAX > UINT32_MAX + while (len > UINT32_MAX) { + Hacl_Streaming_SHA1_legacy_update(state, buf, UINT32_MAX); + len -= UINT32_MAX; + buf += UINT32_MAX; + } +#endif + Hacl_Streaming_SHA1_legacy_update(state, buf, (uint32_t) len); +} + /*[clinic input] SHA1Type.update @@ -416,7 +176,7 @@ SHA1Type_update(SHA1object *self, PyObject *obj) GET_BUFFER_VIEW_OR_ERROUT(obj, &buf); - sha1_process(&self->hash_state, buf.buf, buf.len); + update(self->hash_state, buf.buf, buf.len); PyBuffer_Release(&buf); Py_RETURN_NONE; @@ -509,7 +269,7 @@ _sha1_sha1_impl(PyObject *module, PyObject *string, int usedforsecurity) return NULL; } - sha1_init(&new->hash_state); + new->hash_state = Hacl_Streaming_SHA1_legacy_create_in(); if (PyErr_Occurred()) { Py_DECREF(new); @@ -518,7 +278,7 @@ _sha1_sha1_impl(PyObject *module, PyObject *string, int usedforsecurity) return NULL; } if (string) { - sha1_process(&new->hash_state, buf.buf, buf.len); + update(new->hash_state, buf.buf, buf.len); PyBuffer_Release(&buf); } diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 222963bc42d17c..85dc8caa458ed9 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -400,12 +400,14 @@ + + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index efb96222043ac2..98e7d59ba1020c 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -848,6 +848,9 @@ Modules + + Modules + Modules @@ -863,6 +866,9 @@ Modules + + Modules + Modules diff --git a/configure b/configure index 17dc62fb63de3b..557519ad86e06d 100755 --- a/configure +++ b/configure @@ -26844,7 +26844,7 @@ fi as_fn_append MODULE_BLOCK "MODULE__MD5_STATE=$py_cv_module__md5$as_nl" if test "x$py_cv_module__md5" = xyes; then : - + as_fn_append MODULE_BLOCK "MODULE__MD5_CFLAGS=-I\$(srcdir)/Modules/_hacl/include -I\$(srcdir)/Modules/_hacl/internal -D_BSD_SOURCE -D_DEFAULT_SOURCE$as_nl" fi @@ -26878,7 +26878,7 @@ fi as_fn_append MODULE_BLOCK "MODULE__SHA1_STATE=$py_cv_module__sha1$as_nl" if test "x$py_cv_module__sha1" = xyes; then : - + as_fn_append MODULE_BLOCK "MODULE__SHA1_CFLAGS=-I\$(srcdir)/Modules/_hacl/include -I\$(srcdir)/Modules/_hacl/internal -D_BSD_SOURCE -D_DEFAULT_SOURCE$as_nl" fi diff --git a/configure.ac b/configure.ac index bc288b86cfa590..982f669acbcfe5 100644 --- a/configure.ac +++ b/configure.ac @@ -7194,8 +7194,12 @@ PY_STDLIB_MOD_SIMPLE([unicodedata]) dnl By default we always compile these even when OpenSSL is available dnl (issue #14693). The modules are small. -PY_STDLIB_MOD([_md5], [test "$with_builtin_md5" = yes]) -PY_STDLIB_MOD([_sha1], [test "$with_builtin_sha1" = yes]) +PY_STDLIB_MOD([_md5], + [test "$with_builtin_md5" = yes], [], + [-I\$(srcdir)/Modules/_hacl/include -I\$(srcdir)/Modules/_hacl/internal -D_BSD_SOURCE -D_DEFAULT_SOURCE]) +PY_STDLIB_MOD([_sha1], + [test "$with_builtin_sha1" = yes], [], + [-I\$(srcdir)/Modules/_hacl/include -I\$(srcdir)/Modules/_hacl/internal -D_BSD_SOURCE -D_DEFAULT_SOURCE]) PY_STDLIB_MOD([_sha2], [test "$with_builtin_sha2" = yes], [], [-I\$(srcdir)/Modules/_hacl/include -I\$(srcdir)/Modules/_hacl/internal -D_BSD_SOURCE -D_DEFAULT_SOURCE]) From 8f647477f0ab5362741d261701b5bcd76bd69ec1 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Wed, 22 Feb 2023 18:55:03 -0500 Subject: [PATCH 164/247] Fix syntax error in struct doc example (#102160) Missing closing ) reported on Discuss by Chukwudi Nwachukwu. --- Doc/library/struct.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/struct.rst b/Doc/library/struct.rst index 69d95f27cb61d9..9c0e32ba16bf68 100644 --- a/Doc/library/struct.rst +++ b/Doc/library/struct.rst @@ -371,7 +371,7 @@ ordering:: >>> from struct import * >>> pack(">bhl", 1, 2, 3) b'\x01\x00\x02\x00\x00\x00\x03' - >>> unpack('>bhl', b'\x01\x00\x02\x00\x00\x00\x03' + >>> unpack('>bhl', b'\x01\x00\x02\x00\x00\x00\x03') (1, 2, 3) >>> calcsize('>bhl') 7 From 056dfc71dce15f81887f0bd6da09d6099d71f979 Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Wed, 22 Feb 2023 18:49:22 -0700 Subject: [PATCH 165/247] gh-87634: remove locking from functools.cached_property (GH-101890) Remove the undocumented locking capabilities of functools.cached_property. --- Doc/library/functools.rst | 16 +++++++++ Doc/whatsnew/3.12.rst | 9 +++++ Lib/functools.py | 27 +++++--------- Lib/test/test_functools.py | 36 ------------------- ...3-02-13-12-55-48.gh-issue-87634.q-SBhJ.rst | 1 + 5 files changed, 35 insertions(+), 54 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-02-13-12-55-48.gh-issue-87634.q-SBhJ.rst diff --git a/Doc/library/functools.rst b/Doc/library/functools.rst index 80a405e87d8d56..d467e50bc7a424 100644 --- a/Doc/library/functools.rst +++ b/Doc/library/functools.rst @@ -86,6 +86,14 @@ The :mod:`functools` module defines the following functions: The cached value can be cleared by deleting the attribute. This allows the *cached_property* method to run again. + The *cached_property* does not prevent a possible race condition in + multi-threaded usage. The getter function could run more than once on the + same instance, with the latest run setting the cached value. If the cached + property is idempotent or otherwise not harmful to run more than once on an + instance, this is fine. If synchronization is needed, implement the necessary + locking inside the decorated getter function or around the cached property + access. + Note, this decorator interferes with the operation of :pep:`412` key-sharing dictionaries. This means that instance dictionaries can take more space than usual. @@ -110,6 +118,14 @@ The :mod:`functools` module defines the following functions: def stdev(self): return statistics.stdev(self._data) + + .. versionchanged:: 3.12 + Prior to Python 3.12, ``cached_property`` included an undocumented lock to + ensure that in multi-threaded usage the getter function was guaranteed to + run only once per instance. However, the lock was per-property, not + per-instance, which could result in unacceptably high lock contention. In + Python 3.12+ this locking is removed. + .. versionadded:: 3.8 diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index c62f462a19a2df..909c9102a405f3 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -761,6 +761,15 @@ Changes in the Python API around process-global resources, which are best managed from the main interpreter. (Contributed by Dong-hee Na in :gh:`99127`.) +* The undocumented locking behavior of :func:`~functools.cached_property` + is removed, because it locked across all instances of the class, leading to high + lock contention. This means that a cached property getter function could now run + more than once for a single instance, if two threads race. For most simple + cached properties (e.g. those that are idempotent and simply calculate a value + based on other attributes of the instance) this will be fine. If + synchronization is needed, implement locking within the cached property getter + function or around multi-threaded access points. + Build Changes ============= diff --git a/Lib/functools.py b/Lib/functools.py index 43ead512e1ea4e..aaf4291150fbbf 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -959,15 +959,12 @@ def __isabstractmethod__(self): ### cached_property() - computed once per instance, cached as attribute ################################################################################ -_NOT_FOUND = object() - class cached_property: def __init__(self, func): self.func = func self.attrname = None self.__doc__ = func.__doc__ - self.lock = RLock() def __set_name__(self, owner, name): if self.attrname is None: @@ -992,21 +989,15 @@ def __get__(self, instance, owner=None): f"instance to cache {self.attrname!r} property." ) raise TypeError(msg) from None - val = cache.get(self.attrname, _NOT_FOUND) - if val is _NOT_FOUND: - with self.lock: - # check if another thread filled cache while we awaited lock - val = cache.get(self.attrname, _NOT_FOUND) - if val is _NOT_FOUND: - val = self.func(instance) - try: - cache[self.attrname] = val - except TypeError: - msg = ( - f"The '__dict__' attribute on {type(instance).__name__!r} instance " - f"does not support item assignment for caching {self.attrname!r} property." - ) - raise TypeError(msg) from None + val = self.func(instance) + try: + cache[self.attrname] = val + except TypeError: + msg = ( + f"The '__dict__' attribute on {type(instance).__name__!r} instance " + f"does not support item assignment for caching {self.attrname!r} property." + ) + raise TypeError(msg) from None return val __class_getitem__ = classmethod(GenericAlias) diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index 730ab1f595f22c..57db96d37ee369 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -2931,21 +2931,6 @@ def get_cost(self): cached_cost = py_functools.cached_property(get_cost) -class CachedCostItemWait: - - def __init__(self, event): - self._cost = 1 - self.lock = py_functools.RLock() - self.event = event - - @py_functools.cached_property - def cost(self): - self.event.wait(1) - with self.lock: - self._cost += 1 - return self._cost - - class CachedCostItemWithSlots: __slots__ = ('_cost') @@ -2970,27 +2955,6 @@ def test_cached_attribute_name_differs_from_func_name(self): self.assertEqual(item.get_cost(), 4) self.assertEqual(item.cached_cost, 3) - @threading_helper.requires_working_threading() - def test_threaded(self): - go = threading.Event() - item = CachedCostItemWait(go) - - num_threads = 3 - - orig_si = sys.getswitchinterval() - sys.setswitchinterval(1e-6) - try: - threads = [ - threading.Thread(target=lambda: item.cost) - for k in range(num_threads) - ] - with threading_helper.start_threads(threads): - go.set() - finally: - sys.setswitchinterval(orig_si) - - self.assertEqual(item.cost, 2) - def test_object_with_slots(self): item = CachedCostItemWithSlots() with self.assertRaisesRegex( diff --git a/Misc/NEWS.d/next/Library/2023-02-13-12-55-48.gh-issue-87634.q-SBhJ.rst b/Misc/NEWS.d/next/Library/2023-02-13-12-55-48.gh-issue-87634.q-SBhJ.rst new file mode 100644 index 00000000000000..a17927500bd9a5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-13-12-55-48.gh-issue-87634.q-SBhJ.rst @@ -0,0 +1 @@ +Remove locking behavior from :func:`functools.cached_property`. From 572223f9ce99e8816abdcc1536db6c4ceed2d848 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Thu, 23 Feb 2023 10:17:44 +0000 Subject: [PATCH 166/247] Revert "bpo-46978: Correct docstrings for in-place builtin operators #31802) (#102146) Revert "bpo-46978: Correct docstrings for in-place builtin operators (#31802)" This reverts commit 128379b8cdb88a6d3d7fed24df082c9a654b3fb8. --- .../Core and Builtins/2022-03-10-21-48-05.bpo-46978.f5QFfw.rst | 1 - Objects/typeobject.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-03-10-21-48-05.bpo-46978.f5QFfw.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-03-10-21-48-05.bpo-46978.f5QFfw.rst b/Misc/NEWS.d/next/Core and Builtins/2022-03-10-21-48-05.bpo-46978.f5QFfw.rst deleted file mode 100644 index 72291d042a0394..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2022-03-10-21-48-05.bpo-46978.f5QFfw.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed docstrings for in-place operators of built-in types. diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 2d1220a0695036..b3f1429debc58b 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -8564,7 +8564,7 @@ an all-zero entry. #NAME "($self, /)\n--\n\n" DOC) #define IBSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \ ETSLOT(NAME, as_number.SLOT, FUNCTION, WRAPPER, \ - #NAME "($self, value, /)\n--\n\nCompute self " DOC " value.") + #NAME "($self, value, /)\n--\n\nReturn self" DOC "value.") #define BINSLOT(NAME, SLOT, FUNCTION, DOC) \ ETSLOT(NAME, as_number.SLOT, FUNCTION, wrap_binaryfunc_l, \ #NAME "($self, value, /)\n--\n\nReturn self" DOC "value.") From 22b8d77b98a5944e688be0927b8139c49d4a7257 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 23 Feb 2023 10:19:01 +0000 Subject: [PATCH 167/247] GH-100719: Remove redundant `gi_code` field from generator object. (GH-100749) --- Include/cpython/genobject.h | 5 +- Include/internal/pycore_frame.h | 2 +- Lib/test/test_capi/test_misc.py | 5 ++ Lib/test/test_sys.py | 2 +- ...-01-04-12-49-33.gh-issue-100719.uRPccL.rst | 3 + Modules/_testcapimodule.c | 11 +++ Objects/genobject.c | 72 +++++++++++++----- Python/ceval.c | 73 +++++++++---------- Python/frame.c | 4 +- 9 files changed, 116 insertions(+), 61 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-04-12-49-33.gh-issue-100719.uRPccL.rst diff --git a/Include/cpython/genobject.h b/Include/cpython/genobject.h index 6127ba7babb80f..18b8ce913e6e31 100644 --- a/Include/cpython/genobject.h +++ b/Include/cpython/genobject.h @@ -13,8 +13,6 @@ extern "C" { and coroutine objects. */ #define _PyGenObject_HEAD(prefix) \ PyObject_HEAD \ - /* The code object backing the generator */ \ - PyCodeObject *prefix##_code; \ /* List of weak reference. */ \ PyObject *prefix##_weakreflist; \ /* Name of the generator. */ \ @@ -28,7 +26,7 @@ extern "C" { char prefix##_running_async; \ /* The frame */ \ int8_t prefix##_frame_state; \ - PyObject *prefix##_iframe[1]; + PyObject *prefix##_iframe[1]; \ typedef struct { /* The gi_ prefix is intended to remind of generator-iterator. */ @@ -46,6 +44,7 @@ PyAPI_FUNC(PyObject *) PyGen_NewWithQualName(PyFrameObject *, PyAPI_FUNC(int) _PyGen_SetStopIterationValue(PyObject *); PyAPI_FUNC(int) _PyGen_FetchStopIterationValue(PyObject **); PyAPI_FUNC(void) _PyGen_Finalize(PyObject *self); +PyAPI_FUNC(PyCodeObject *) PyGen_GetCode(PyGenObject *gen); /* --- PyCoroObject ------------------------------------------------------- */ diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index 81d16b219c305b..5806cf05f174a9 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -209,7 +209,7 @@ _PyFrame_GetFrameObject(_PyInterpreterFrame *frame) * frames like the ones in generators and coroutines. */ void -_PyFrame_Clear(_PyInterpreterFrame * frame); +_PyFrame_ClearExceptCode(_PyInterpreterFrame * frame); int _PyFrame_Traverse(_PyInterpreterFrame *frame, visitproc visit, void *arg); diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index ad099c61463b66..f4569fb005461f 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -1213,6 +1213,11 @@ def test_pendingcalls_non_threaded(self): self.pendingcalls_submit(l, n) self.pendingcalls_wait(l, n) + def test_gen_get_code(self): + def genf(): yield + gen = genf() + self.assertEqual(_testcapi.gen_get_code(gen), gen.gi_code) + class SubinterpreterTest(unittest.TestCase): diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index ab1a0659471857..58aa9d10210edf 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1460,7 +1460,7 @@ def bar(cls): check(bar, size('PP')) # generator def get_gen(): yield 1 - check(get_gen(), size('P2P4P4c7P2ic??2P')) + check(get_gen(), size('PP4P4c7P2ic??2P')) # iterator check(iter('abc'), size('lP')) # callable-iterator diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-04-12-49-33.gh-issue-100719.uRPccL.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-04-12-49-33.gh-issue-100719.uRPccL.rst new file mode 100644 index 00000000000000..2addef27b8ea4d --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-04-12-49-33.gh-issue-100719.uRPccL.rst @@ -0,0 +1,3 @@ +Remove gi_code field from generator (and coroutine and async generator) +objects as it is redundant. The frame already includes a reference to the +code object. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 0d8d1d73fb2390..e2237d25a9a940 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3076,6 +3076,16 @@ eval_get_func_desc(PyObject *self, PyObject *func) return PyUnicode_FromString(PyEval_GetFuncDesc(func)); } +static PyObject * +gen_get_code(PyObject *self, PyObject *gen) +{ + if (!PyGen_Check(gen)) { + PyErr_SetString(PyExc_TypeError, "argument must be a generator object"); + return NULL; + } + return (PyObject *)PyGen_GetCode((PyGenObject *)gen); +} + static PyObject * eval_eval_code_ex(PyObject *mod, PyObject *pos_args) { @@ -3657,6 +3667,7 @@ static PyMethodDef TestMethods[] = { {"frame_getvarstring", test_frame_getvarstring, METH_VARARGS, NULL}, {"eval_get_func_name", eval_get_func_name, METH_O, NULL}, {"eval_get_func_desc", eval_get_func_desc, METH_O, NULL}, + {"gen_get_code", gen_get_code, METH_O, NULL}, {"get_feature_macros", get_feature_macros, METH_NOARGS, NULL}, {"test_code_api", test_code_api, METH_NOARGS, NULL}, {"settrace_to_record", settrace_to_record, METH_O, NULL}, diff --git a/Objects/genobject.c b/Objects/genobject.c index aec64ca7004e11..4ab6581e12ab3a 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -24,6 +24,21 @@ static const char *NON_INIT_CORO_MSG = "can't send non-None value to a " static const char *ASYNC_GEN_IGNORED_EXIT_MSG = "async generator ignored GeneratorExit"; +/* Returns a borrowed reference */ +static inline PyCodeObject * +_PyGen_GetCode(PyGenObject *gen) { + _PyInterpreterFrame *frame = (_PyInterpreterFrame *)(gen->gi_iframe); + return frame->f_code; +} + +PyCodeObject * +PyGen_GetCode(PyGenObject *gen) { + assert(PyGen_Check(gen)); + PyCodeObject *res = _PyGen_GetCode(gen); + Py_INCREF(res); + return res; +} + static inline int exc_state_traverse(_PyErr_StackItem *exc_state, visitproc visit, void *arg) { @@ -34,7 +49,6 @@ exc_state_traverse(_PyErr_StackItem *exc_state, visitproc visit, void *arg) static int gen_traverse(PyGenObject *gen, visitproc visit, void *arg) { - Py_VISIT(gen->gi_code); Py_VISIT(gen->gi_name); Py_VISIT(gen->gi_qualname); if (gen->gi_frame_state < FRAME_CLEARED) { @@ -88,8 +102,8 @@ _PyGen_Finalize(PyObject *self) /* If `gen` is a coroutine, and if it was never awaited on, issue a RuntimeWarning. */ - if (gen->gi_code != NULL && - ((PyCodeObject *)gen->gi_code)->co_flags & CO_COROUTINE && + assert(_PyGen_GetCode(gen) != NULL); + if (_PyGen_GetCode(gen)->co_flags & CO_COROUTINE && gen->gi_frame_state == FRAME_CREATED) { _PyErr_WarnUnawaitedCoroutine((PyObject *)gen); @@ -137,12 +151,12 @@ gen_dealloc(PyGenObject *gen) _PyInterpreterFrame *frame = (_PyInterpreterFrame *)gen->gi_iframe; gen->gi_frame_state = FRAME_CLEARED; frame->previous = NULL; - _PyFrame_Clear(frame); + _PyFrame_ClearExceptCode(frame); } - if (((PyCodeObject *)gen->gi_code)->co_flags & CO_COROUTINE) { + if (_PyGen_GetCode(gen)->co_flags & CO_COROUTINE) { Py_CLEAR(((PyCoroObject *)gen)->cr_origin_or_finalizer); } - Py_CLEAR(gen->gi_code); + Py_DECREF(_PyGen_GetCode(gen)); Py_CLEAR(gen->gi_name); Py_CLEAR(gen->gi_qualname); _PyErr_ClearExcState(&gen->gi_exc_state); @@ -332,7 +346,7 @@ _PyGen_yf(PyGenObject *gen) /* Return immediately if the frame didn't start yet. SEND always come after LOAD_CONST: a code object should not start with SEND */ - assert(_PyCode_CODE(gen->gi_code)[0].op.code != SEND); + assert(_PyCode_CODE(_PyGen_GetCode(gen))[0].op.code != SEND); return NULL; } _Py_CODEUNIT next = frame->prev_instr[1]; @@ -767,6 +781,21 @@ gen_getframe(PyGenObject *gen, void *Py_UNUSED(ignored)) return _gen_getframe(gen, "gi_frame"); } +static PyObject * +_gen_getcode(PyGenObject *gen, const char *const name) +{ + if (PySys_Audit("object.__getattr__", "Os", gen, name) < 0) { + return NULL; + } + return Py_NewRef(_PyGen_GetCode(gen)); +} + +static PyObject * +gen_getcode(PyGenObject *gen, void *Py_UNUSED(ignored)) +{ + return _gen_getcode(gen, "gi_code"); +} + static PyGetSetDef gen_getsetlist[] = { {"__name__", (getter)gen_get_name, (setter)gen_set_name, PyDoc_STR("name of the generator")}, @@ -777,11 +806,11 @@ static PyGetSetDef gen_getsetlist[] = { {"gi_running", (getter)gen_getrunning, NULL, NULL}, {"gi_frame", (getter)gen_getframe, NULL, NULL}, {"gi_suspended", (getter)gen_getsuspended, NULL, NULL}, + {"gi_code", (getter)gen_getcode, NULL, NULL}, {NULL} /* Sentinel */ }; static PyMemberDef gen_memberlist[] = { - {"gi_code", T_OBJECT, offsetof(PyGenObject, gi_code), READONLY|PY_AUDIT_READ}, {NULL} /* Sentinel */ }; @@ -790,7 +819,7 @@ gen_sizeof(PyGenObject *gen, PyObject *Py_UNUSED(ignored)) { Py_ssize_t res; res = offsetof(PyGenObject, gi_iframe) + offsetof(_PyInterpreterFrame, localsplus); - PyCodeObject *code = gen->gi_code; + PyCodeObject *code = _PyGen_GetCode(gen); res += _PyFrame_NumSlotsForCodeObject(code) * sizeof(PyObject *); return PyLong_FromSsize_t(res); } @@ -878,7 +907,6 @@ make_gen(PyTypeObject *type, PyFunctionObject *func) return NULL; } gen->gi_frame_state = FRAME_CLEARED; - gen->gi_code = (PyCodeObject *)Py_NewRef(func->func_code); gen->gi_weakreflist = NULL; gen->gi_exc_state.exc_value = NULL; gen->gi_exc_state.previous_item = NULL; @@ -960,8 +988,6 @@ gen_new_with_qualname(PyTypeObject *type, PyFrameObject *f, f->f_frame = frame; frame->owner = FRAME_OWNED_BY_GENERATOR; assert(PyObject_GC_IsTracked((PyObject *)f)); - gen->gi_code = PyFrame_GetCode(f); - Py_INCREF(gen->gi_code); Py_DECREF(f); gen->gi_weakreflist = NULL; gen->gi_exc_state.exc_value = NULL; @@ -969,11 +995,11 @@ gen_new_with_qualname(PyTypeObject *type, PyFrameObject *f, if (name != NULL) gen->gi_name = Py_NewRef(name); else - gen->gi_name = Py_NewRef(gen->gi_code->co_name); + gen->gi_name = Py_NewRef(_PyGen_GetCode(gen)->co_name); if (qualname != NULL) gen->gi_qualname = Py_NewRef(qualname); else - gen->gi_qualname = Py_NewRef(gen->gi_code->co_qualname); + gen->gi_qualname = Py_NewRef(_PyGen_GetCode(gen)->co_qualname); _PyObject_GC_TRACK(gen); return (PyObject *)gen; } @@ -1001,7 +1027,7 @@ static int gen_is_coroutine(PyObject *o) { if (PyGen_CheckExact(o)) { - PyCodeObject *code = (PyCodeObject *)((PyGenObject*)o)->gi_code; + PyCodeObject *code = _PyGen_GetCode((PyGenObject*)o); if (code->co_flags & CO_ITERABLE_COROUTINE) { return 1; } @@ -1110,6 +1136,12 @@ cr_getframe(PyCoroObject *coro, void *Py_UNUSED(ignored)) return _gen_getframe((PyGenObject *)coro, "cr_frame"); } +static PyObject * +cr_getcode(PyCoroObject *coro, void *Py_UNUSED(ignored)) +{ + return _gen_getcode((PyGenObject *)coro, "cr_code"); +} + static PyGetSetDef coro_getsetlist[] = { {"__name__", (getter)gen_get_name, (setter)gen_set_name, @@ -1120,12 +1152,12 @@ static PyGetSetDef coro_getsetlist[] = { PyDoc_STR("object being awaited on, or None")}, {"cr_running", (getter)cr_getrunning, NULL, NULL}, {"cr_frame", (getter)cr_getframe, NULL, NULL}, + {"cr_code", (getter)cr_getcode, NULL, NULL}, {"cr_suspended", (getter)cr_getsuspended, NULL, NULL}, {NULL} /* Sentinel */ }; static PyMemberDef coro_memberlist[] = { - {"cr_code", T_OBJECT, offsetof(PyCoroObject, cr_code), READONLY|PY_AUDIT_READ}, {"cr_origin", T_OBJECT, offsetof(PyCoroObject, cr_origin_or_finalizer), READONLY}, {NULL} /* Sentinel */ }; @@ -1514,6 +1546,12 @@ ag_getframe(PyAsyncGenObject *ag, void *Py_UNUSED(ignored)) return _gen_getframe((PyGenObject *)ag, "ag_frame"); } +static PyObject * +ag_getcode(PyGenObject *gen, void *Py_UNUSED(ignored)) +{ + return _gen_getcode(gen, "ag__code"); +} + static PyGetSetDef async_gen_getsetlist[] = { {"__name__", (getter)gen_get_name, (setter)gen_set_name, PyDoc_STR("name of the async generator")}, @@ -1522,13 +1560,13 @@ static PyGetSetDef async_gen_getsetlist[] = { {"ag_await", (getter)coro_get_cr_await, NULL, PyDoc_STR("object being awaited on, or None")}, {"ag_frame", (getter)ag_getframe, NULL, NULL}, + {"ag_code", (getter)ag_getcode, NULL, NULL}, {NULL} /* Sentinel */ }; static PyMemberDef async_gen_memberlist[] = { {"ag_running", T_BOOL, offsetof(PyAsyncGenObject, ag_running_async), READONLY}, - {"ag_code", T_OBJECT, offsetof(PyAsyncGenObject, ag_code), READONLY|PY_AUDIT_READ}, {NULL} /* Sentinel */ }; diff --git a/Python/ceval.c b/Python/ceval.c index 001bdb15c0f755..b382d2109b93b7 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1604,41 +1604,6 @@ initialize_locals(PyThreadState *tstate, PyFunctionObject *func, return -1; } -/* Consumes references to func, locals and all the args */ -static _PyInterpreterFrame * -_PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func, - PyObject *locals, PyObject* const* args, - size_t argcount, PyObject *kwnames) -{ - PyCodeObject * code = (PyCodeObject *)func->func_code; - CALL_STAT_INC(frames_pushed); - _PyInterpreterFrame *frame = _PyThreadState_PushFrame(tstate, code->co_framesize); - if (frame == NULL) { - goto fail; - } - _PyFrame_Initialize(frame, func, locals, code, 0); - PyObject **localsarray = &frame->localsplus[0]; - if (initialize_locals(tstate, func, localsarray, args, argcount, kwnames)) { - assert(frame->owner != FRAME_OWNED_BY_GENERATOR); - _PyEvalFrameClearAndPop(tstate, frame); - return NULL; - } - return frame; -fail: - /* Consume the references */ - for (size_t i = 0; i < argcount; i++) { - Py_DECREF(args[i]); - } - if (kwnames) { - Py_ssize_t kwcount = PyTuple_GET_SIZE(kwnames); - for (Py_ssize_t i = 0; i < kwcount; i++) { - Py_DECREF(args[i+argcount]); - } - } - PyErr_NoMemory(); - return NULL; -} - static void clear_thread_frame(PyThreadState *tstate, _PyInterpreterFrame * frame) { @@ -1649,7 +1614,8 @@ clear_thread_frame(PyThreadState *tstate, _PyInterpreterFrame * frame) tstate->datastack_top); tstate->c_recursion_remaining--; assert(frame->frame_obj == NULL || frame->frame_obj->f_frame == frame); - _PyFrame_Clear(frame); + _PyFrame_ClearExceptCode(frame); + Py_DECREF(frame->f_code); tstate->c_recursion_remaining++; _PyThreadState_PopFrame(tstate, frame); } @@ -1665,7 +1631,7 @@ clear_gen_frame(PyThreadState *tstate, _PyInterpreterFrame * frame) gen->gi_exc_state.previous_item = NULL; tstate->c_recursion_remaining--; assert(frame->frame_obj == NULL || frame->frame_obj->f_frame == frame); - _PyFrame_Clear(frame); + _PyFrame_ClearExceptCode(frame); tstate->c_recursion_remaining++; frame->previous = NULL; } @@ -1681,6 +1647,39 @@ _PyEvalFrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame * frame) } } +/* Consumes references to func, locals and all the args */ +static _PyInterpreterFrame * +_PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func, + PyObject *locals, PyObject* const* args, + size_t argcount, PyObject *kwnames) +{ + PyCodeObject * code = (PyCodeObject *)func->func_code; + CALL_STAT_INC(frames_pushed); + _PyInterpreterFrame *frame = _PyThreadState_PushFrame(tstate, code->co_framesize); + if (frame == NULL) { + goto fail; + } + _PyFrame_Initialize(frame, func, locals, code, 0); + if (initialize_locals(tstate, func, frame->localsplus, args, argcount, kwnames)) { + assert(frame->owner == FRAME_OWNED_BY_THREAD); + clear_thread_frame(tstate, frame); + return NULL; + } + return frame; +fail: + /* Consume the references */ + for (size_t i = 0; i < argcount; i++) { + Py_DECREF(args[i]); + } + if (kwnames) { + Py_ssize_t kwcount = PyTuple_GET_SIZE(kwnames); + for (Py_ssize_t i = 0; i < kwcount; i++) { + Py_DECREF(args[i+argcount]); + } + } + PyErr_NoMemory(); + return NULL; +} PyObject * _PyEval_Vector(PyThreadState *tstate, PyFunctionObject *func, diff --git a/Python/frame.c b/Python/frame.c index 6a287d4724051a..b562709ce10fee 100644 --- a/Python/frame.c +++ b/Python/frame.c @@ -84,6 +84,7 @@ take_ownership(PyFrameObject *f, _PyInterpreterFrame *frame) assert(frame->owner != FRAME_OWNED_BY_FRAME_OBJECT); assert(frame->owner != FRAME_CLEARED); Py_ssize_t size = ((char*)&frame->localsplus[frame->stacktop]) - (char *)frame; + Py_INCREF(frame->f_code); memcpy((_PyInterpreterFrame *)f->_f_frame_data, frame, size); frame = (_PyInterpreterFrame *)f->_f_frame_data; f->f_frame = frame; @@ -118,7 +119,7 @@ take_ownership(PyFrameObject *f, _PyInterpreterFrame *frame) } void -_PyFrame_Clear(_PyInterpreterFrame *frame) +_PyFrame_ClearExceptCode(_PyInterpreterFrame *frame) { /* It is the responsibility of the owning generator/coroutine * to have cleared the enclosing generator, if any. */ @@ -144,7 +145,6 @@ _PyFrame_Clear(_PyInterpreterFrame *frame) Py_XDECREF(frame->frame_obj); Py_XDECREF(frame->f_locals); Py_DECREF(frame->f_funcobj); - Py_DECREF(frame->f_code); } int From 5b9573eed43c9a43bf0cf54fe012413e08cce34f Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 23 Feb 2023 13:19:21 +0100 Subject: [PATCH 168/247] gh-101578: Fixup NEWS and add What's New entry for new exception APIs (#102157) --- Doc/whatsnew/3.12.rst | 18 ++++++++++++++++++ ...-02-06-16-14-30.gh-issue-101578.PW5fA9.rst | 19 ++++++++----------- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 909c9102a405f3..e551c5b4fd06a9 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -870,6 +870,19 @@ New Features get a frame variable by its name. (Contributed by Victor Stinner in :gh:`91248`.) +* Add :c:func:`PyErr_GetRaisedException` and :c:func:`PyErr_SetRaisedException` + for saving and restoring the current exception. + These functions return and accept a single exception object, + rather than the triple arguments of the now-deprecated + :c:func:`PyErr_Fetch` and :c:func:`PyErr_Restore`. + This is less error prone and a bit more efficient. + (Contributed by Mark Shannon in :gh:`101578`.) + +* Add :c:func:`PyException_GetArgs` and :c:func:`PyException_SetArgs` + as convenience functions for retrieving and modifying + the :attr:`~BaseException.args` passed to the exception's constructor. + (Contributed by Mark Shannon in :gh:`101578`.) + Porting to Python 3.12 ---------------------- @@ -993,6 +1006,11 @@ Deprecated (Contributed in :gh:`47146` by Petr Viktorin, based on earlier work by Alexander Belopolsky and Matthias Braun.) +* :c:func:`PyErr_Fetch` and :c:func:`PyErr_Restore` are deprecated. + Use :c:func:`PyErr_GetRaisedException` and + :c:func:`PyErr_SetRaisedException` instead. + (Contributed by Mark Shannon in :gh:`101578`.) + Removed ------- diff --git a/Misc/NEWS.d/next/C API/2023-02-06-16-14-30.gh-issue-101578.PW5fA9.rst b/Misc/NEWS.d/next/C API/2023-02-06-16-14-30.gh-issue-101578.PW5fA9.rst index fc694f6e051b53..27294a9e5179c4 100644 --- a/Misc/NEWS.d/next/C API/2023-02-06-16-14-30.gh-issue-101578.PW5fA9.rst +++ b/Misc/NEWS.d/next/C API/2023-02-06-16-14-30.gh-issue-101578.PW5fA9.rst @@ -1,13 +1,10 @@ -Add new C-API functions for saving and restoring the current exception: -``PyErr_GetRaisedException`` and ``PyErr_SetRaisedException``. -These functions take and return a single exception rather than -the triple of ``PyErr_Fetch`` and ``PyErr_Restore``. +Add :c:func:`PyErr_GetRaisedException` and :c:func:`PyErr_SetRaisedException` +for saving and restoring the current exception. +These functions return and accept a single exception object, +rather than the triple arguments of the now-deprecated +:c:func:`PyErr_Fetch` and :c:func:`PyErr_Restore`. This is less error prone and a bit more efficient. -The three arguments forms of saving and restoring the -current exception: ``PyErr_Fetch`` and ``PyErr_Restore`` -are deprecated. - -Also add ``PyException_GetArgs`` and ``PyException_SetArgs`` -as convenience functions to help dealing with -exceptions in the C API. +Add :c:func:`PyException_GetArgs` and :c:func:`PyException_SetArgs` +as convenience functions for retrieving and modifying +the :attr:`~BaseException.args` passed to the exception's constructor. From 9bba8035bd99813203cb3b0de218f9cc3bcdaf2f Mon Sep 17 00:00:00 2001 From: Tanner Firl <105078804+TannerFirl@users.noreply.github.com> Date: Thu, 23 Feb 2023 07:28:33 -0600 Subject: [PATCH 169/247] Fix typo in `Py_GetPythonHome` signature (#102168) --- Doc/c-api/init.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index ad06616eeb0e63..b50ee3b3803e29 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -818,7 +818,7 @@ Process-wide parameters .. deprecated:: 3.11 -.. c:function:: w_char* Py_GetPythonHome() +.. c:function:: wchar_t* Py_GetPythonHome() Return the default "home", that is, the value set by a previous call to :c:func:`Py_SetPythonHome`, or the value of the :envvar:`PYTHONHOME` From 665730d2176aabd05ca5741056aef43189b6f754 Mon Sep 17 00:00:00 2001 From: Zackery Spytz Date: Thu, 23 Feb 2023 06:00:58 -0800 Subject: [PATCH 170/247] bpo-23224: Fix segfaults and multiple leaks in the lzma and bz2 modules (GH-7822) lzma.LZMADecompressor and bz2.BZ2Decompressor objects caused segfaults when their `__init__()` methods were not called. lzma.LZMADecompressor, lzma.LZMACompressor, bz2.BZ2Compressor, and bz2.BZ2Decompressor objects would leak locks and internal buffers when their `__init__()` methods were called multiple times. https://bugs.python.org/issue23224 --- Lib/test/test_bz2.py | 4 + Lib/test/test_lzma.py | 4 + .../2018-06-20-09-12-21.bpo-23224.zxCQ13.rst | 6 + Modules/_bz2module.c | 230 ++++++++++-------- Modules/_lzmamodule.c | 126 +++++----- Modules/clinic/_bz2module.c.h | 75 +++++- Modules/clinic/_lzmamodule.c.h | 18 +- 7 files changed, 288 insertions(+), 175 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2018-06-20-09-12-21.bpo-23224.zxCQ13.rst diff --git a/Lib/test/test_bz2.py b/Lib/test/test_bz2.py index c97ed1cea0d113..e4dd7fc2100b62 100644 --- a/Lib/test/test_bz2.py +++ b/Lib/test/test_bz2.py @@ -844,6 +844,10 @@ def test_refleaks_in___init__(self): bzd.__init__() self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10) + def test_uninitialized_BZ2Decompressor_crash(self): + self.assertEqual(BZ2Decompressor.__new__(BZ2Decompressor). + decompress(bytes()), b'') + class CompressDecompressTest(BaseTest): def testCompress(self): diff --git a/Lib/test/test_lzma.py b/Lib/test/test_lzma.py index 18f474ba2a8bdc..ac53bdda2f1747 100644 --- a/Lib/test/test_lzma.py +++ b/Lib/test/test_lzma.py @@ -380,6 +380,10 @@ def test_refleaks_in_decompressor___init__(self): lzd.__init__() self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10) + def test_uninitialized_LZMADecompressor_crash(self): + self.assertEqual(LZMADecompressor.__new__(LZMADecompressor). + decompress(bytes()), b'') + class CompressDecompressFunctionTestCase(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2018-06-20-09-12-21.bpo-23224.zxCQ13.rst b/Misc/NEWS.d/next/Library/2018-06-20-09-12-21.bpo-23224.zxCQ13.rst new file mode 100644 index 00000000000000..8909753c7f9ee6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-06-20-09-12-21.bpo-23224.zxCQ13.rst @@ -0,0 +1,6 @@ +Fix segfaults when creating :class:`lzma.LZMADecompressor` and +:class:`bz2.BZ2Decompressor` objects without calling ``__init__()``, and fix +leakage of locks and internal buffers when calling the ``__init__()`` +methods of :class:`lzma.LZMADecompressor`, :class:`lzma.LZMACompressor`, +:class:`bz2.BZ2Compressor`, and :class:`bz2.BZ2Decompressor` objects +multiple times. diff --git a/Modules/_bz2module.c b/Modules/_bz2module.c index 9304c13fbed5fc..8e7b8e8078af4e 100644 --- a/Modules/_bz2module.c +++ b/Modules/_bz2module.c @@ -15,6 +15,29 @@ #error "The maximum block size accepted by libbzip2 is UINT32_MAX." #endif +typedef struct { + PyTypeObject *bz2_compressor_type; + PyTypeObject *bz2_decompressor_type; +} _bz2_state; + +static inline _bz2_state * +get_module_state(PyObject *module) +{ + void *state = PyModule_GetState(module); + assert(state != NULL); + return (_bz2_state *)state; +} + +static struct PyModuleDef _bz2module; + +static inline _bz2_state * +find_module_state_by_def(PyTypeObject *type) +{ + PyObject *module = PyType_GetModuleByDef(type, &_bz2module); + assert(module != NULL); + return get_module_state(module); +} + /* On success, return value >= 0 On failure, return -1 */ static inline Py_ssize_t @@ -214,12 +237,14 @@ compress(BZ2Compressor *c, char *data, size_t len, int action) /*[clinic input] module _bz2 -class _bz2.BZ2Compressor "BZ2Compressor *" "&BZ2Compressor_Type" -class _bz2.BZ2Decompressor "BZ2Decompressor *" "&BZ2Decompressor_Type" +class _bz2.BZ2Compressor "BZ2Compressor *" "clinic_state()->bz2_compressor_type" +class _bz2.BZ2Decompressor "BZ2Decompressor *" "clinic_state()->bz2_decompressor_type" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=dc7d7992a79f9cb7]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=92348121632b94c4]*/ +#define clinic_state() (find_module_state_by_def(type)) #include "clinic/_bz2module.c.h" +#undef clinic_state /*[clinic input] _bz2.BZ2Compressor.compress @@ -295,24 +320,43 @@ BZ2_Free(void* ctx, void *ptr) PyMem_RawFree(ptr); } +/*[clinic input] +@classmethod +_bz2.BZ2Compressor.__new__ + + compresslevel: int = 9 + Compression level, as a number between 1 and 9. + / -/* Argument Clinic is not used since the Argument Clinic always want to - check the type which would be wrong here */ -static int -_bz2_BZ2Compressor___init___impl(BZ2Compressor *self, int compresslevel) +Create a compressor object for compressing data incrementally. + +For one-shot compression, use the compress() function instead. +[clinic start generated code]*/ + +static PyObject * +_bz2_BZ2Compressor_impl(PyTypeObject *type, int compresslevel) +/*[clinic end generated code: output=83346c96beaacad7 input=d4500d2a52c8b263]*/ { int bzerror; + BZ2Compressor *self; if (!(1 <= compresslevel && compresslevel <= 9)) { PyErr_SetString(PyExc_ValueError, "compresslevel must be between 1 and 9"); - return -1; + return NULL; + } + + assert(type != NULL && type->tp_alloc != NULL); + self = (BZ2Compressor *)type->tp_alloc(type, 0); + if (self == NULL) { + return NULL; } self->lock = PyThread_allocate_lock(); if (self->lock == NULL) { + Py_DECREF(self); PyErr_SetString(PyExc_MemoryError, "Unable to allocate lock"); - return -1; + return NULL; } self->bzs.opaque = NULL; @@ -322,49 +366,11 @@ _bz2_BZ2Compressor___init___impl(BZ2Compressor *self, int compresslevel) if (catch_bz2_error(bzerror)) goto error; - return 0; + return (PyObject *)self; error: - PyThread_free_lock(self->lock); - self->lock = NULL; - return -1; -} - -PyDoc_STRVAR(_bz2_BZ2Compressor___init____doc__, -"BZ2Compressor(compresslevel=9, /)\n" -"--\n" -"\n" -"Create a compressor object for compressing data incrementally.\n" -"\n" -" compresslevel\n" -" Compression level, as a number between 1 and 9.\n" -"\n" -"For one-shot compression, use the compress() function instead."); - -static int -_bz2_BZ2Compressor___init__(PyObject *self, PyObject *args, PyObject *kwargs) -{ - int return_value = -1; - int compresslevel = 9; - - if (!_PyArg_NoKeywords("BZ2Compressor", kwargs)) { - goto exit; - } - if (!_PyArg_CheckPositional("BZ2Compressor", PyTuple_GET_SIZE(args), 0, 1)) { - goto exit; - } - if (PyTuple_GET_SIZE(args) < 1) { - goto skip_optional; - } - compresslevel = _PyLong_AsInt(PyTuple_GET_ITEM(args, 0)); - if (compresslevel == -1 && PyErr_Occurred()) { - goto exit; - } -skip_optional: - return_value = _bz2_BZ2Compressor___init___impl((BZ2Compressor *)self, compresslevel); - -exit: - return return_value; + Py_DECREF(self); + return NULL; } static void @@ -395,9 +401,8 @@ static PyMethodDef BZ2Compressor_methods[] = { static PyType_Slot bz2_compressor_type_slots[] = { {Py_tp_dealloc, BZ2Compressor_dealloc}, {Py_tp_methods, BZ2Compressor_methods}, - {Py_tp_init, _bz2_BZ2Compressor___init__}, - {Py_tp_new, PyType_GenericNew}, - {Py_tp_doc, (char *)_bz2_BZ2Compressor___init____doc__}, + {Py_tp_new, _bz2_BZ2Compressor}, + {Py_tp_doc, (char *)_bz2_BZ2Compressor__doc__}, {Py_tp_traverse, BZ2Compressor_traverse}, {0, 0} }; @@ -624,28 +629,40 @@ _bz2_BZ2Decompressor_decompress_impl(BZ2Decompressor *self, Py_buffer *data, return result; } -/* Argument Clinic is not used since the Argument Clinic always want to - check the type which would be wrong here */ -static int -_bz2_BZ2Decompressor___init___impl(BZ2Decompressor *self) +/*[clinic input] +@classmethod +_bz2.BZ2Decompressor.__new__ + +Create a decompressor object for decompressing data incrementally. + +For one-shot decompression, use the decompress() function instead. +[clinic start generated code]*/ + +static PyObject * +_bz2_BZ2Decompressor_impl(PyTypeObject *type) +/*[clinic end generated code: output=5150d51ccaab220e input=b87413ce51853528]*/ { + BZ2Decompressor *self; int bzerror; - PyThread_type_lock lock = PyThread_allocate_lock(); - if (lock == NULL) { - PyErr_SetString(PyExc_MemoryError, "Unable to allocate lock"); - return -1; + assert(type != NULL && type->tp_alloc != NULL); + self = (BZ2Decompressor *)type->tp_alloc(type, 0); + if (self == NULL) { + return NULL; } - if (self->lock != NULL) { - PyThread_free_lock(self->lock); + + self->lock = PyThread_allocate_lock(); + if (self->lock == NULL) { + Py_DECREF(self); + PyErr_SetString(PyExc_MemoryError, "Unable to allocate lock"); + return NULL; } - self->lock = lock; self->needs_input = 1; self->bzs_avail_in_real = 0; self->input_buffer = NULL; self->input_buffer_size = 0; - Py_XSETREF(self->unused_data, PyBytes_FromStringAndSize(NULL, 0)); + self->unused_data = PyBytes_FromStringAndSize(NULL, 0); if (self->unused_data == NULL) goto error; @@ -653,40 +670,13 @@ _bz2_BZ2Decompressor___init___impl(BZ2Decompressor *self) if (catch_bz2_error(bzerror)) goto error; - return 0; + return (PyObject *)self; error: - Py_CLEAR(self->unused_data); - PyThread_free_lock(self->lock); - self->lock = NULL; - return -1; -} - -static int -_bz2_BZ2Decompressor___init__(PyObject *self, PyObject *args, PyObject *kwargs) -{ - int return_value = -1; - - if (!_PyArg_NoPositional("BZ2Decompressor", args)) { - goto exit; - } - if (!_PyArg_NoKeywords("BZ2Decompressor", kwargs)) { - goto exit; - } - return_value = _bz2_BZ2Decompressor___init___impl((BZ2Decompressor *)self); - -exit: - return return_value; + Py_DECREF(self); + return NULL; } -PyDoc_STRVAR(_bz2_BZ2Decompressor___init____doc__, -"BZ2Decompressor()\n" -"--\n" -"\n" -"Create a decompressor object for decompressing data incrementally.\n" -"\n" -"For one-shot decompression, use the decompress() function instead."); - static void BZ2Decompressor_dealloc(BZ2Decompressor *self) { @@ -738,10 +728,9 @@ static PyMemberDef BZ2Decompressor_members[] = { static PyType_Slot bz2_decompressor_type_slots[] = { {Py_tp_dealloc, BZ2Decompressor_dealloc}, {Py_tp_methods, BZ2Decompressor_methods}, - {Py_tp_init, _bz2_BZ2Decompressor___init__}, - {Py_tp_doc, (char *)_bz2_BZ2Decompressor___init____doc__}, + {Py_tp_doc, (char *)_bz2_BZ2Decompressor__doc__}, {Py_tp_members, BZ2Decompressor_members}, - {Py_tp_new, PyType_GenericNew}, + {Py_tp_new, _bz2_BZ2Decompressor}, {Py_tp_traverse, BZ2Decompressor_traverse}, {0, 0} }; @@ -762,31 +751,52 @@ static PyType_Spec bz2_decompressor_type_spec = { static int _bz2_exec(PyObject *module) { - PyTypeObject *bz2_compressor_type = (PyTypeObject *)PyType_FromModuleAndSpec(module, + _bz2_state *state = get_module_state(module); + state->bz2_compressor_type = (PyTypeObject *)PyType_FromModuleAndSpec(module, &bz2_compressor_type_spec, NULL); - if (bz2_compressor_type == NULL) { + if (state->bz2_compressor_type == NULL) { return -1; } - int rc = PyModule_AddType(module, bz2_compressor_type); - Py_DECREF(bz2_compressor_type); - if (rc < 0) { + if (PyModule_AddType(module, state->bz2_compressor_type) < 0) { return -1; } - PyTypeObject *bz2_decompressor_type = (PyTypeObject *)PyType_FromModuleAndSpec(module, + state->bz2_decompressor_type = (PyTypeObject *)PyType_FromModuleAndSpec(module, &bz2_decompressor_type_spec, NULL); - if (bz2_decompressor_type == NULL) { + if (state->bz2_decompressor_type == NULL) { return -1; } - rc = PyModule_AddType(module, bz2_decompressor_type); - Py_DECREF(bz2_decompressor_type); - if (rc < 0) { + if (PyModule_AddType(module, state->bz2_decompressor_type) < 0) { return -1; } return 0; } +static int +_bz2_traverse(PyObject *module, visitproc visit, void *arg) +{ + _bz2_state *state = get_module_state(module); + Py_VISIT(state->bz2_compressor_type); + Py_VISIT(state->bz2_decompressor_type); + return 0; +} + +static int +_bz2_clear(PyObject *module) +{ + _bz2_state *state = get_module_state(module); + Py_CLEAR(state->bz2_compressor_type); + Py_CLEAR(state->bz2_decompressor_type); + return 0; +} + +static void +_bz2_free(void *module) +{ + (void)_bz2_clear((PyObject *)module); +} + static struct PyModuleDef_Slot _bz2_slots[] = { {Py_mod_exec, _bz2_exec}, {0, NULL} @@ -795,6 +805,10 @@ static struct PyModuleDef_Slot _bz2_slots[] = { static struct PyModuleDef _bz2module = { .m_base = PyModuleDef_HEAD_INIT, .m_name = "_bz2", + .m_size = sizeof(_bz2_state), + .m_traverse = _bz2_traverse, + .m_clear = _bz2_clear, + .m_free = _bz2_free, .m_slots = _bz2_slots, }; diff --git a/Modules/_lzmamodule.c b/Modules/_lzmamodule.c index b572d8cd909fd1..bccab8639159e7 100644 --- a/Modules/_lzmamodule.c +++ b/Modules/_lzmamodule.c @@ -734,7 +734,8 @@ Compressor_init_raw(_lzma_state *state, lzma_stream *lzs, PyObject *filterspecs) } /*[-clinic input] -_lzma.LZMACompressor.__init__ +@classmethod +_lzma.LZMACompressor.__new__ format: int(c_default="FORMAT_XZ") = FORMAT_XZ The container format to use for the output. This can @@ -765,8 +766,8 @@ the raw compressor does not support preset compression levels. For one-shot compression, use the compress() function instead. [-clinic start generated code]*/ -static int -Compressor_init(Compressor *self, PyObject *args, PyObject *kwargs) +static PyObject * +Compressor_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { static char *arg_names[] = {"format", "check", "preset", "filters", NULL}; int format = FORMAT_XZ; @@ -774,31 +775,37 @@ Compressor_init(Compressor *self, PyObject *args, PyObject *kwargs) uint32_t preset = LZMA_PRESET_DEFAULT; PyObject *preset_obj = Py_None; PyObject *filterspecs = Py_None; - _lzma_state *state = PyType_GetModuleState(Py_TYPE(self)); + Compressor *self; + + _lzma_state *state = PyType_GetModuleState(type); assert(state != NULL); if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|iiOO:LZMACompressor", arg_names, &format, &check, &preset_obj, &filterspecs)) { - return -1; + return NULL; } if (format != FORMAT_XZ && check != -1 && check != LZMA_CHECK_NONE) { PyErr_SetString(PyExc_ValueError, "Integrity checks are only supported by FORMAT_XZ"); - return -1; + return NULL; } if (preset_obj != Py_None && filterspecs != Py_None) { PyErr_SetString(PyExc_ValueError, "Cannot specify both preset and filter chain"); - return -1; + return NULL; } - if (preset_obj != Py_None) { - if (!uint32_converter(preset_obj, &preset)) { - return -1; - } + if (preset_obj != Py_None && !uint32_converter(preset_obj, &preset)) { + return NULL; + } + + assert(type != NULL && type->tp_alloc != NULL); + self = (Compressor *)type->tp_alloc(type, 0); + if (self == NULL) { + return NULL; } self->alloc.opaque = NULL; @@ -808,8 +815,9 @@ Compressor_init(Compressor *self, PyObject *args, PyObject *kwargs) self->lock = PyThread_allocate_lock(); if (self->lock == NULL) { + Py_DECREF(self); PyErr_SetString(PyExc_MemoryError, "Unable to allocate lock"); - return -1; + return NULL; } self->flushed = 0; @@ -819,31 +827,33 @@ Compressor_init(Compressor *self, PyObject *args, PyObject *kwargs) check = LZMA_CHECK_CRC64; } if (Compressor_init_xz(state, &self->lzs, check, preset, filterspecs) != 0) { - break; + goto error; } - return 0; + break; case FORMAT_ALONE: if (Compressor_init_alone(state, &self->lzs, preset, filterspecs) != 0) { - break; + goto error; } - return 0; + break; case FORMAT_RAW: if (Compressor_init_raw(state, &self->lzs, filterspecs) != 0) { - break; + goto error; } - return 0; + break; default: PyErr_Format(PyExc_ValueError, "Invalid container format: %d", format); - break; + goto error; } - PyThread_free_lock(self->lock); - self->lock = NULL; - return -1; + return (PyObject *)self; + +error: + Py_DECREF(self); + return NULL; } static void @@ -902,8 +912,7 @@ PyDoc_STRVAR(Compressor_doc, static PyType_Slot lzma_compressor_type_slots[] = { {Py_tp_dealloc, Compressor_dealloc}, {Py_tp_methods, Compressor_methods}, - {Py_tp_init, Compressor_init}, - {Py_tp_new, PyType_GenericNew}, + {Py_tp_new, Compressor_new}, {Py_tp_doc, (char *)Compressor_doc}, {Py_tp_traverse, Compressor_traverse}, {0, 0} @@ -1165,7 +1174,8 @@ Decompressor_init_raw(_lzma_state *state, lzma_stream *lzs, PyObject *filterspec } /*[clinic input] -_lzma.LZMADecompressor.__init__ +@classmethod +_lzma.LZMADecompressor.__new__ format: int(c_default="FORMAT_AUTO") = FORMAT_AUTO Specifies the container format of the input stream. If this is @@ -1189,54 +1199,57 @@ Create a decompressor object for decompressing data incrementally. For one-shot decompression, use the decompress() function instead. [clinic start generated code]*/ -static int -_lzma_LZMADecompressor___init___impl(Decompressor *self, int format, - PyObject *memlimit, PyObject *filters) -/*[clinic end generated code: output=3e1821f8aa36564c input=81fe684a6c2f8a27]*/ +static PyObject * +_lzma_LZMADecompressor_impl(PyTypeObject *type, int format, + PyObject *memlimit, PyObject *filters) +/*[clinic end generated code: output=2d46d5e70f10bc7f input=ca40cd1cb1202b0d]*/ { + Decompressor *self; const uint32_t decoder_flags = LZMA_TELL_ANY_CHECK | LZMA_TELL_NO_CHECK; uint64_t memlimit_ = UINT64_MAX; lzma_ret lzret; - _lzma_state *state = PyType_GetModuleState(Py_TYPE(self)); + _lzma_state *state = PyType_GetModuleState(type); assert(state != NULL); if (memlimit != Py_None) { if (format == FORMAT_RAW) { PyErr_SetString(PyExc_ValueError, "Cannot specify memory limit with FORMAT_RAW"); - return -1; + return NULL; } memlimit_ = PyLong_AsUnsignedLongLong(memlimit); if (PyErr_Occurred()) { - return -1; + return NULL; } } if (format == FORMAT_RAW && filters == Py_None) { PyErr_SetString(PyExc_ValueError, "Must specify filters for FORMAT_RAW"); - return -1; + return NULL; } else if (format != FORMAT_RAW && filters != Py_None) { PyErr_SetString(PyExc_ValueError, "Cannot specify filters except with FORMAT_RAW"); - return -1; + return NULL; } + assert(type != NULL && type->tp_alloc != NULL); + self = (Decompressor *)type->tp_alloc(type, 0); + if (self == NULL) { + return NULL; + } self->alloc.opaque = NULL; self->alloc.alloc = PyLzma_Malloc; self->alloc.free = PyLzma_Free; self->lzs.allocator = &self->alloc; self->lzs.next_in = NULL; - PyThread_type_lock lock = PyThread_allocate_lock(); - if (lock == NULL) { + self->lock = PyThread_allocate_lock(); + if (self->lock == NULL) { + Py_DECREF(self); PyErr_SetString(PyExc_MemoryError, "Unable to allocate lock"); - return -1; - } - if (self->lock != NULL) { - PyThread_free_lock(self->lock); + return NULL; } - self->lock = lock; self->check = LZMA_CHECK_UNKNOWN; self->needs_input = 1; @@ -1251,43 +1264,43 @@ _lzma_LZMADecompressor___init___impl(Decompressor *self, int format, case FORMAT_AUTO: lzret = lzma_auto_decoder(&self->lzs, memlimit_, decoder_flags); if (catch_lzma_error(state, lzret)) { - break; + goto error; } - return 0; + break; case FORMAT_XZ: lzret = lzma_stream_decoder(&self->lzs, memlimit_, decoder_flags); if (catch_lzma_error(state, lzret)) { - break; + goto error; } - return 0; + break; case FORMAT_ALONE: self->check = LZMA_CHECK_NONE; lzret = lzma_alone_decoder(&self->lzs, memlimit_); if (catch_lzma_error(state, lzret)) { - break; + goto error; } - return 0; + break; case FORMAT_RAW: self->check = LZMA_CHECK_NONE; if (Decompressor_init_raw(state, &self->lzs, filters) == -1) { - break; + goto error; } - return 0; + break; default: PyErr_Format(PyExc_ValueError, "Invalid container format: %d", format); - break; + goto error; } + return (PyObject *)self; + error: - Py_CLEAR(self->unused_data); - PyThread_free_lock(self->lock); - self->lock = NULL; - return -1; + Py_DECREF(self); + return NULL; } static void @@ -1345,9 +1358,8 @@ static PyMemberDef Decompressor_members[] = { static PyType_Slot lzma_decompressor_type_slots[] = { {Py_tp_dealloc, Decompressor_dealloc}, {Py_tp_methods, Decompressor_methods}, - {Py_tp_init, _lzma_LZMADecompressor___init__}, - {Py_tp_new, PyType_GenericNew}, - {Py_tp_doc, (char *)_lzma_LZMADecompressor___init____doc__}, + {Py_tp_new, _lzma_LZMADecompressor}, + {Py_tp_doc, (char *)_lzma_LZMADecompressor__doc__}, {Py_tp_traverse, Decompressor_traverse}, {Py_tp_members, Decompressor_members}, {0, 0} diff --git a/Modules/clinic/_bz2module.c.h b/Modules/clinic/_bz2module.c.h index 50a48b0bf2b825..d7797d639ae32e 100644 --- a/Modules/clinic/_bz2module.c.h +++ b/Modules/clinic/_bz2module.c.h @@ -71,6 +71,48 @@ _bz2_BZ2Compressor_flush(BZ2Compressor *self, PyObject *Py_UNUSED(ignored)) return _bz2_BZ2Compressor_flush_impl(self); } +PyDoc_STRVAR(_bz2_BZ2Compressor__doc__, +"BZ2Compressor(compresslevel=9, /)\n" +"--\n" +"\n" +"Create a compressor object for compressing data incrementally.\n" +"\n" +" compresslevel\n" +" Compression level, as a number between 1 and 9.\n" +"\n" +"For one-shot compression, use the compress() function instead."); + +static PyObject * +_bz2_BZ2Compressor_impl(PyTypeObject *type, int compresslevel); + +static PyObject * +_bz2_BZ2Compressor(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + PyTypeObject *base_tp = clinic_state()->bz2_compressor_type; + int compresslevel = 9; + + if ((type == base_tp || type->tp_init == base_tp->tp_init) && + !_PyArg_NoKeywords("BZ2Compressor", kwargs)) { + goto exit; + } + if (!_PyArg_CheckPositional("BZ2Compressor", PyTuple_GET_SIZE(args), 0, 1)) { + goto exit; + } + if (PyTuple_GET_SIZE(args) < 1) { + goto skip_optional; + } + compresslevel = _PyLong_AsInt(PyTuple_GET_ITEM(args, 0)); + if (compresslevel == -1 && PyErr_Occurred()) { + goto exit; + } +skip_optional: + return_value = _bz2_BZ2Compressor_impl(type, compresslevel); + +exit: + return return_value; +} + PyDoc_STRVAR(_bz2_BZ2Decompressor_decompress__doc__, "decompress($self, /, data, max_length=-1)\n" "--\n" @@ -168,4 +210,35 @@ _bz2_BZ2Decompressor_decompress(BZ2Decompressor *self, PyObject *const *args, Py return return_value; } -/*[clinic end generated code: output=829bed4097cf2e63 input=a9049054013a1b77]*/ + +PyDoc_STRVAR(_bz2_BZ2Decompressor__doc__, +"BZ2Decompressor()\n" +"--\n" +"\n" +"Create a decompressor object for decompressing data incrementally.\n" +"\n" +"For one-shot decompression, use the decompress() function instead."); + +static PyObject * +_bz2_BZ2Decompressor_impl(PyTypeObject *type); + +static PyObject * +_bz2_BZ2Decompressor(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + PyTypeObject *base_tp = clinic_state()->bz2_decompressor_type; + + if ((type == base_tp || type->tp_init == base_tp->tp_init) && + !_PyArg_NoPositional("BZ2Decompressor", args)) { + goto exit; + } + if ((type == base_tp || type->tp_init == base_tp->tp_init) && + !_PyArg_NoKeywords("BZ2Decompressor", kwargs)) { + goto exit; + } + return_value = _bz2_BZ2Decompressor_impl(type); + +exit: + return return_value; +} +/*[clinic end generated code: output=805400e4805098ec input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_lzmamodule.c.h b/Modules/clinic/_lzmamodule.c.h index 286d2b0070659f..9b396a56683921 100644 --- a/Modules/clinic/_lzmamodule.c.h +++ b/Modules/clinic/_lzmamodule.c.h @@ -169,7 +169,7 @@ _lzma_LZMADecompressor_decompress(Decompressor *self, PyObject *const *args, Py_ return return_value; } -PyDoc_STRVAR(_lzma_LZMADecompressor___init____doc__, +PyDoc_STRVAR(_lzma_LZMADecompressor__doc__, "LZMADecompressor(format=FORMAT_AUTO, memlimit=None, filters=None)\n" "--\n" "\n" @@ -192,14 +192,14 @@ PyDoc_STRVAR(_lzma_LZMADecompressor___init____doc__, "\n" "For one-shot decompression, use the decompress() function instead."); -static int -_lzma_LZMADecompressor___init___impl(Decompressor *self, int format, - PyObject *memlimit, PyObject *filters); +static PyObject * +_lzma_LZMADecompressor_impl(PyTypeObject *type, int format, + PyObject *memlimit, PyObject *filters); -static int -_lzma_LZMADecompressor___init__(PyObject *self, PyObject *args, PyObject *kwargs) +static PyObject * +_lzma_LZMADecompressor(PyTypeObject *type, PyObject *args, PyObject *kwargs) { - int return_value = -1; + PyObject *return_value = NULL; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) #define NUM_KEYWORDS 3 @@ -257,7 +257,7 @@ _lzma_LZMADecompressor___init__(PyObject *self, PyObject *args, PyObject *kwargs } filters = fastargs[2]; skip_optional_pos: - return_value = _lzma_LZMADecompressor___init___impl((Decompressor *)self, format, memlimit, filters); + return_value = _lzma_LZMADecompressor_impl(type, format, memlimit, filters); exit: return return_value; @@ -338,4 +338,4 @@ _lzma__decode_filter_properties(PyObject *module, PyObject *const *args, Py_ssiz return return_value; } -/*[clinic end generated code: output=da3e83ba97244044 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=96c1fbdada1ef232 input=a9049054013a1b77]*/ From c3a178398c199038f3a0891d09f0363ec73f3b38 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 23 Feb 2023 15:09:51 +0100 Subject: [PATCH 171/247] gh-102151: Correctly fetch CONFIG_ARGS in Tools/freeze/test/freeze.py (#102152) --- Tools/freeze/test/freeze.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/freeze/test/freeze.py b/Tools/freeze/test/freeze.py index b4c76ff36a873b..f6a5adb4519fdb 100644 --- a/Tools/freeze/test/freeze.py +++ b/Tools/freeze/test/freeze.py @@ -153,7 +153,7 @@ def prepare(script=None, outdir=None): print(f'configuring python in {builddir}...') cmd = [ os.path.join(srcdir, 'configure'), - *shlex.split(get_config_var(builddir, 'CONFIG_ARGS') or ''), + *shlex.split(get_config_var(srcdir, 'CONFIG_ARGS') or ''), ] ensure_opt(cmd, 'cache-file', os.path.join(outdir, 'python-config.cache')) prefix = os.path.join(outdir, 'python-installation') From e07b304bb004e1298283c82bd135dd5ef96a90cc Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 23 Feb 2023 16:02:23 +0100 Subject: [PATCH 172/247] gh-101981: Consolidate macOS configure steps in CI (GH-102131) Automerge-Triggered-By: GH:erlend-aasland --- .github/workflows/build.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eec11e25a7c7f6..2241b0b8aa409e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -162,13 +162,11 @@ jobs: - uses: actions/checkout@v3 - name: Install Homebrew dependencies run: brew install pkg-config openssl@1.1 xz gdbm tcl-tk - - name: Prepare Homebrew environment variables - run: | - echo "CFLAGS=-I$(brew --prefix gdbm)/include -I$(brew --prefix xz)/include" >> $GITHUB_ENV - echo "LDFLAGS=-L$(brew --prefix gdbm)/lib -I$(brew --prefix xz)/lib" >> $GITHUB_ENV - echo "PKG_CONFIG_PATH=$(brew --prefix openssl@1.1)/lib/pkgconfig:$(brew --prefix tcl-tk)/lib/pkgconfig" >> $GITHUB_ENV - name: Configure CPython run: | + CFLAGS="-I$(brew --prefix gdbm)/include -I$(brew --prefix xz)/include" \ + LDFLAGS="-L$(brew --prefix gdbm)/lib -I$(brew --prefix xz)/lib" \ + PKG_CONFIG_PATH="$(brew --prefix tcl-tk)/lib/pkgconfig" \ ./configure \ --with-pydebug \ --prefix=/opt/python-dev \ From efc985a714b6f43c43ae629183f95618054422ae Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 23 Feb 2023 16:03:13 +0100 Subject: [PATCH 173/247] gh-93649: Split exception tests from _testcapimodule.c (GH-102173) Automerge-Triggered-By: GH:erlend-aasland --- Lib/test/test_capi/test_exceptions.py | 145 +++++++++++++ Lib/test/test_capi/test_misc.py | 131 +----------- Modules/Setup.stdlib.in | 2 +- Modules/_testcapi/exceptions.c | 277 ++++++++++++++++++++++++ Modules/_testcapi/parts.h | 1 + Modules/_testcapimodule.c | 290 +------------------------- PCbuild/_testcapi.vcxproj | 1 + PCbuild/_testcapi.vcxproj.filters | 3 + 8 files changed, 434 insertions(+), 416 deletions(-) create mode 100644 Lib/test/test_capi/test_exceptions.py create mode 100644 Modules/_testcapi/exceptions.c diff --git a/Lib/test/test_capi/test_exceptions.py b/Lib/test/test_capi/test_exceptions.py new file mode 100644 index 00000000000000..b543a1a565a56f --- /dev/null +++ b/Lib/test/test_capi/test_exceptions.py @@ -0,0 +1,145 @@ +import re +import sys +import unittest + +from test import support +from test.support import import_helper +from test.support.script_helper import assert_python_failure + +from .test_misc import decode_stderr + +# Skip this test if the _testcapi module isn't available. +_testcapi = import_helper.import_module('_testcapi') + +class Test_Exceptions(unittest.TestCase): + + def test_exception(self): + raised_exception = ValueError("5") + new_exc = TypeError("TEST") + try: + raise raised_exception + except ValueError as e: + orig_sys_exception = sys.exception() + orig_exception = _testcapi.set_exception(new_exc) + new_sys_exception = sys.exception() + new_exception = _testcapi.set_exception(orig_exception) + reset_sys_exception = sys.exception() + + self.assertEqual(orig_exception, e) + + self.assertEqual(orig_exception, raised_exception) + self.assertEqual(orig_sys_exception, orig_exception) + self.assertEqual(reset_sys_exception, orig_exception) + self.assertEqual(new_exception, new_exc) + self.assertEqual(new_sys_exception, new_exception) + else: + self.fail("Exception not raised") + + def test_exc_info(self): + raised_exception = ValueError("5") + new_exc = TypeError("TEST") + try: + raise raised_exception + except ValueError as e: + tb = e.__traceback__ + orig_sys_exc_info = sys.exc_info() + orig_exc_info = _testcapi.set_exc_info(new_exc.__class__, new_exc, None) + new_sys_exc_info = sys.exc_info() + new_exc_info = _testcapi.set_exc_info(*orig_exc_info) + reset_sys_exc_info = sys.exc_info() + + self.assertEqual(orig_exc_info[1], e) + + self.assertSequenceEqual(orig_exc_info, (raised_exception.__class__, raised_exception, tb)) + self.assertSequenceEqual(orig_sys_exc_info, orig_exc_info) + self.assertSequenceEqual(reset_sys_exc_info, orig_exc_info) + self.assertSequenceEqual(new_exc_info, (new_exc.__class__, new_exc, None)) + self.assertSequenceEqual(new_sys_exc_info, new_exc_info) + else: + self.assertTrue(False) + + +class Test_FatalError(unittest.TestCase): + + def check_fatal_error(self, code, expected, not_expected=()): + with support.SuppressCrashReport(): + rc, out, err = assert_python_failure('-sSI', '-c', code) + + err = decode_stderr(err) + self.assertIn('Fatal Python error: test_fatal_error: MESSAGE\n', + err) + + match = re.search(r'^Extension modules:(.*) \(total: ([0-9]+)\)$', + err, re.MULTILINE) + if not match: + self.fail(f"Cannot find 'Extension modules:' in {err!r}") + modules = set(match.group(1).strip().split(', ')) + total = int(match.group(2)) + + for name in expected: + self.assertIn(name, modules) + for name in not_expected: + self.assertNotIn(name, modules) + self.assertEqual(len(modules), total) + + @support.requires_subprocess() + def test_fatal_error(self): + # By default, stdlib extension modules are ignored, + # but not test modules. + expected = ('_testcapi',) + not_expected = ('sys',) + code = 'import _testcapi, sys; _testcapi.fatal_error(b"MESSAGE")' + self.check_fatal_error(code, expected, not_expected) + + # Mark _testcapi as stdlib module, but not sys + expected = ('sys',) + not_expected = ('_testcapi',) + code = """if True: + import _testcapi, sys + sys.stdlib_module_names = frozenset({"_testcapi"}) + _testcapi.fatal_error(b"MESSAGE") + """ + self.check_fatal_error(code, expected) + + +class Test_ErrSetAndRestore(unittest.TestCase): + + def test_err_set_raised(self): + with self.assertRaises(ValueError): + _testcapi.err_set_raised(ValueError()) + v = ValueError() + try: + _testcapi.err_set_raised(v) + except ValueError as ex: + self.assertIs(v, ex) + + def test_err_restore(self): + with self.assertRaises(ValueError): + _testcapi.err_restore(ValueError) + with self.assertRaises(ValueError): + _testcapi.err_restore(ValueError, 1) + with self.assertRaises(ValueError): + _testcapi.err_restore(ValueError, 1, None) + with self.assertRaises(ValueError): + _testcapi.err_restore(ValueError, ValueError()) + try: + _testcapi.err_restore(KeyError, "hi") + except KeyError as k: + self.assertEqual("hi", k.args[0]) + try: + 1/0 + except Exception as e: + tb = e.__traceback__ + with self.assertRaises(ValueError): + _testcapi.err_restore(ValueError, 1, tb) + with self.assertRaises(TypeError): + _testcapi.err_restore(ValueError, 1, 0) + try: + _testcapi.err_restore(ValueError, 1, tb) + except ValueError as v: + self.assertEqual(1, v.args[0]) + self.assertIs(tb, v.__traceback__.tb_next) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index f4569fb005461f..c34ee578b5c83f 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -8,7 +8,6 @@ import os import pickle import random -import re import subprocess import sys import textwrap @@ -91,51 +90,6 @@ def test_no_FatalError_infinite_loop(self): def test_memoryview_from_NULL_pointer(self): self.assertRaises(ValueError, _testcapi.make_memoryview_from_NULL_pointer) - def test_exception(self): - raised_exception = ValueError("5") - new_exc = TypeError("TEST") - try: - raise raised_exception - except ValueError as e: - orig_sys_exception = sys.exception() - orig_exception = _testcapi.set_exception(new_exc) - new_sys_exception = sys.exception() - new_exception = _testcapi.set_exception(orig_exception) - reset_sys_exception = sys.exception() - - self.assertEqual(orig_exception, e) - - self.assertEqual(orig_exception, raised_exception) - self.assertEqual(orig_sys_exception, orig_exception) - self.assertEqual(reset_sys_exception, orig_exception) - self.assertEqual(new_exception, new_exc) - self.assertEqual(new_sys_exception, new_exception) - else: - self.fail("Exception not raised") - - def test_exc_info(self): - raised_exception = ValueError("5") - new_exc = TypeError("TEST") - try: - raise raised_exception - except ValueError as e: - tb = e.__traceback__ - orig_sys_exc_info = sys.exc_info() - orig_exc_info = _testcapi.set_exc_info(new_exc.__class__, new_exc, None) - new_sys_exc_info = sys.exc_info() - new_exc_info = _testcapi.set_exc_info(*orig_exc_info) - reset_sys_exc_info = sys.exc_info() - - self.assertEqual(orig_exc_info[1], e) - - self.assertSequenceEqual(orig_exc_info, (raised_exception.__class__, raised_exception, tb)) - self.assertSequenceEqual(orig_sys_exc_info, orig_exc_info) - self.assertSequenceEqual(reset_sys_exc_info, orig_exc_info) - self.assertSequenceEqual(new_exc_info, (new_exc.__class__, new_exc, None)) - self.assertSequenceEqual(new_sys_exc_info, new_exc_info) - else: - self.assertTrue(False) - @unittest.skipUnless(_posixsubprocess, '_posixsubprocess required for this test.') def test_seq_bytes_to_charp_array(self): # Issue #15732: crash in _PySequence_BytesToCharpArray() @@ -837,46 +791,6 @@ def __index__(self): self.assertRaises(TypeError, pynumber_tobase, '123', 10) self.assertRaises(SystemError, pynumber_tobase, 123, 0) - def check_fatal_error(self, code, expected, not_expected=()): - with support.SuppressCrashReport(): - rc, out, err = assert_python_failure('-sSI', '-c', code) - - err = decode_stderr(err) - self.assertIn('Fatal Python error: test_fatal_error: MESSAGE\n', - err) - - match = re.search(r'^Extension modules:(.*) \(total: ([0-9]+)\)$', - err, re.MULTILINE) - if not match: - self.fail(f"Cannot find 'Extension modules:' in {err!r}") - modules = set(match.group(1).strip().split(', ')) - total = int(match.group(2)) - - for name in expected: - self.assertIn(name, modules) - for name in not_expected: - self.assertNotIn(name, modules) - self.assertEqual(len(modules), total) - - @support.requires_subprocess() - def test_fatal_error(self): - # By default, stdlib extension modules are ignored, - # but not test modules. - expected = ('_testcapi',) - not_expected = ('sys',) - code = 'import _testcapi, sys; _testcapi.fatal_error(b"MESSAGE")' - self.check_fatal_error(code, expected, not_expected) - - # Mark _testcapi as stdlib module, but not sys - expected = ('sys',) - not_expected = ('_testcapi',) - code = textwrap.dedent(''' - import _testcapi, sys - sys.stdlib_module_names = frozenset({"_testcapi"}) - _testcapi.fatal_error(b"MESSAGE") - ''') - self.check_fatal_error(code, expected) - def test_pyobject_repr_from_null(self): s = _testcapi.pyobject_repr_from_null() self.assertEqual(s, '') @@ -1214,9 +1128,9 @@ def test_pendingcalls_non_threaded(self): self.pendingcalls_wait(l, n) def test_gen_get_code(self): - def genf(): yield - gen = genf() - self.assertEqual(_testcapi.gen_get_code(gen), gen.gi_code) + def genf(): yield + gen = genf() + self.assertEqual(_testcapi.gen_get_code(gen), gen.gi_code) class SubinterpreterTest(unittest.TestCase): @@ -1641,44 +1555,5 @@ def func2(x=None): self.do_test(func2) -class Test_ErrSetAndRestore(unittest.TestCase): - - def test_err_set_raised(self): - with self.assertRaises(ValueError): - _testcapi.err_set_raised(ValueError()) - v = ValueError() - try: - _testcapi.err_set_raised(v) - except ValueError as ex: - self.assertIs(v, ex) - - def test_err_restore(self): - with self.assertRaises(ValueError): - _testcapi.err_restore(ValueError) - with self.assertRaises(ValueError): - _testcapi.err_restore(ValueError, 1) - with self.assertRaises(ValueError): - _testcapi.err_restore(ValueError, 1, None) - with self.assertRaises(ValueError): - _testcapi.err_restore(ValueError, ValueError()) - try: - _testcapi.err_restore(KeyError, "hi") - except KeyError as k: - self.assertEqual("hi", k.args[0]) - try: - 1/0 - except Exception as e: - tb = e.__traceback__ - with self.assertRaises(ValueError): - _testcapi.err_restore(ValueError, 1, tb) - with self.assertRaises(TypeError): - _testcapi.err_restore(ValueError, 1, 0) - try: - _testcapi.err_restore(ValueError, 1, tb) - except ValueError as v: - self.assertEqual(1, v.args[0]) - self.assertIs(tb, v.__traceback__.tb_next) - - if __name__ == "__main__": unittest.main() diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index d33cd82239995f..7551e5b349430e 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -169,7 +169,7 @@ @MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c @MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c @MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c -@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/unicode.c _testcapi/getargs.c _testcapi/pytime.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/structmember.c +@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/unicode.c _testcapi/getargs.c _testcapi/pytime.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/structmember.c _testcapi/exceptions.c @MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c # Some testing modules MUST be built as shared libraries. diff --git a/Modules/_testcapi/exceptions.c b/Modules/_testcapi/exceptions.c new file mode 100644 index 00000000000000..43b88ccf261d98 --- /dev/null +++ b/Modules/_testcapi/exceptions.c @@ -0,0 +1,277 @@ +#include "parts.h" + +static PyObject * +err_set_raised(PyObject *self, PyObject *exc) +{ + Py_INCREF(exc); + PyErr_SetRaisedException(exc); + assert(PyErr_Occurred()); + return NULL; +} + +static PyObject * +err_restore(PyObject *self, PyObject *args) { + PyObject *type = NULL, *value = NULL, *traceback = NULL; + switch(PyTuple_Size(args)) { + case 3: + traceback = PyTuple_GetItem(args, 2); + Py_INCREF(traceback); + /* fall through */ + case 2: + value = PyTuple_GetItem(args, 1); + Py_INCREF(value); + /* fall through */ + case 1: + type = PyTuple_GetItem(args, 0); + Py_INCREF(type); + break; + default: + PyErr_SetString(PyExc_TypeError, + "wrong number of arguments"); + return NULL; + } + PyErr_Restore(type, value, traceback); + assert(PyErr_Occurred()); + return NULL; +} + +/* To test the format of exceptions as printed out. */ +static PyObject * +exception_print(PyObject *self, PyObject *args) +{ + PyObject *value; + PyObject *tb = NULL; + + if (!PyArg_ParseTuple(args, "O:exception_print", &value)) { + return NULL; + } + + if (PyExceptionInstance_Check(value)) { + tb = PyException_GetTraceback(value); + } + + PyErr_Display((PyObject *) Py_TYPE(value), value, tb); + Py_XDECREF(tb); + + Py_RETURN_NONE; +} + +/* Test PyErr_NewExceptionWithDoc (also exercise PyErr_NewException). + Run via Lib/test/test_exceptions.py */ +static PyObject * +make_exception_with_doc(PyObject *self, PyObject *args, PyObject *kwargs) +{ + const char *name; + const char *doc = NULL; + PyObject *base = NULL; + PyObject *dict = NULL; + + static char *kwlist[] = {"name", "doc", "base", "dict", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, + "s|sOO:make_exception_with_doc", kwlist, + &name, &doc, &base, &dict)) + { + return NULL; + } + + return PyErr_NewExceptionWithDoc(name, doc, base, dict); +} + +static PyObject * +raise_exception(PyObject *self, PyObject *args) +{ + PyObject *exc; + int num_args; + + if (!PyArg_ParseTuple(args, "Oi:raise_exception", &exc, &num_args)) { + return NULL; + } + + PyObject *exc_args = PyTuple_New(num_args); + if (exc_args == NULL) { + return NULL; + } + for (int i = 0; i < num_args; ++i) { + PyObject *v = PyLong_FromLong(i); + if (v == NULL) { + Py_DECREF(exc_args); + return NULL; + } + PyTuple_SET_ITEM(exc_args, i, v); + } + PyErr_SetObject(exc, exc_args); + Py_DECREF(exc_args); + return NULL; +} + +/* reliably raise a MemoryError */ +static PyObject * +raise_memoryerror(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return PyErr_NoMemory(); +} + +static PyObject * +test_fatal_error(PyObject *self, PyObject *args) +{ + char *message; + int release_gil = 0; + if (!PyArg_ParseTuple(args, "y|i:fatal_error", &message, &release_gil)) { + return NULL; + } + if (release_gil) { + Py_BEGIN_ALLOW_THREADS + Py_FatalError(message); + Py_END_ALLOW_THREADS + } + else { + Py_FatalError(message); + } + // Py_FatalError() does not return, but exits the process. + Py_RETURN_NONE; +} + +static PyObject * +test_set_exc_info(PyObject *self, PyObject *args) +{ + PyObject *new_type, *new_value, *new_tb; + PyObject *type, *value, *tb; + if (!PyArg_ParseTuple(args, "OOO:test_set_exc_info", + &new_type, &new_value, &new_tb)) + { + return NULL; + } + + PyErr_GetExcInfo(&type, &value, &tb); + + Py_INCREF(new_type); + Py_INCREF(new_value); + Py_INCREF(new_tb); + PyErr_SetExcInfo(new_type, new_value, new_tb); + + PyObject *orig_exc = PyTuple_Pack(3, + type ? type : Py_None, + value ? value : Py_None, + tb ? tb : Py_None); + Py_XDECREF(type); + Py_XDECREF(value); + Py_XDECREF(tb); + return orig_exc; +} + +static PyObject * +test_set_exception(PyObject *self, PyObject *new_exc) +{ + PyObject *exc = PyErr_GetHandledException(); + assert(PyExceptionInstance_Check(exc) || exc == NULL); + + PyErr_SetHandledException(new_exc); + return exc; +} + +static PyObject * +test_write_unraisable_exc(PyObject *self, PyObject *args) +{ + PyObject *exc, *err_msg, *obj; + if (!PyArg_ParseTuple(args, "OOO", &exc, &err_msg, &obj)) { + return NULL; + } + + const char *err_msg_utf8; + if (err_msg != Py_None) { + err_msg_utf8 = PyUnicode_AsUTF8(err_msg); + if (err_msg_utf8 == NULL) { + return NULL; + } + } + else { + err_msg_utf8 = NULL; + } + + PyErr_SetObject((PyObject *)Py_TYPE(exc), exc); + _PyErr_WriteUnraisableMsg(err_msg_utf8, obj); + Py_RETURN_NONE; +} + +/* To test the format of tracebacks as printed out. */ +static PyObject * +traceback_print(PyObject *self, PyObject *args) +{ + PyObject *file; + PyObject *traceback; + + if (!PyArg_ParseTuple(args, "OO:traceback_print", + &traceback, &file)) + { + return NULL; + } + + if (PyTraceBack_Print(traceback, file) < 0) { + return NULL; + } + Py_RETURN_NONE; +} + + +/* + * Define the PyRecurdingInfinitelyError_Type + */ + +static PyTypeObject PyRecursingInfinitelyError_Type; + +static int +recurse_infinitely_error_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + PyObject *type = (PyObject *)&PyRecursingInfinitelyError_Type; + + /* Instantiating this exception starts infinite recursion. */ + Py_INCREF(type); + PyErr_SetObject(type, NULL); + return -1; +} + +static PyTypeObject PyRecursingInfinitelyError_Type = { + .tp_name = "RecursingInfinitelyError", + .tp_basicsize = sizeof(PyBaseExceptionObject), + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .tp_doc = PyDoc_STR("Instantiating this exception starts infinite recursion."), + .tp_init = (initproc)recurse_infinitely_error_init, +}; + +static PyMethodDef test_methods[] = { + {"err_restore", err_restore, METH_VARARGS}, + {"err_set_raised", err_set_raised, METH_O}, + {"exception_print", exception_print, METH_VARARGS}, + {"fatal_error", test_fatal_error, METH_VARARGS, + PyDoc_STR("fatal_error(message, release_gil=False): call Py_FatalError(message)")}, + {"make_exception_with_doc", _PyCFunction_CAST(make_exception_with_doc), + METH_VARARGS | METH_KEYWORDS}, + {"raise_exception", raise_exception, METH_VARARGS}, + {"raise_memoryerror", raise_memoryerror, METH_NOARGS}, + {"set_exc_info", test_set_exc_info, METH_VARARGS}, + {"set_exception", test_set_exception, METH_O}, + {"traceback_print", traceback_print, METH_VARARGS}, + {"write_unraisable_exc", test_write_unraisable_exc, METH_VARARGS}, + {NULL}, +}; + +int +_PyTestCapi_Init_Exceptions(PyObject *mod) +{ + PyRecursingInfinitelyError_Type.tp_base = (PyTypeObject *)PyExc_Exception; + if (PyType_Ready(&PyRecursingInfinitelyError_Type) < 0) { + return -1; + } + if (PyModule_AddObjectRef(mod, "RecursingInfinitelyError", + (PyObject *)&PyRecursingInfinitelyError_Type) < 0) + { + return -1; + } + + if (PyModule_AddFunctions(mod, test_methods) < 0) { + return -1; + } + + return 0; +} diff --git a/Modules/_testcapi/parts.h b/Modules/_testcapi/parts.h index 7ba3c4ebff8cde..1689f186b833f6 100644 --- a/Modules/_testcapi/parts.h +++ b/Modules/_testcapi/parts.h @@ -36,6 +36,7 @@ int _PyTestCapi_Init_Watchers(PyObject *module); int _PyTestCapi_Init_Long(PyObject *module); int _PyTestCapi_Init_Float(PyObject *module); int _PyTestCapi_Init_Structmember(PyObject *module); +int _PyTestCapi_Init_Exceptions(PyObject *module); #ifdef LIMITED_API_AVAILABLE int _PyTestCapi_Init_VectorcallLimited(PyObject *module); diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index e2237d25a9a940..6bb424282a875d 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -720,33 +720,6 @@ pyobject_bytes_from_null(PyObject *self, PyObject *Py_UNUSED(ignored)) return PyObject_Bytes(NULL); } -static PyObject * -raise_exception(PyObject *self, PyObject *args) -{ - PyObject *exc; - PyObject *exc_args, *v; - int num_args, i; - - if (!PyArg_ParseTuple(args, "Oi:raise_exception", - &exc, &num_args)) - return NULL; - - exc_args = PyTuple_New(num_args); - if (exc_args == NULL) - return NULL; - for (i = 0; i < num_args; ++i) { - v = PyLong_FromLong(i); - if (v == NULL) { - Py_DECREF(exc_args); - return NULL; - } - PyTuple_SET_ITEM(exc_args, i, v); - } - PyErr_SetObject(exc, exc_args); - Py_DECREF(exc_args); - return NULL; -} - static PyObject * set_errno(PyObject *self, PyObject *args) { @@ -759,40 +732,6 @@ set_errno(PyObject *self, PyObject *args) Py_RETURN_NONE; } -static PyObject * -test_set_exception(PyObject *self, PyObject *new_exc) -{ - PyObject *exc = PyErr_GetHandledException(); - assert(PyExceptionInstance_Check(exc) || exc == NULL); - - PyErr_SetHandledException(new_exc); - return exc; -} - -static PyObject * -test_set_exc_info(PyObject *self, PyObject *args) -{ - PyObject *orig_exc; - PyObject *new_type, *new_value, *new_tb; - PyObject *type, *value, *tb; - if (!PyArg_ParseTuple(args, "OOO:test_set_exc_info", - &new_type, &new_value, &new_tb)) - return NULL; - - PyErr_GetExcInfo(&type, &value, &tb); - - Py_INCREF(new_type); - Py_INCREF(new_value); - Py_INCREF(new_tb); - PyErr_SetExcInfo(new_type, new_value, new_tb); - - orig_exc = PyTuple_Pack(3, type ? type : Py_None, value ? value : Py_None, tb ? tb : Py_None); - Py_XDECREF(type); - Py_XDECREF(value); - Py_XDECREF(tb); - return orig_exc; -} - /* test_thread_state spawns a thread of its own, and that thread releases * `thread_done` when it's finished. The driver code has to know when the * thread finishes, because the thread uses a PyObject (the callable) that @@ -1272,57 +1211,6 @@ profile_int(PyObject *self, PyObject* args) } #endif -/* To test the format of tracebacks as printed out. */ -static PyObject * -traceback_print(PyObject *self, PyObject *args) -{ - PyObject *file; - PyObject *traceback; - int result; - - if (!PyArg_ParseTuple(args, "OO:traceback_print", - &traceback, &file)) - return NULL; - - result = PyTraceBack_Print(traceback, file); - if (result < 0) - return NULL; - Py_RETURN_NONE; -} - -/* To test the format of exceptions as printed out. */ -static PyObject * -exception_print(PyObject *self, PyObject *args) -{ - PyObject *value; - PyObject *tb = NULL; - - if (!PyArg_ParseTuple(args, "O:exception_print", - &value)) { - return NULL; - } - - if (PyExceptionInstance_Check(value)) { - tb = PyException_GetTraceback(value); - } - - PyErr_Display((PyObject *) Py_TYPE(value), value, tb); - Py_XDECREF(tb); - - Py_RETURN_NONE; -} - - - - -/* reliably raise a MemoryError */ -static PyObject * -raise_memoryerror(PyObject *self, PyObject *Py_UNUSED(ignored)) -{ - PyErr_NoMemory(); - return NULL; -} - /* Issue 6012 */ static PyObject *str1, *str2; static int @@ -1368,26 +1256,6 @@ code_newempty(PyObject *self, PyObject *args) return (PyObject *)PyCode_NewEmpty(filename, funcname, firstlineno); } -/* Test PyErr_NewExceptionWithDoc (also exercise PyErr_NewException). - Run via Lib/test/test_exceptions.py */ -static PyObject * -make_exception_with_doc(PyObject *self, PyObject *args, PyObject *kwargs) -{ - const char *name; - const char *doc = NULL; - PyObject *base = NULL; - PyObject *dict = NULL; - - static char *kwlist[] = {"name", "doc", "base", "dict", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, - "s|sOO:make_exception_with_doc", kwlist, - &name, &doc, &base, &dict)) - return NULL; - - return PyErr_NewExceptionWithDoc(name, doc, base, dict); -} - static PyObject * make_memoryview_from_NULL_pointer(PyObject *self, PyObject *Py_UNUSED(ignored)) { @@ -2491,31 +2359,6 @@ negative_refcount(PyObject *self, PyObject *Py_UNUSED(args)) #endif -static PyObject* -test_write_unraisable_exc(PyObject *self, PyObject *args) -{ - PyObject *exc, *err_msg, *obj; - if (!PyArg_ParseTuple(args, "OOO", &exc, &err_msg, &obj)) { - return NULL; - } - - const char *err_msg_utf8; - if (err_msg != Py_None) { - err_msg_utf8 = PyUnicode_AsUTF8(err_msg); - if (err_msg_utf8 == NULL) { - return NULL; - } - } - else { - err_msg_utf8 = NULL; - } - - PyErr_SetObject((PyObject *)Py_TYPE(exc), exc); - _PyErr_WriteUnraisableMsg(err_msg_utf8, obj); - Py_RETURN_NONE; -} - - static PyObject * sequence_getitem(PyObject *self, PyObject *args) { @@ -2874,25 +2717,6 @@ test_py_is_funcs(PyObject *self, PyObject *Py_UNUSED(ignored)) } -static PyObject * -test_fatal_error(PyObject *self, PyObject *args) -{ - char *message; - int release_gil = 0; - if (!PyArg_ParseTuple(args, "y|i:fatal_error", &message, &release_gil)) - return NULL; - if (release_gil) { - Py_BEGIN_ALLOW_THREADS - Py_FatalError(message); - Py_END_ALLOW_THREADS - } - else { - Py_FatalError(message); - } - // Py_FatalError() does not return, but exits the process. - Py_RETURN_NONE; -} - // type->tp_version_tag static PyObject * type_get_version(PyObject *self, PyObject *type) @@ -3492,46 +3316,9 @@ function_set_kw_defaults(PyObject *self, PyObject *args) Py_RETURN_NONE; } -static PyObject * -err_set_raised(PyObject *self, PyObject *exc) -{ - Py_INCREF(exc); - PyErr_SetRaisedException(exc); - assert(PyErr_Occurred()); - return NULL; -} - -static PyObject * -err_restore(PyObject *self, PyObject *args) { - PyObject *type = NULL, *value = NULL, *traceback = NULL; - switch(PyTuple_Size(args)) { - case 3: - traceback = PyTuple_GetItem(args, 2); - Py_INCREF(traceback); - /* fall through */ - case 2: - value = PyTuple_GetItem(args, 1); - Py_INCREF(value); - /* fall through */ - case 1: - type = PyTuple_GetItem(args, 0); - Py_INCREF(type); - break; - default: - PyErr_SetString(PyExc_TypeError, - "wrong number of arguments"); - return NULL; - } - PyErr_Restore(type, value, traceback); - assert(PyErr_Occurred()); - return NULL; -} - static PyObject *test_buildvalue_issue38913(PyObject *, PyObject *); static PyMethodDef TestMethods[] = { - {"raise_exception", raise_exception, METH_VARARGS}, - {"raise_memoryerror", raise_memoryerror, METH_NOARGS}, {"set_errno", set_errno, METH_VARARGS}, {"test_config", test_config, METH_NOARGS}, {"test_sizeof_c_types", test_sizeof_c_types, METH_NOARGS}, @@ -3574,15 +3361,9 @@ static PyMethodDef TestMethods[] = { #ifdef HAVE_GETTIMEOFDAY {"profile_int", profile_int, METH_NOARGS}, #endif - {"traceback_print", traceback_print, METH_VARARGS}, - {"exception_print", exception_print, METH_VARARGS}, - {"set_exception", test_set_exception, METH_O}, - {"set_exc_info", test_set_exc_info, METH_VARARGS}, {"argparsing", argparsing, METH_VARARGS}, {"code_newempty", code_newempty, METH_VARARGS}, {"eval_code_ex", eval_eval_code_ex, METH_VARARGS}, - {"make_exception_with_doc", _PyCFunction_CAST(make_exception_with_doc), - METH_VARARGS | METH_KEYWORDS}, {"make_memoryview_from_NULL_pointer", make_memoryview_from_NULL_pointer, METH_NOARGS}, {"crash_no_current_thread", crash_no_current_thread, METH_NOARGS}, @@ -3633,7 +3414,6 @@ static PyMethodDef TestMethods[] = { #ifdef Py_REF_DEBUG {"negative_refcount", negative_refcount, METH_NOARGS}, #endif - {"write_unraisable_exc", test_write_unraisable_exc, METH_VARARGS}, {"sequence_getitem", sequence_getitem, METH_VARARGS}, {"sequence_setitem", sequence_setitem, METH_VARARGS}, {"sequence_delitem", sequence_delitem, METH_VARARGS}, @@ -3653,8 +3433,6 @@ static PyMethodDef TestMethods[] = { {"test_refcount_funcs", test_refcount_funcs, METH_NOARGS}, {"test_py_is_macros", test_py_is_macros, METH_NOARGS}, {"test_py_is_funcs", test_py_is_funcs, METH_NOARGS}, - {"fatal_error", test_fatal_error, METH_VARARGS, - PyDoc_STR("fatal_error(message, release_gil=False): call Py_FatalError(message)")}, {"type_get_version", type_get_version, METH_O, PyDoc_STR("type->tp_version_tag")}, {"test_tstate_capi", test_tstate_capi, METH_NOARGS, NULL}, {"frame_getlocals", frame_getlocals, METH_O, NULL}, @@ -3680,8 +3458,6 @@ static PyMethodDef TestMethods[] = { {"function_set_defaults", function_set_defaults, METH_VARARGS, NULL}, {"function_get_kw_defaults", function_get_kw_defaults, METH_O, NULL}, {"function_set_kw_defaults", function_set_kw_defaults, METH_VARARGS, NULL}, - {"err_set_raised", err_set_raised, METH_O, NULL}, - {"err_restore", err_restore, METH_VARARGS, NULL}, {NULL, NULL} /* sentinel */ }; @@ -3902,61 +3678,6 @@ static PyTypeObject awaitType = { }; -static int recurse_infinitely_error_init(PyObject *, PyObject *, PyObject *); - -static PyTypeObject PyRecursingInfinitelyError_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "RecursingInfinitelyError", /* tp_name */ - sizeof(PyBaseExceptionObject), /* tp_basicsize */ - 0, /* tp_itemsize */ - 0, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - PyDoc_STR("Instantiating this exception starts infinite recursion."), /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)recurse_infinitely_error_init, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ -}; - -static int -recurse_infinitely_error_init(PyObject *self, PyObject *args, PyObject *kwds) -{ - PyObject *type = (PyObject *)&PyRecursingInfinitelyError_Type; - - /* Instantiating this exception starts infinite recursion. */ - Py_INCREF(type); - PyErr_SetObject(type, NULL); - return -1; -} - - /* Test bpo-35983: create a subclass of "list" which checks that instances * are not deallocated twice */ @@ -4283,14 +4004,6 @@ PyInit__testcapi(void) Py_INCREF(&MethStatic_Type); PyModule_AddObject(m, "MethStatic", (PyObject *)&MethStatic_Type); - PyRecursingInfinitelyError_Type.tp_base = (PyTypeObject *)PyExc_Exception; - if (PyType_Ready(&PyRecursingInfinitelyError_Type) < 0) { - return NULL; - } - Py_INCREF(&PyRecursingInfinitelyError_Type); - PyModule_AddObject(m, "RecursingInfinitelyError", - (PyObject *)&PyRecursingInfinitelyError_Type); - PyModule_AddObject(m, "CHAR_MAX", PyLong_FromLong(CHAR_MAX)); PyModule_AddObject(m, "CHAR_MIN", PyLong_FromLong(CHAR_MIN)); PyModule_AddObject(m, "UCHAR_MAX", PyLong_FromLong(UCHAR_MAX)); @@ -4368,6 +4081,9 @@ PyInit__testcapi(void) if (_PyTestCapi_Init_Structmember(m) < 0) { return NULL; } + if (_PyTestCapi_Init_Exceptions(m) < 0) { + return NULL; + } #ifndef LIMITED_API_AVAILABLE PyModule_AddObjectRef(m, "LIMITED_API_AVAILABLE", Py_False); diff --git a/PCbuild/_testcapi.vcxproj b/PCbuild/_testcapi.vcxproj index 58bf4e1eacbf21..742eb3ed2d9056 100644 --- a/PCbuild/_testcapi.vcxproj +++ b/PCbuild/_testcapi.vcxproj @@ -107,6 +107,7 @@ + diff --git a/PCbuild/_testcapi.vcxproj.filters b/PCbuild/_testcapi.vcxproj.filters index 101c5322761634..ab5afc150c32f5 100644 --- a/PCbuild/_testcapi.vcxproj.filters +++ b/PCbuild/_testcapi.vcxproj.filters @@ -51,6 +51,9 @@ Source Files + + Source Files + From d43c2652d4b1ca4d0afa468e58c4f50052f4bfa2 Mon Sep 17 00:00:00 2001 From: Jonathan Protzenko Date: Thu, 23 Feb 2023 09:23:57 -0800 Subject: [PATCH 174/247] gh-99108: Followup fix for Modules/Setup (GH-102183) Automerge-Triggered-By: GH:erlend-aasland --- Modules/Setup | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/Setup b/Modules/Setup index f9fa26cac9e233..ff432e2929ec2d 100644 --- a/Modules/Setup +++ b/Modules/Setup @@ -163,8 +163,8 @@ PYTHONPATH=$(COREPYTHONPATH) # hashing builtins #_blake2 _blake2/blake2module.c _blake2/blake2b_impl.c _blake2/blake2s_impl.c -#_md5 md5module.c -I$(srcdir)/Modules/_hacl/include _hacl/libHacl_Hash_MD5.c -#_sha1 sha1module.c -I$(srcdir)/Modules/_hacl/include _hacl/libHacl_Hash_SHA1.c +#_md5 md5module.c -I$(srcdir)/Modules/_hacl/include _hacl/Hacl_Hash_MD5.c +#_sha1 sha1module.c -I$(srcdir)/Modules/_hacl/include _hacl/Hacl_Hash_SHA1.c #_sha2 sha2module.c -I$(srcdir)/Modules/_hacl/include Modules/_hacl/libHacl_Streaming_SHA2.a #_sha3 _sha3/sha3module.c From ccd98a3146d66343499d04a44e038223a1a09e80 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 23 Feb 2023 22:42:15 +0100 Subject: [PATCH 175/247] gh-101476: Add _PyType_GetModuleState (GH-101477) For fast module state access from heap type methods. --- Include/internal/pycore_typeobject.h | 16 ++++++++++++++++ Modules/itertoolsmodule.c | 3 ++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Include/internal/pycore_typeobject.h b/Include/internal/pycore_typeobject.h index 4d705740a9a62b..cc5ce2875101ea 100644 --- a/Include/internal/pycore_typeobject.h +++ b/Include/internal/pycore_typeobject.h @@ -4,6 +4,8 @@ extern "C" { #endif +#include "pycore_moduleobject.h" + #ifndef Py_BUILD_CORE # error "this header requires Py_BUILD_CORE define" #endif @@ -62,6 +64,20 @@ _PyStaticType_GET_WEAKREFS_LISTPTR(static_builtin_state *state) return &state->tp_weaklist; } +/* Like PyType_GetModuleState, but skips verification + * that type is a heap type with an associated module */ +static inline void * +_PyType_GetModuleState(PyTypeObject *type) +{ + assert(PyType_Check(type)); + assert(type->tp_flags & Py_TPFLAGS_HEAPTYPE); + PyHeapTypeObject *et = (PyHeapTypeObject *)type; + assert(et->ht_module); + PyModuleObject *mod = (PyModuleObject *)(et->ht_module); + assert(mod != NULL); + return mod->md_state; +} + struct types_state { struct type_cache type_cache; size_t num_builtins_initialized; diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index 6986695e47b1ae..c986e02867ca82 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -3,6 +3,7 @@ #include "pycore_call.h" // _PyObject_CallNoArgs() #include "pycore_long.h" // _PyLong_GetZero() #include "pycore_moduleobject.h" // _PyModule_GetState() +#include "pycore_typeobject.h" // _PyType_GetModuleState() #include "pycore_object.h" // _PyObject_GC_TRACK() #include "pycore_tuple.h" // _PyTuple_ITEMS() #include "structmember.h" // PyMemberDef @@ -48,7 +49,7 @@ get_module_state(PyObject *mod) static inline itertools_state * get_module_state_by_cls(PyTypeObject *cls) { - void *state = PyType_GetModuleState(cls); + void *state = _PyType_GetModuleState(cls); assert(state != NULL); return (itertools_state *)state; } From 0c857865e4f255f99d58678f878e09c11da89892 Mon Sep 17 00:00:00 2001 From: Jacob Bower <1978924+jbower-fb@users.noreply.github.com> Date: Thu, 23 Feb 2023 14:57:06 -0800 Subject: [PATCH 176/247] Fix deadlock on shutdown if test_current_{exception,frames} fails (#102019) * Don't deadlock on shutdown if test_current_{exception,frames} fails These tests spawn a thread that waits on a threading.Event. If the test fails any of its assertions, the Event won't be signaled and the thread will wait indefinitely, causing a deadlock when threading._shutdown() tries to join all outstanding threads. Co-authored-by: Brett Simmers * Add a news entry * Fix whitespace --------- Co-authored-by: Brett Simmers Co-authored-by: Oleg Iarygin --- Lib/test/test_sys.py | 148 +++++++++--------- ...-02-18-10-51-02.gh-issue-102019.0797SJ.rst | 2 + 2 files changed, 77 insertions(+), 73 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2023-02-18-10-51-02.gh-issue-102019.0797SJ.rst diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 58aa9d10210edf..b839985def9a12 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -445,46 +445,47 @@ def g456(): t.start() entered_g.wait() - # At this point, t has finished its entered_g.set(), although it's - # impossible to guess whether it's still on that line or has moved on - # to its leave_g.wait(). - self.assertEqual(len(thread_info), 1) - thread_id = thread_info[0] - - d = sys._current_frames() - for tid in d: - self.assertIsInstance(tid, int) - self.assertGreater(tid, 0) - - main_id = threading.get_ident() - self.assertIn(main_id, d) - self.assertIn(thread_id, d) - - # Verify that the captured main-thread frame is _this_ frame. - frame = d.pop(main_id) - self.assertTrue(frame is sys._getframe()) - - # Verify that the captured thread frame is blocked in g456, called - # from f123. This is a little tricky, since various bits of - # threading.py are also in the thread's call stack. - frame = d.pop(thread_id) - stack = traceback.extract_stack(frame) - for i, (filename, lineno, funcname, sourceline) in enumerate(stack): - if funcname == "f123": - break - else: - self.fail("didn't find f123() on thread's call stack") - - self.assertEqual(sourceline, "g456()") + try: + # At this point, t has finished its entered_g.set(), although it's + # impossible to guess whether it's still on that line or has moved on + # to its leave_g.wait(). + self.assertEqual(len(thread_info), 1) + thread_id = thread_info[0] + + d = sys._current_frames() + for tid in d: + self.assertIsInstance(tid, int) + self.assertGreater(tid, 0) + + main_id = threading.get_ident() + self.assertIn(main_id, d) + self.assertIn(thread_id, d) + + # Verify that the captured main-thread frame is _this_ frame. + frame = d.pop(main_id) + self.assertTrue(frame is sys._getframe()) + + # Verify that the captured thread frame is blocked in g456, called + # from f123. This is a little tricky, since various bits of + # threading.py are also in the thread's call stack. + frame = d.pop(thread_id) + stack = traceback.extract_stack(frame) + for i, (filename, lineno, funcname, sourceline) in enumerate(stack): + if funcname == "f123": + break + else: + self.fail("didn't find f123() on thread's call stack") - # And the next record must be for g456(). - filename, lineno, funcname, sourceline = stack[i+1] - self.assertEqual(funcname, "g456") - self.assertIn(sourceline, ["leave_g.wait()", "entered_g.set()"]) + self.assertEqual(sourceline, "g456()") - # Reap the spawned thread. - leave_g.set() - t.join() + # And the next record must be for g456(). + filename, lineno, funcname, sourceline = stack[i+1] + self.assertEqual(funcname, "g456") + self.assertIn(sourceline, ["leave_g.wait()", "entered_g.set()"]) + finally: + # Reap the spawned thread. + leave_g.set() + t.join() @threading_helper.reap_threads @threading_helper.requires_working_threading() @@ -516,43 +517,44 @@ def g456(): t.start() entered_g.wait() - # At this point, t has finished its entered_g.set(), although it's - # impossible to guess whether it's still on that line or has moved on - # to its leave_g.wait(). - self.assertEqual(len(thread_info), 1) - thread_id = thread_info[0] - - d = sys._current_exceptions() - for tid in d: - self.assertIsInstance(tid, int) - self.assertGreater(tid, 0) - - main_id = threading.get_ident() - self.assertIn(main_id, d) - self.assertIn(thread_id, d) - self.assertEqual((None, None, None), d.pop(main_id)) - - # Verify that the captured thread frame is blocked in g456, called - # from f123. This is a little tricky, since various bits of - # threading.py are also in the thread's call stack. - exc_type, exc_value, exc_tb = d.pop(thread_id) - stack = traceback.extract_stack(exc_tb.tb_frame) - for i, (filename, lineno, funcname, sourceline) in enumerate(stack): - if funcname == "f123": - break - else: - self.fail("didn't find f123() on thread's call stack") - - self.assertEqual(sourceline, "g456()") + try: + # At this point, t has finished its entered_g.set(), although it's + # impossible to guess whether it's still on that line or has moved on + # to its leave_g.wait(). + self.assertEqual(len(thread_info), 1) + thread_id = thread_info[0] + + d = sys._current_exceptions() + for tid in d: + self.assertIsInstance(tid, int) + self.assertGreater(tid, 0) + + main_id = threading.get_ident() + self.assertIn(main_id, d) + self.assertIn(thread_id, d) + self.assertEqual((None, None, None), d.pop(main_id)) + + # Verify that the captured thread frame is blocked in g456, called + # from f123. This is a little tricky, since various bits of + # threading.py are also in the thread's call stack. + exc_type, exc_value, exc_tb = d.pop(thread_id) + stack = traceback.extract_stack(exc_tb.tb_frame) + for i, (filename, lineno, funcname, sourceline) in enumerate(stack): + if funcname == "f123": + break + else: + self.fail("didn't find f123() on thread's call stack") - # And the next record must be for g456(). - filename, lineno, funcname, sourceline = stack[i+1] - self.assertEqual(funcname, "g456") - self.assertTrue(sourceline.startswith("if leave_g.wait(")) + self.assertEqual(sourceline, "g456()") - # Reap the spawned thread. - leave_g.set() - t.join() + # And the next record must be for g456(). + filename, lineno, funcname, sourceline = stack[i+1] + self.assertEqual(funcname, "g456") + self.assertTrue(sourceline.startswith("if leave_g.wait(")) + finally: + # Reap the spawned thread. + leave_g.set() + t.join() def test_attributes(self): self.assertIsInstance(sys.api_version, int) diff --git a/Misc/NEWS.d/next/Tests/2023-02-18-10-51-02.gh-issue-102019.0797SJ.rst b/Misc/NEWS.d/next/Tests/2023-02-18-10-51-02.gh-issue-102019.0797SJ.rst new file mode 100644 index 00000000000000..63e36046d26dfe --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-02-18-10-51-02.gh-issue-102019.0797SJ.rst @@ -0,0 +1,2 @@ +Fix deadlock on shutdown if ``test_current_{exception,frames}`` fails. Patch +by Jacob Bower. From 9f3ecd1aa3566947648a053bd9716ed67dd9a718 Mon Sep 17 00:00:00 2001 From: Eclips4 <80244920+Eclips4@users.noreply.github.com> Date: Fri, 24 Feb 2023 05:28:24 +0300 Subject: [PATCH 177/247] gh-102158: Add tests for `softkwlist` (#102159) --------- Co-authored-by: Terry Jan Reedy --- Lib/test/test_keyword.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Lib/test/test_keyword.py b/Lib/test/test_keyword.py index 3e2a8b3fb7f4c3..f329f88fa01d51 100644 --- a/Lib/test/test_keyword.py +++ b/Lib/test/test_keyword.py @@ -20,18 +20,36 @@ def test_changing_the_kwlist_does_not_affect_iskeyword(self): keyword.kwlist = ['its', 'all', 'eggs', 'beans', 'and', 'a', 'slice'] self.assertFalse(keyword.iskeyword('eggs')) + def test_changing_the_softkwlist_does_not_affect_issoftkeyword(self): + oldlist = keyword.softkwlist + self.addCleanup(setattr, keyword, "softkwlist", oldlist) + keyword.softkwlist = ["foo", "bar", "spam", "egs", "case"] + self.assertFalse(keyword.issoftkeyword("spam")) + def test_all_keywords_fail_to_be_used_as_names(self): for key in keyword.kwlist: with self.assertRaises(SyntaxError): exec(f"{key} = 42") + def test_all_soft_keywords_can_be_used_as_names(self): + for key in keyword.softkwlist: + exec(f"{key} = 42") + def test_async_and_await_are_keywords(self): self.assertIn("async", keyword.kwlist) self.assertIn("await", keyword.kwlist) + def test_match_and_case_are_soft_keywords(self): + self.assertIn("match", keyword.softkwlist) + self.assertIn("case", keyword.softkwlist) + self.assertIn("_", keyword.softkwlist) + def test_keywords_are_sorted(self): self.assertListEqual(sorted(keyword.kwlist), keyword.kwlist) + def test_softkeywords_are_sorted(self): + self.assertListEqual(sorted(keyword.softkwlist), keyword.softkwlist) + if __name__ == "__main__": unittest.main() From 347f7406df62b2bbe551685d72a466f27b951f8e Mon Sep 17 00:00:00 2001 From: Yeojin Kim Date: Fri, 24 Feb 2023 19:26:51 +0900 Subject: [PATCH 178/247] gh-81652: Add MAP_ALIGNED_SUPER FreeBSD and MAP_CONCEAL OpenBSD constants (gh-102191) --- Doc/library/mmap.rst | 14 +++++++++++--- Misc/ACKS | 1 + .../2023-02-23-20-39-52.gh-issue-81652.Vxz0Mr.rst | 2 ++ Modules/mmapmodule.c | 6 ++++++ 4 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-02-23-20-39-52.gh-issue-81652.Vxz0Mr.rst diff --git a/Doc/library/mmap.rst b/Doc/library/mmap.rst index c4f8781f2ac993..69afadff1f5f42 100644 --- a/Doc/library/mmap.rst +++ b/Doc/library/mmap.rst @@ -370,11 +370,19 @@ MAP_* Constants MAP_ANONYMOUS MAP_POPULATE MAP_STACK + MAP_ALIGNED_SUPER + MAP_CONCEAL - These are the various flags that can be passed to :meth:`mmap.mmap`. Note that some options might not be present on some systems. + These are the various flags that can be passed to :meth:`mmap.mmap`. :data:`MAP_ALIGNED_SUPER` + is only available at FreeBSD and :data:`MAP_CONCEAL` is only available at OpenBSD. Note + that some options might not be present on some systems. .. versionchanged:: 3.10 - Added MAP_POPULATE constant. + Added :data:`MAP_POPULATE` constant. .. versionadded:: 3.11 - Added MAP_STACK constant. + Added :data:`MAP_STACK` constant. + + .. versionadded:: 3.12 + Added :data:`MAP_ALIGNED_SUPER` constant. + Added :data:`MAP_CONCEAL` constant. diff --git a/Misc/ACKS b/Misc/ACKS index 33dbf4e989d96a..43e420a1373cb7 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -930,6 +930,7 @@ Derek D. Kim Gihwan Kim Jan Kim Taek Joo Kim +Yeojin Kim Sam Kimbrel Tomohiko Kinebuchi James King diff --git a/Misc/NEWS.d/next/Library/2023-02-23-20-39-52.gh-issue-81652.Vxz0Mr.rst b/Misc/NEWS.d/next/Library/2023-02-23-20-39-52.gh-issue-81652.Vxz0Mr.rst new file mode 100644 index 00000000000000..48acce1d863ea6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-23-20-39-52.gh-issue-81652.Vxz0Mr.rst @@ -0,0 +1,2 @@ +Add :data:`mmap.MAP_ALIGNED_SUPER` FreeBSD and :data:`mmap.MAP_CONCEAL` +OpenBSD constants to :mod:`mmap`. Patch by Yeojin Kim. diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c index 8244202376c74e..a01e798265c5a5 100644 --- a/Modules/mmapmodule.c +++ b/Modules/mmapmodule.c @@ -1603,6 +1603,12 @@ mmap_exec(PyObject *module) // Mostly a no-op on Linux and NetBSD, but useful on OpenBSD // for stack usage (even on x86 arch) ADD_INT_MACRO(module, MAP_STACK); +#endif +#ifdef MAP_ALIGNED_SUPER + ADD_INT_MACRO(module, MAP_ALIGNED_SUPER); +#endif +#ifdef MAP_CONCEAL + ADD_INT_MACRO(module, MAP_CONCEAL); #endif if (PyModule_AddIntConstant(module, "PAGESIZE", (long)my_getpagesize()) < 0 ) { return -1; From 1fa38906f0b228e6b0a6baa89ab6316989b0388a Mon Sep 17 00:00:00 2001 From: Max Bachmann Date: Fri, 24 Feb 2023 13:38:21 +0100 Subject: [PATCH 179/247] gh-102141: replace use of getpid on Windows with GetCurrentProcessId (GH-102142) --- ...2023-02-22-15-15-32.gh-issue-102027.Km4G-d.rst | 2 ++ Modules/_randommodule.c | 4 +++- Modules/posixmodule.c | 15 +++++++++------ PC/pyconfig.h | 3 +++ 4 files changed, 17 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-22-15-15-32.gh-issue-102027.Km4G-d.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-22-15-15-32.gh-issue-102027.Km4G-d.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-22-15-15-32.gh-issue-102027.Km4G-d.rst new file mode 100644 index 00000000000000..514a8ef26594dc --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-22-15-15-32.gh-issue-102027.Km4G-d.rst @@ -0,0 +1,2 @@ +Use ``GetCurrentProcessId`` on Windows when ``getpid`` is unavailable. Patch by +Max Bachmann. diff --git a/Modules/_randommodule.c b/Modules/_randommodule.c index 95f1e505dd1873..68060c07033d34 100644 --- a/Modules/_randommodule.c +++ b/Modules/_randommodule.c @@ -259,7 +259,9 @@ random_seed_time_pid(RandomObject *self) key[0] = (uint32_t)(now & 0xffffffffU); key[1] = (uint32_t)(now >> 32); -#ifdef HAVE_GETPID +#ifdef MS_WINDOWS_NON_DESKTOP + key[2] = (uint32_t)GetCurrentProcessId(); +#elif defined(HAVE_GETPID) key[2] = (uint32_t)getpid(); #else key[2] = 0; diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 524dc7eb1ccc97..51aa89ef715a19 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -7946,7 +7946,7 @@ os_getgid_impl(PyObject *module) #endif /* HAVE_GETGID */ -#ifdef HAVE_GETPID +#if defined(HAVE_GETPID) /*[clinic input] os.getpid @@ -7957,9 +7957,13 @@ static PyObject * os_getpid_impl(PyObject *module) /*[clinic end generated code: output=9ea6fdac01ed2b3c input=5a9a00f0ab68aa00]*/ { +#ifdef MS_WINDOWS_NON_DESKTOP + return PyLong_FromUnsignedLong(GetCurrentProcessId()); +#else return PyLong_FromPid(getpid()); +#endif } -#endif /* HAVE_GETPID */ +#endif /* defined(HAVE_GETPID) */ #ifdef NGROUPS_MAX #define MAX_GROUPS NGROUPS_MAX @@ -8265,12 +8269,11 @@ static PyObject* win32_getppid() { HANDLE snapshot; - pid_t mypid; PyObject* result = NULL; BOOL have_record; PROCESSENTRY32 pe; - mypid = getpid(); /* This function never fails */ + DWORD mypid = GetCurrentProcessId(); /* This function never fails */ snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (snapshot == INVALID_HANDLE_VALUE) @@ -8279,9 +8282,9 @@ win32_getppid() pe.dwSize = sizeof(pe); have_record = Process32First(snapshot, &pe); while (have_record) { - if (mypid == (pid_t)pe.th32ProcessID) { + if (mypid == pe.th32ProcessID) { /* We could cache the ulong value in a static variable. */ - result = PyLong_FromPid((pid_t)pe.th32ParentProcessID); + result = PyLong_FromUnsignedLong(pe.th32ParentProcessID); break; } diff --git a/PC/pyconfig.h b/PC/pyconfig.h index f5166a1506c945..a34d420ab7ecaa 100644 --- a/PC/pyconfig.h +++ b/PC/pyconfig.h @@ -72,6 +72,9 @@ WIN32 is still required for the locale module. #define USE_SOCKET #endif +#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP) +#define MS_WINDOWS_NON_DESKTOP +#endif /* Compiler specific defines */ From e5e1c1fabd8b5626f9193e6c61b9d7ceb7fb2f95 Mon Sep 17 00:00:00 2001 From: Max Bachmann Date: Fri, 24 Feb 2023 15:53:50 +0100 Subject: [PATCH 180/247] Remove references to old Windows source files from internal documentation (GH-102216) --- PC/readme.txt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/PC/readme.txt b/PC/readme.txt index bef5111c591825..a917a72c1232cd 100644 --- a/PC/readme.txt +++ b/PC/readme.txt @@ -60,11 +60,6 @@ python_nt.rc Resource compiler input for python15.dll. dl_nt.c Additional sources used for 32-bit Windows features. -getpathp.c Default sys.path calculations (for all PC platforms). - -dllbase_nt.txt A (manually maintained) list of base addresses for - various DLLs, to avoid run-time relocation. - Note for Windows 3.x and DOS users ================================== From 81bf10e4f20a0f6d36b67085eefafdf7ebb97c33 Mon Sep 17 00:00:00 2001 From: Stefan Pochmann <609905+pochmann@users.noreply.github.com> Date: Fri, 24 Feb 2023 17:13:05 +0100 Subject: [PATCH 181/247] gh-102105 Fix wording in filterfalse/quantify/filter (GH-102189) --- Doc/library/functions.rst | 4 ++-- Doc/library/itertools.rst | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 3ff28849025153..f0f374771b0cf1 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -623,7 +623,7 @@ are always available. They are listed here in alphabetical order. .. function:: filter(function, iterable) Construct an iterator from those elements of *iterable* for which *function* - returns true. *iterable* may be either a sequence, a container which + is true. *iterable* may be either a sequence, a container which supports iteration, or an iterator. If *function* is ``None``, the identity function is assumed, that is, all elements of *iterable* that are false are removed. @@ -634,7 +634,7 @@ are always available. They are listed here in alphabetical order. ``None``. See :func:`itertools.filterfalse` for the complementary function that returns - elements of *iterable* for which *function* returns false. + elements of *iterable* for which *function* is false. .. class:: float(x=0.0) diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index 8d83d92660d6ef..95da7166842ee6 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -398,7 +398,7 @@ loops that truncate the stream. .. function:: filterfalse(predicate, iterable) Make an iterator that filters elements from iterable returning only those for - which the predicate is ``False``. If *predicate* is ``None``, return the items + which the predicate is false. If *predicate* is ``None``, return the items that are false. Roughly equivalent to:: def filterfalse(predicate, iterable): @@ -831,7 +831,7 @@ which incur interpreter overhead. return next(g, True) and not next(g, False) def quantify(iterable, pred=bool): - "Count how many times the predicate is true" + "Count how many times the predicate is True" return sum(map(pred, iterable)) def ncycles(iterable, n): From 568fc0dee42a353f327b059a48f97c911de904b3 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Fri, 24 Feb 2023 21:16:29 +0100 Subject: [PATCH 182/247] gh-101476: Use _PyType_GetModuleState where applicable (#102188) --- Modules/_abc.c | 2 +- Modules/_asynciomodule.c | 2 +- Modules/_lsprof.c | 2 +- Modules/_operator.c | 6 +++--- Modules/_sha3/sha3module.c | 7 ++++--- Modules/_zoneinfo.c | 2 +- Modules/md5module.c | 3 ++- Modules/sha1module.c | 3 ++- Modules/sha2module.c | 7 ++++--- 9 files changed, 19 insertions(+), 15 deletions(-) diff --git a/Modules/_abc.c b/Modules/_abc.c index e146d4fd0cac39..9d6654b4e58aad 100644 --- a/Modules/_abc.c +++ b/Modules/_abc.c @@ -79,7 +79,7 @@ abc_data_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return NULL; } - state = PyType_GetModuleState(type); + state = _PyType_GetModuleState(type); if (state == NULL) { Py_DECREF(self); return NULL; diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 055dded05431df..21b2ca1971f9b3 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -76,7 +76,7 @@ get_asyncio_state(PyObject *mod) static inline asyncio_state * get_asyncio_state_by_cls(PyTypeObject *cls) { - asyncio_state *state = (asyncio_state *)PyType_GetModuleState(cls); + asyncio_state *state = (asyncio_state *)_PyType_GetModuleState(cls); assert(state != NULL); return state; } diff --git a/Modules/_lsprof.c b/Modules/_lsprof.c index 37170bbea56ad3..3237d796dc2961 100644 --- a/Modules/_lsprof.c +++ b/Modules/_lsprof.c @@ -607,7 +607,7 @@ _lsprof_Profiler_getstats_impl(ProfilerObject *self, PyTypeObject *cls) /*[clinic end generated code: output=1806ef720019ee03 input=445e193ef4522902]*/ { statscollector_t collect; - collect.state = PyType_GetModuleState(cls); + collect.state = _PyType_GetModuleState(cls); if (pending_exception(self)) { return NULL; } diff --git a/Modules/_operator.c b/Modules/_operator.c index 4f2367150eefc4..38335b6995016c 100644 --- a/Modules/_operator.c +++ b/Modules/_operator.c @@ -1002,7 +1002,7 @@ itemgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } else { item = args; } - _operator_state *state = PyType_GetModuleState(type); + _operator_state *state = _PyType_GetModuleState(type); /* create itemgetterobject structure */ ig = PyObject_GC_New(itemgetterobject, (PyTypeObject *) state->itemgetter_type); if (ig == NULL) { @@ -1298,7 +1298,7 @@ attrgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } } - _operator_state *state = PyType_GetModuleState(type); + _operator_state *state = _PyType_GetModuleState(type); /* create attrgetterobject structure */ ag = PyObject_GC_New(attrgetterobject, (PyTypeObject *)state->attrgetter_type); if (ag == NULL) { @@ -1578,7 +1578,7 @@ methodcaller_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return NULL; } - _operator_state *state = PyType_GetModuleState(type); + _operator_state *state = _PyType_GetModuleState(type); /* create methodcallerobject structure */ mc = PyObject_GC_New(methodcallerobject, (PyTypeObject *)state->methodcaller_type); if (mc == NULL) { diff --git a/Modules/_sha3/sha3module.c b/Modules/_sha3/sha3module.c index bd1dd596bdda68..633a0c0ea08d2a 100644 --- a/Modules/_sha3/sha3module.c +++ b/Modules/_sha3/sha3module.c @@ -21,6 +21,7 @@ #include "Python.h" #include "pycore_strhex.h" // _Py_strhex() +#include "pycore_typeobject.h" // _PyType_GetModuleState() #include "../hashlib.h" #include "sha3.c" @@ -106,7 +107,7 @@ py_sha3_new_impl(PyTypeObject *type, PyObject *data, int usedforsecurity) { HashReturn res; Py_buffer buf = {NULL, NULL}; - SHA3State *state = PyType_GetModuleState(type); + SHA3State *state = _PyType_GetModuleState(type); SHA3object *self = newSHA3object(type); if (self == NULL) { goto error; @@ -337,7 +338,7 @@ SHA3_get_name(SHA3object *self, void *closure) { PyTypeObject *type = Py_TYPE(self); - SHA3State *state = PyType_GetModuleState(type); + SHA3State *state = _PyType_GetModuleState(type); assert(state != NULL); if (type == state->sha3_224_type) { @@ -408,7 +409,7 @@ static PyGetSetDef SHA3_getseters[] = { {0,0} \ } -// Using PyType_GetModuleState() on these types is safe since they +// Using _PyType_GetModuleState() on these types is safe since they // cannot be subclassed: it does not have the Py_TPFLAGS_BASETYPE flag. #define SHA3_TYPE_SPEC(type_spec_obj, type_name, type_slots) \ static PyType_Spec type_spec_obj = { \ diff --git a/Modules/_zoneinfo.c b/Modules/_zoneinfo.c index 9f423559f51a43..6e1a37611b6152 100644 --- a/Modules/_zoneinfo.c +++ b/Modules/_zoneinfo.c @@ -204,7 +204,7 @@ zoneinfo_get_state(PyObject *mod) static inline zoneinfo_state * zoneinfo_get_state_by_cls(PyTypeObject *cls) { - zoneinfo_state *state = (zoneinfo_state *)PyType_GetModuleState(cls); + zoneinfo_state *state = (zoneinfo_state *)_PyType_GetModuleState(cls); assert(state != NULL); return state; } diff --git a/Modules/md5module.c b/Modules/md5module.c index df3d6a4a70d789..4f7bc77a8836a3 100644 --- a/Modules/md5module.c +++ b/Modules/md5module.c @@ -22,6 +22,7 @@ #include "Python.h" #include "hashlib.h" #include "pycore_strhex.h" // _Py_strhex() +#include "pycore_typeobject.h" // _PyType_GetModuleState() /*[clinic input] module _md5 @@ -108,7 +109,7 @@ static PyObject * MD5Type_copy_impl(MD5object *self, PyTypeObject *cls) /*[clinic end generated code: output=bf055e08244bf5ee input=d89087dcfb2a8620]*/ { - MD5State *st = PyType_GetModuleState(cls); + MD5State *st = _PyType_GetModuleState(cls); MD5object *newobj; if ((newobj = newMD5object(st))==NULL) diff --git a/Modules/sha1module.c b/Modules/sha1module.c index 0f50d532acf925..f8d4056fd34b65 100644 --- a/Modules/sha1module.c +++ b/Modules/sha1module.c @@ -22,6 +22,7 @@ #include "Python.h" #include "hashlib.h" #include "pycore_strhex.h" // _Py_strhex() +#include "pycore_typeobject.h" // _PyType_GetModuleState() /*[clinic input] module _sha1 @@ -108,7 +109,7 @@ static PyObject * SHA1Type_copy_impl(SHA1object *self, PyTypeObject *cls) /*[clinic end generated code: output=b32d4461ce8bc7a7 input=6c22e66fcc34c58e]*/ { - SHA1State *st = PyType_GetModuleState(cls); + SHA1State *st = _PyType_GetModuleState(cls); SHA1object *newobj; if ((newobj = newSHA1object(st)) == NULL) diff --git a/Modules/sha2module.c b/Modules/sha2module.c index 9999f255cd578a..72de20b44762d7 100644 --- a/Modules/sha2module.c +++ b/Modules/sha2module.c @@ -23,6 +23,7 @@ #include "Python.h" #include "pycore_bitutils.h" // _Py_bswap32() #include "pycore_moduleobject.h" // _PyModule_GetState() +#include "pycore_typeobject.h" // _PyType_GetModuleState() #include "pycore_strhex.h" // _Py_strhex() #include "structmember.h" // PyMemberDef #include "hashlib.h" @@ -217,7 +218,7 @@ SHA256Type_copy_impl(SHA256object *self, PyTypeObject *cls) /*[clinic end generated code: output=fabd515577805cd3 input=3137146fcb88e212]*/ { SHA256object *newobj; - sha2_state *state = PyType_GetModuleState(cls); + sha2_state *state = _PyType_GetModuleState(cls); if (Py_IS_TYPE(self, state->sha256_type)) { if ((newobj = newSHA256object(state)) == NULL) { return NULL; @@ -245,7 +246,7 @@ SHA512Type_copy_impl(SHA512object *self, PyTypeObject *cls) /*[clinic end generated code: output=66d2a8ef20de8302 input=f673a18f66527c90]*/ { SHA512object *newobj; - sha2_state *state = PyType_GetModuleState(cls); + sha2_state *state = _PyType_GetModuleState(cls); if (Py_IS_TYPE((PyObject*)self, state->sha512_type)) { if ((newobj = newSHA512object(state)) == NULL) { @@ -482,7 +483,7 @@ static PyType_Slot sha512_type_slots[] = { {0,0} }; -// Using PyType_GetModuleState() on these types is safe since they +// Using _PyType_GetModuleState() on these types is safe since they // cannot be subclassed: they don't have the Py_TPFLAGS_BASETYPE flag. static PyType_Spec sha224_type_spec = { .name = "_sha2.SHA224Type", From 2db23d10bf64bf7c061fd95c6a8079ddc5c9aa4b Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Fri, 24 Feb 2023 21:43:03 +0000 Subject: [PATCH 183/247] gh-102192: Replace PyErr_Fetch/Restore etc by more efficient alternatives (in Modules/) (#102196) --- Modules/_asynciomodule.c | 69 ++++++++++--------------------- Modules/_io/_iomodule.c | 7 ++-- Modules/_io/bufferedio.c | 60 +++++++++++++-------------- Modules/_io/fileio.c | 31 +++++++------- Modules/_io/iobase.c | 12 +++--- Modules/_io/textio.c | 23 ++++++----- Modules/_io/winconsoleio.c | 15 ++++--- Modules/_lsprof.c | 5 +-- Modules/_sqlite/connection.c | 14 +++---- Modules/_sqlite/cursor.c | 10 ++--- Modules/_testcapi/heaptype.c | 7 ++-- Modules/_testcapi/watchers.c | 14 +++---- Modules/_testcapimodule.c | 7 ++-- Modules/_xxinterpchannelsmodule.c | 6 +-- Modules/_zoneinfo.c | 5 +-- Modules/posixmodule.c | 5 +-- Modules/signalmodule.c | 10 ++--- Modules/socketmodule.c | 5 +-- 18 files changed, 136 insertions(+), 169 deletions(-) diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 21b2ca1971f9b3..13d98eedf32f0e 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -1422,7 +1422,6 @@ _asyncio_Future__make_cancelled_error_impl(FutureObj *self) static void FutureObj_finalize(FutureObj *fut) { - PyObject *error_type, *error_value, *error_traceback; PyObject *context; PyObject *message = NULL; PyObject *func; @@ -1434,7 +1433,7 @@ FutureObj_finalize(FutureObj *fut) fut->fut_log_tb = 0; /* Save the current exception, if any. */ - PyErr_Fetch(&error_type, &error_value, &error_traceback); + PyObject *exc = PyErr_GetRaisedException(); context = PyDict_New(); if (context == NULL) { @@ -1476,7 +1475,7 @@ FutureObj_finalize(FutureObj *fut) Py_XDECREF(message); /* Restore the saved exception. */ - PyErr_Restore(error_type, error_value, error_traceback); + PyErr_SetRaisedException(exc); } static PyMethodDef FutureType_methods[] = { @@ -2491,14 +2490,13 @@ TaskObj_finalize(TaskObj *task) PyObject *context; PyObject *message = NULL; PyObject *func; - PyObject *error_type, *error_value, *error_traceback; if (task->task_state != STATE_PENDING || !task->task_log_destroy_pending) { goto done; } /* Save the current exception, if any. */ - PyErr_Fetch(&error_type, &error_value, &error_traceback); + PyObject *exc = PyErr_GetRaisedException(); context = PyDict_New(); if (context == NULL) { @@ -2541,7 +2539,7 @@ TaskObj_finalize(TaskObj *task) Py_XDECREF(message); /* Restore the saved exception. */ - PyErr_Restore(error_type, error_value, error_traceback); + PyErr_SetRaisedException(exc); done: FutureObj_finalize((FutureObj*)task); @@ -2766,8 +2764,6 @@ task_step_impl(asyncio_state *state, TaskObj *task, PyObject *exc) } if (gen_status == PYGEN_RETURN || gen_status == PYGEN_ERROR) { - PyObject *et, *ev, *tb; - if (result != NULL) { /* The error is StopIteration and that means that the underlying coroutine has resolved */ @@ -2794,52 +2790,39 @@ task_step_impl(asyncio_state *state, TaskObj *task, PyObject *exc) if (PyErr_ExceptionMatches(state->asyncio_CancelledError)) { /* CancelledError */ - PyErr_Fetch(&et, &ev, &tb); - assert(et); - PyErr_NormalizeException(&et, &ev, &tb); - if (tb != NULL) { - PyException_SetTraceback(ev, tb); - Py_DECREF(tb); - } - Py_XDECREF(et); + + PyObject *exc = PyErr_GetRaisedException(); + assert(exc); FutureObj *fut = (FutureObj*)task; /* transfer ownership */ - fut->fut_cancelled_exc = ev; + fut->fut_cancelled_exc = exc; return future_cancel(state, fut, NULL); } /* Some other exception; pop it and call Task.set_exception() */ - PyErr_Fetch(&et, &ev, &tb); - assert(et); - PyErr_NormalizeException(&et, &ev, &tb); - if (tb != NULL) { - PyException_SetTraceback(ev, tb); - } + PyObject *exc = PyErr_GetRaisedException(); + assert(exc); - o = future_set_exception(state, (FutureObj*)task, ev); + o = future_set_exception(state, (FutureObj*)task, exc); if (!o) { /* An exception in Task.set_exception() */ - Py_DECREF(et); - Py_XDECREF(tb); - Py_XDECREF(ev); + Py_DECREF(exc); goto fail; } assert(o == Py_None); Py_DECREF(o); - if (PyErr_GivenExceptionMatches(et, PyExc_KeyboardInterrupt) || - PyErr_GivenExceptionMatches(et, PyExc_SystemExit)) + if (PyErr_GivenExceptionMatches(exc, PyExc_KeyboardInterrupt) || + PyErr_GivenExceptionMatches(exc, PyExc_SystemExit)) { /* We've got a KeyboardInterrupt or a SystemError; re-raise it */ - PyErr_Restore(et, ev, tb); + PyErr_SetRaisedException(exc); goto fail; } - Py_DECREF(et); - Py_XDECREF(tb); - Py_XDECREF(ev); + Py_DECREF(exc); Py_RETURN_NONE; } @@ -3059,10 +3042,9 @@ task_step(asyncio_state *state, TaskObj *task, PyObject *exc) res = task_step_impl(state, task, exc); if (res == NULL) { - PyObject *et, *ev, *tb; - PyErr_Fetch(&et, &ev, &tb); + PyObject *exc = PyErr_GetRaisedException(); leave_task(state, task->task_loop, (PyObject*)task); - _PyErr_ChainExceptions(et, ev, tb); /* Normalizes (et, ev, tb) */ + _PyErr_ChainExceptions1(exc); return NULL; } else { @@ -3079,7 +3061,6 @@ task_step(asyncio_state *state, TaskObj *task, PyObject *exc) static PyObject * task_wakeup(TaskObj *task, PyObject *o) { - PyObject *et, *ev, *tb; PyObject *result; assert(o); @@ -3111,18 +3092,12 @@ task_wakeup(TaskObj *task, PyObject *o) /* exception raised */ } - PyErr_Fetch(&et, &ev, &tb); - assert(et); - PyErr_NormalizeException(&et, &ev, &tb); - if (tb != NULL) { - PyException_SetTraceback(ev, tb); - } + PyObject *exc = PyErr_GetRaisedException(); + assert(exc); - result = task_step(state, task, ev); + result = task_step(state, task, exc); - Py_DECREF(et); - Py_XDECREF(tb); - Py_XDECREF(ev); + Py_DECREF(exc); return result; } diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c index 55b6535eb34b66..d8d836b8382eb1 100644 --- a/Modules/_io/_iomodule.c +++ b/Modules/_io/_iomodule.c @@ -437,10 +437,9 @@ _io_open_impl(PyObject *module, PyObject *file, const char *mode, error: if (result != NULL) { - PyObject *exc, *val, *tb, *close_result; - PyErr_Fetch(&exc, &val, &tb); - close_result = PyObject_CallMethodNoArgs(result, &_Py_ID(close)); - _PyErr_ChainExceptions(exc, val, tb); + PyObject *exc = PyErr_GetRaisedException(); + PyObject *close_result = PyObject_CallMethodNoArgs(result, &_Py_ID(close)); + _PyErr_ChainExceptions1(exc); Py_XDECREF(close_result); Py_DECREF(result); } diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index 56491f097100c0..960026707fc5ed 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -472,12 +472,13 @@ buffered_closed_get(buffered *self, void *context) static PyObject * buffered_close(buffered *self, PyObject *args) { - PyObject *res = NULL, *exc = NULL, *val, *tb; + PyObject *res = NULL; int r; CHECK_INITIALIZED(self) - if (!ENTER_BUFFERED(self)) + if (!ENTER_BUFFERED(self)) { return NULL; + } r = buffered_closed(self); if (r < 0) @@ -497,12 +498,16 @@ buffered_close(buffered *self, PyObject *args) /* flush() will most probably re-take the lock, so drop it first */ LEAVE_BUFFERED(self) res = PyObject_CallMethodNoArgs((PyObject *)self, &_Py_ID(flush)); - if (!ENTER_BUFFERED(self)) + if (!ENTER_BUFFERED(self)) { return NULL; - if (res == NULL) - PyErr_Fetch(&exc, &val, &tb); - else + } + PyObject *exc = NULL; + if (res == NULL) { + exc = PyErr_GetRaisedException(); + } + else { Py_DECREF(res); + } res = PyObject_CallMethodNoArgs(self->raw, &_Py_ID(close)); @@ -512,7 +517,7 @@ buffered_close(buffered *self, PyObject *args) } if (exc != NULL) { - _PyErr_ChainExceptions(exc, val, tb); + _PyErr_ChainExceptions1(exc); Py_CLEAR(res); } @@ -637,17 +642,14 @@ _set_BlockingIOError(const char *msg, Py_ssize_t written) static Py_ssize_t * _buffered_check_blocking_error(void) { - PyObject *t, *v, *tb; - PyOSErrorObject *err; - - PyErr_Fetch(&t, &v, &tb); - if (v == NULL || !PyErr_GivenExceptionMatches(v, PyExc_BlockingIOError)) { - PyErr_Restore(t, v, tb); + PyObject *exc = PyErr_GetRaisedException(); + if (exc == NULL || !PyErr_GivenExceptionMatches(exc, PyExc_BlockingIOError)) { + PyErr_SetRaisedException(exc); return NULL; } - err = (PyOSErrorObject *) v; + PyOSErrorObject *err = (PyOSErrorObject *)exc; /* TODO: sanity check (err->written >= 0) */ - PyErr_Restore(t, v, tb); + PyErr_SetRaisedException(exc); return &err->written; } @@ -749,13 +751,11 @@ _buffered_init(buffered *self) int _PyIO_trap_eintr(void) { - PyObject *typ, *val, *tb; - PyOSErrorObject *env_err; - if (!PyErr_ExceptionMatches(PyExc_OSError)) + if (!PyErr_ExceptionMatches(PyExc_OSError)) { return 0; - PyErr_Fetch(&typ, &val, &tb); - PyErr_NormalizeException(&typ, &val, &tb); - env_err = (PyOSErrorObject *) val; + } + PyObject *exc = PyErr_GetRaisedException(); + PyOSErrorObject *env_err = (PyOSErrorObject *)exc; assert(env_err != NULL); if (env_err->myerrno != NULL) { assert(EINTR > 0 && EINTR < INT_MAX); @@ -764,14 +764,12 @@ _PyIO_trap_eintr(void) int myerrno = PyLong_AsLongAndOverflow(env_err->myerrno, &overflow); PyErr_Clear(); if (myerrno == EINTR) { - Py_DECREF(typ); - Py_DECREF(val); - Py_XDECREF(tb); + Py_DECREF(exc); return 1; } } /* This silences any error set by PyObject_RichCompareBool() */ - PyErr_Restore(typ, val, tb); + PyErr_SetRaisedException(exc); return 0; } @@ -2228,15 +2226,17 @@ bufferedrwpair_writable(rwpair *self, PyObject *Py_UNUSED(ignored)) static PyObject * bufferedrwpair_close(rwpair *self, PyObject *Py_UNUSED(ignored)) { - PyObject *exc = NULL, *val, *tb; + PyObject *exc = NULL; PyObject *ret = _forward_call(self->writer, &_Py_ID(close), NULL); - if (ret == NULL) - PyErr_Fetch(&exc, &val, &tb); - else + if (ret == NULL) { + exc = PyErr_GetRaisedException(); + } + else { Py_DECREF(ret); + } ret = _forward_call(self->reader, &_Py_ID(close), NULL); if (exc != NULL) { - _PyErr_ChainExceptions(exc, val, tb); + _PyErr_ChainExceptions1(exc); Py_CLEAR(ret); } return ret; diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c index f424fb8439d7a8..35a498ce5a8354 100644 --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -88,14 +88,13 @@ static PyObject * fileio_dealloc_warn(fileio *self, PyObject *source) { if (self->fd >= 0 && self->closefd) { - PyObject *exc, *val, *tb; - PyErr_Fetch(&exc, &val, &tb); + PyObject *exc = PyErr_GetRaisedException(); if (PyErr_ResourceWarning(source, 1, "unclosed file %R", source)) { /* Spurious errors can appear at shutdown */ if (PyErr_ExceptionMatches(PyExc_Warning)) PyErr_WriteUnraisable((PyObject *) self); } - PyErr_Restore(exc, val, tb); + PyErr_SetRaisedException(exc); } Py_RETURN_NONE; } @@ -140,7 +139,7 @@ _io_FileIO_close_impl(fileio *self) /*[clinic end generated code: output=7737a319ef3bad0b input=f35231760d54a522]*/ { PyObject *res; - PyObject *exc, *val, *tb; + PyObject *exc; int rc; res = PyObject_CallMethodOneArg((PyObject*)&PyRawIOBase_Type, &_Py_ID(close), (PyObject *)self); @@ -148,20 +147,25 @@ _io_FileIO_close_impl(fileio *self) self->fd = -1; return res; } - if (res == NULL) - PyErr_Fetch(&exc, &val, &tb); + if (res == NULL) { + exc = PyErr_GetRaisedException(); + } if (self->finalizing) { PyObject *r = fileio_dealloc_warn(self, (PyObject *) self); - if (r) + if (r) { Py_DECREF(r); - else + } + else { PyErr_Clear(); + } } rc = internal_close(self); - if (res == NULL) - _PyErr_ChainExceptions(exc, val, tb); - if (rc < 0) + if (res == NULL) { + _PyErr_ChainExceptions1(exc); + } + if (rc < 0) { Py_CLEAR(res); + } return res; } @@ -487,10 +491,9 @@ _io_FileIO___init___impl(fileio *self, PyObject *nameobj, const char *mode, if (!fd_is_own) self->fd = -1; if (self->fd >= 0) { - PyObject *exc, *val, *tb; - PyErr_Fetch(&exc, &val, &tb); + PyObject *exc = PyErr_GetRaisedException(); internal_close(self); - _PyErr_ChainExceptions(exc, val, tb); + _PyErr_ChainExceptions1(exc); } done: diff --git a/Modules/_io/iobase.c b/Modules/_io/iobase.c index 7b9391ec54d732..682ed000eb1fd9 100644 --- a/Modules/_io/iobase.c +++ b/Modules/_io/iobase.c @@ -220,7 +220,6 @@ static PyObject * _io__IOBase_close_impl(PyObject *self) /*[clinic end generated code: output=63c6a6f57d783d6d input=f4494d5c31dbc6b7]*/ { - PyObject *res, *exc, *val, *tb; int rc, closed = iobase_is_closed(self); if (closed < 0) { @@ -230,11 +229,11 @@ _io__IOBase_close_impl(PyObject *self) Py_RETURN_NONE; } - res = PyObject_CallMethodNoArgs(self, &_Py_ID(flush)); + PyObject *res = PyObject_CallMethodNoArgs(self, &_Py_ID(flush)); - PyErr_Fetch(&exc, &val, &tb); + PyObject *exc = PyErr_GetRaisedException(); rc = PyObject_SetAttr(self, &_Py_ID(__IOBase_closed), Py_True); - _PyErr_ChainExceptions(exc, val, tb); + _PyErr_ChainExceptions1(exc); if (rc < 0) { Py_CLEAR(res); } @@ -252,11 +251,10 @@ static void iobase_finalize(PyObject *self) { PyObject *res; - PyObject *error_type, *error_value, *error_traceback; int closed; /* Save the current exception, if any. */ - PyErr_Fetch(&error_type, &error_value, &error_traceback); + PyObject *exc = PyErr_GetRaisedException(); /* If `closed` doesn't exist or can't be evaluated as bool, then the object is probably in an unusable state, so ignore. */ @@ -297,7 +295,7 @@ iobase_finalize(PyObject *self) } /* Restore the saved exception. */ - PyErr_Restore(error_type, error_value, error_traceback); + PyErr_SetRaisedException(exc); } int diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index fbf0bf46840374..3ff84cb623af74 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -2827,11 +2827,10 @@ _io_TextIOWrapper_tell_impl(textio *self) fail: if (saved_state) { - PyObject *type, *value, *traceback; - PyErr_Fetch(&type, &value, &traceback); + PyObject *exc = PyErr_GetRaisedException(); res = PyObject_CallMethodOneArg( self->decoder, &_Py_ID(setstate), saved_state); - _PyErr_ChainExceptions(type, value, traceback); + _PyErr_ChainExceptions1(exc); Py_DECREF(saved_state); Py_XDECREF(res); } @@ -3028,24 +3027,28 @@ _io_TextIOWrapper_close_impl(textio *self) Py_RETURN_NONE; /* stream already closed */ } else { - PyObject *exc = NULL, *val, *tb; + PyObject *exc = NULL; if (self->finalizing) { res = PyObject_CallMethodOneArg(self->buffer, &_Py_ID(_dealloc_warn), (PyObject *)self); - if (res) + if (res) { Py_DECREF(res); - else + } + else { PyErr_Clear(); + } } res = PyObject_CallMethodNoArgs((PyObject *)self, &_Py_ID(flush)); - if (res == NULL) - PyErr_Fetch(&exc, &val, &tb); - else + if (res == NULL) { + exc = PyErr_GetRaisedException(); + } + else { Py_DECREF(res); + } res = PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(close)); if (exc != NULL) { - _PyErr_ChainExceptions(exc, val, tb); + _PyErr_ChainExceptions1(exc); Py_CLEAR(res); } return res; diff --git a/Modules/_io/winconsoleio.c b/Modules/_io/winconsoleio.c index e913d831874617..de07b50f5ce4cb 100644 --- a/Modules/_io/winconsoleio.c +++ b/Modules/_io/winconsoleio.c @@ -192,7 +192,7 @@ _io__WindowsConsoleIO_close_impl(winconsoleio *self) /*[clinic end generated code: output=27ef95b66c29057b input=68c4e5754f8136c2]*/ { PyObject *res; - PyObject *exc, *val, *tb; + PyObject *exc; int rc; res = PyObject_CallMethodOneArg((PyObject*)&PyRawIOBase_Type, &_Py_ID(close), (PyObject*)self); @@ -200,13 +200,16 @@ _io__WindowsConsoleIO_close_impl(winconsoleio *self) self->fd = -1; return res; } - if (res == NULL) - PyErr_Fetch(&exc, &val, &tb); + if (res == NULL) { + exc = PyErr_GetRaisedException(); + } rc = internal_close(self); - if (res == NULL) - _PyErr_ChainExceptions(exc, val, tb); - if (rc < 0) + if (res == NULL) { + _PyErr_ChainExceptions1(exc); + } + if (rc < 0) { Py_CLEAR(res); + } return res; } diff --git a/Modules/_lsprof.c b/Modules/_lsprof.c index 3237d796dc2961..83d034ae7eed78 100644 --- a/Modules/_lsprof.c +++ b/Modules/_lsprof.c @@ -348,8 +348,7 @@ ptrace_enter_call(PyObject *self, void *key, PyObject *userObj) * exception, and some of the code under here assumes that * PyErr_* is its own to mess around with, so we have to * save and restore any current exception. */ - PyObject *last_type, *last_value, *last_tb; - PyErr_Fetch(&last_type, &last_value, &last_tb); + PyObject *exc = PyErr_GetRaisedException(); profEntry = getEntry(pObj, key); if (profEntry == NULL) { @@ -374,7 +373,7 @@ ptrace_enter_call(PyObject *self, void *key, PyObject *userObj) initContext(pObj, pContext, profEntry); restorePyerr: - PyErr_Restore(last_type, last_value, last_tb); + PyErr_SetRaisedException(exc); } static void diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 4c07d5e0b61f8c..fb61ef82ef869b 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -908,7 +908,6 @@ final_callback(sqlite3_context *context) PyObject* function_result; PyObject** aggregate_instance; int ok; - PyObject *exception, *value, *tb; aggregate_instance = (PyObject**)sqlite3_aggregate_context(context, 0); if (aggregate_instance == NULL) { @@ -923,7 +922,7 @@ final_callback(sqlite3_context *context) } // Keep the exception (if any) of the last call to step, value, or inverse - PyErr_Fetch(&exception, &value, &tb); + PyObject *exc = PyErr_GetRaisedException(); callback_context *ctx = (callback_context *)sqlite3_user_data(context); assert(ctx != NULL); @@ -938,7 +937,7 @@ final_callback(sqlite3_context *context) } if (!ok) { int attr_err = PyErr_ExceptionMatches(PyExc_AttributeError); - _PyErr_ChainExceptions(exception, value, tb); + _PyErr_ChainExceptions1(exc); /* Note: contrary to the step, value, and inverse callbacks, SQLite * does _not_, as of SQLite 3.38.0, propagate errors to sqlite3_step() @@ -949,7 +948,7 @@ final_callback(sqlite3_context *context) : "user-defined aggregate's 'finalize' method raised error"); } else { - PyErr_Restore(exception, value, tb); + PyErr_SetRaisedException(exc); } error: @@ -2274,15 +2273,14 @@ pysqlite_connection_exit_impl(pysqlite_Connection *self, PyObject *exc_type, if (commit) { /* Commit failed; try to rollback in order to unlock the database. * If rollback also fails, chain the exceptions. */ - PyObject *exc, *val, *tb; - PyErr_Fetch(&exc, &val, &tb); + PyObject *exc = PyErr_GetRaisedException(); result = pysqlite_connection_rollback_impl(self); if (result == NULL) { - _PyErr_ChainExceptions(exc, val, tb); + _PyErr_ChainExceptions1(exc); } else { Py_DECREF(result); - PyErr_Restore(exc, val, tb); + PyErr_SetRaisedException(exc); } } return NULL; diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c index 6f7970cf8197a2..caeedbddb8d88b 100644 --- a/Modules/_sqlite/cursor.c +++ b/Modules/_sqlite/cursor.c @@ -705,11 +705,10 @@ bind_parameters(pysqlite_state *state, pysqlite_Statement *self, Py_DECREF(adapted); if (rc != SQLITE_OK) { - PyObject *exc, *val, *tb; - PyErr_Fetch(&exc, &val, &tb); + PyObject *exc = PyErr_GetRaisedException(); sqlite3 *db = sqlite3_db_handle(self->st); _pysqlite_seterror(state, db); - _PyErr_ChainExceptions(exc, val, tb); + _PyErr_ChainExceptions1(exc); return; } } @@ -765,11 +764,10 @@ bind_parameters(pysqlite_state *state, pysqlite_Statement *self, Py_DECREF(adapted); if (rc != SQLITE_OK) { - PyObject *exc, *val, *tb; - PyErr_Fetch(&exc, &val, &tb); + PyObject *exc = PyErr_GetRaisedException(); sqlite3 *db = sqlite3_db_handle(self->st); _pysqlite_seterror(state, db); - _PyErr_ChainExceptions(exc, val, tb); + _PyErr_ChainExceptions1(exc); return; } } diff --git a/Modules/_testcapi/heaptype.c b/Modules/_testcapi/heaptype.c index 39639f7ed048f2..df2a061ed82b06 100644 --- a/Modules/_testcapi/heaptype.c +++ b/Modules/_testcapi/heaptype.c @@ -623,16 +623,15 @@ heapctypesubclasswithfinalizer_init(PyObject *self, PyObject *args, PyObject *kw static void heapctypesubclasswithfinalizer_finalize(PyObject *self) { - PyObject *error_type, *error_value, *error_traceback, *m; PyObject *oldtype = NULL, *newtype = NULL, *refcnt = NULL; /* Save the current exception, if any. */ - PyErr_Fetch(&error_type, &error_value, &error_traceback); + PyObject *exc = PyErr_GetRaisedException(); if (_testcapimodule == NULL) { goto cleanup_finalize; } - m = PyState_FindModule(_testcapimodule); + PyObject *m = PyState_FindModule(_testcapimodule); if (m == NULL) { goto cleanup_finalize; } @@ -667,7 +666,7 @@ heapctypesubclasswithfinalizer_finalize(PyObject *self) Py_XDECREF(refcnt); /* Restore the saved exception. */ - PyErr_Restore(error_type, error_value, error_traceback); + PyErr_SetRaisedException(exc); } static PyType_Slot HeapCTypeSubclassWithFinalizer_slots[] = { diff --git a/Modules/_testcapi/watchers.c b/Modules/_testcapi/watchers.c index 2e8fe1dbf78651..d9ace632768ae8 100644 --- a/Modules/_testcapi/watchers.c +++ b/Modules/_testcapi/watchers.c @@ -389,16 +389,15 @@ allocate_too_many_code_watchers(PyObject *self, PyObject *args) watcher_ids[i] = watcher_id; num_watchers++; } - PyObject *type, *value, *traceback; - PyErr_Fetch(&type, &value, &traceback); + PyObject *exc = PyErr_GetRaisedException(); for (int i = 0; i < num_watchers; i++) { if (PyCode_ClearWatcher(watcher_ids[i]) < 0) { PyErr_WriteUnraisable(Py_None); break; } } - if (type) { - PyErr_Restore(type, value, traceback); + if (exc) { + PyErr_SetRaisedException(exc); return NULL; } else if (PyErr_Occurred()) { @@ -578,16 +577,15 @@ allocate_too_many_func_watchers(PyObject *self, PyObject *args) watcher_ids[i] = watcher_id; num_watchers++; } - PyObject *type, *value, *traceback; - PyErr_Fetch(&type, &value, &traceback); + PyObject *exc = PyErr_GetRaisedException(); for (int i = 0; i < num_watchers; i++) { if (PyFunction_ClearWatcher(watcher_ids[i]) < 0) { PyErr_WriteUnraisable(Py_None); break; } } - if (type) { - PyErr_Restore(type, value, traceback); + if (exc) { + PyErr_SetRaisedException(exc); return NULL; } else if (PyErr_Occurred()) { diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 6bb424282a875d..fc716a3564d39a 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -1611,19 +1611,18 @@ static void slot_tp_del(PyObject *self) { PyObject *del, *res; - PyObject *error_type, *error_value, *error_traceback; /* Temporarily resurrect the object. */ assert(Py_REFCNT(self) == 0); Py_SET_REFCNT(self, 1); /* Save the current exception, if any. */ - PyErr_Fetch(&error_type, &error_value, &error_traceback); + PyObject *exc = PyErr_GetRaisedException(); PyObject *tp_del = PyUnicode_InternFromString("__tp_del__"); if (tp_del == NULL) { PyErr_WriteUnraisable(NULL); - PyErr_Restore(error_type, error_value, error_traceback); + PyErr_SetRaisedException(exc); return; } /* Execute __del__ method, if any. */ @@ -1638,7 +1637,7 @@ slot_tp_del(PyObject *self) } /* Restore the saved exception. */ - PyErr_Restore(error_type, error_value, error_traceback); + PyErr_SetRaisedException(exc); /* Undo the temporary resurrection; can't use DECREF here, it would * cause a recursive call. diff --git a/Modules/_xxinterpchannelsmodule.c b/Modules/_xxinterpchannelsmodule.c index 60538c31874864..a0cd4a2363fb53 100644 --- a/Modules/_xxinterpchannelsmodule.c +++ b/Modules/_xxinterpchannelsmodule.c @@ -100,9 +100,9 @@ add_new_type(PyObject *mod, PyType_Spec *spec, crossinterpdatafunc shared) static int _release_xid_data(_PyCrossInterpreterData *data, int ignoreexc) { - PyObject *exctype, *excval, *exctb; + PyObject *exc; if (ignoreexc) { - PyErr_Fetch(&exctype, &excval, &exctb); + exc = PyErr_GetRaisedException(); } int res = _PyCrossInterpreterData_Release(data); if (res < 0) { @@ -125,7 +125,7 @@ _release_xid_data(_PyCrossInterpreterData *data, int ignoreexc) } } if (ignoreexc) { - PyErr_Restore(exctype, excval, exctb); + PyErr_SetRaisedException(exc); } return res; } diff --git a/Modules/_zoneinfo.c b/Modules/_zoneinfo.c index 6e1a37611b6152..c215a75b804fdb 100644 --- a/Modules/_zoneinfo.c +++ b/Modules/_zoneinfo.c @@ -270,10 +270,9 @@ zoneinfo_new_instance(zoneinfo_state *state, PyTypeObject *type, PyObject *key) Py_CLEAR(self); cleanup: if (file_obj != NULL) { - PyObject *exc, *val, *tb; - PyErr_Fetch(&exc, &val, &tb); + PyObject *exc = PyErr_GetRaisedException(); PyObject *tmp = PyObject_CallMethod(file_obj, "close", NULL); - _PyErr_ChainExceptions(exc, val, tb); + _PyErr_ChainExceptions1(exc); if (tmp == NULL) { Py_CLEAR(self); } diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 51aa89ef715a19..6ea216b3bf5e79 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -14759,10 +14759,9 @@ ScandirIterator_exit(ScandirIterator *self, PyObject *args) static void ScandirIterator_finalize(ScandirIterator *iterator) { - PyObject *error_type, *error_value, *error_traceback; /* Save the current exception, if any. */ - PyErr_Fetch(&error_type, &error_value, &error_traceback); + PyObject *exc = PyErr_GetRaisedException(); if (!ScandirIterator_is_closed(iterator)) { ScandirIterator_closedir(iterator); @@ -14779,7 +14778,7 @@ ScandirIterator_finalize(ScandirIterator *iterator) path_cleanup(&iterator->path); /* Restore the saved exception. */ - PyErr_Restore(error_type, error_value, error_traceback); + PyErr_SetRaisedException(exc); } static void diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c index 44a5ecf63e9d1e..cd26eca351c0ed 100644 --- a/Modules/signalmodule.c +++ b/Modules/signalmodule.c @@ -234,14 +234,13 @@ signal_default_int_handler_impl(PyObject *module, int signalnum, static int report_wakeup_write_error(void *data) { - PyObject *exc, *val, *tb; int save_errno = errno; errno = (int) (intptr_t) data; - PyErr_Fetch(&exc, &val, &tb); + PyObject *exc = PyErr_GetRaisedException(); PyErr_SetFromErrno(PyExc_OSError); _PyErr_WriteUnraisableMsg("when trying to write to the signal wakeup fd", NULL); - PyErr_Restore(exc, val, tb); + PyErr_SetRaisedException(exc); errno = save_errno; return 0; } @@ -252,14 +251,13 @@ report_wakeup_send_error(void* data) { int send_errno = (int) (intptr_t) data; - PyObject *exc, *val, *tb; - PyErr_Fetch(&exc, &val, &tb); + PyObject *exc = PyErr_GetRaisedException(); /* PyErr_SetExcFromWindowsErr() invokes FormatMessage() which recognizes the error codes used by both GetLastError() and WSAGetLastError */ PyErr_SetExcFromWindowsErr(PyExc_OSError, send_errno); _PyErr_WriteUnraisableMsg("when trying to send to the signal wakeup fd", NULL); - PyErr_Restore(exc, val, tb); + PyErr_SetRaisedException(exc); return 0; } #endif /* MS_WINDOWS */ diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 2d300f19436b1a..00608be38f61bb 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -5175,10 +5175,9 @@ static void sock_finalize(PySocketSockObject *s) { SOCKET_T fd; - PyObject *error_type, *error_value, *error_traceback; /* Save the current exception, if any. */ - PyErr_Fetch(&error_type, &error_value, &error_traceback); + PyObject *exc = PyErr_GetRaisedException(); if (s->sock_fd != INVALID_SOCKET) { if (PyErr_ResourceWarning((PyObject *)s, 1, "unclosed %R", s)) { @@ -5202,7 +5201,7 @@ sock_finalize(PySocketSockObject *s) } /* Restore the saved exception. */ - PyErr_Restore(error_type, error_value, error_traceback); + PyErr_SetRaisedException(exc); } static void From 89b4c1205327cc8032f4a39e3dfbdb59009a0704 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 24 Feb 2023 17:58:10 -0500 Subject: [PATCH 184/247] gh-102209: Disable the timeout in test_implied_dirs_performance. (#102225) Disable the timeout in test_implied_dirs_performance. Workaround for #102209 until I can work out a more robust test for linearity. --- Lib/test/test_zipfile/test_path.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_zipfile/test_path.py b/Lib/test/test_zipfile/test_path.py index 92fda690b2fc50..53cbef15a17dc6 100644 --- a/Lib/test/test_zipfile/test_path.py +++ b/Lib/test/test_zipfile/test_path.py @@ -330,7 +330,8 @@ def test_joinpath_constant_time(self): # Check the file iterated all items assert entries.count == self.HUGE_ZIPFILE_NUM_ENTRIES - @set_timeout(3) + # timeout disabled due to #102209 + # @set_timeout(3) def test_implied_dirs_performance(self): data = ['/'.join(string.ascii_lowercase + str(n)) for n in range(10000)] zipfile.CompleteDirs._implied_dirs(data) From 54dfa14c5a94b893b67a4d9e9e403ff538ce9023 Mon Sep 17 00:00:00 2001 From: Ionite Date: Fri, 24 Feb 2023 18:02:04 -0500 Subject: [PATCH 185/247] gh-101765: Fix SystemError / segmentation fault in iter `__reduce__` when internal access of `builtins.__dict__` exhausts the iterator (#101769) --- Lib/test/test_iter.py | 81 +++++++++++++++++++ ...-02-10-07-21-47.gh-issue-101765.MO5LlC.rst | 1 + Objects/bytearrayobject.c | 11 ++- Objects/bytesobject.c | 11 ++- Objects/genericaliasobject.c | 11 ++- Objects/iterobject.c | 22 +++-- Objects/listobject.c | 12 ++- Objects/tupleobject.c | 11 ++- Objects/unicodeobject.c | 11 ++- 9 files changed, 148 insertions(+), 23 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-10-07-21-47.gh-issue-101765.MO5LlC.rst diff --git a/Lib/test/test_iter.py b/Lib/test/test_iter.py index acbdcb5f302060..6ab9b7a7230309 100644 --- a/Lib/test/test_iter.py +++ b/Lib/test/test_iter.py @@ -7,6 +7,9 @@ from test.support import check_free_after_iterating, ALWAYS_EQ, NEVER_EQ import pickle import collections.abc +import functools +import contextlib +import builtins # Test result of triple loop (too big to inline) TRIPLETS = [(0, 0, 0), (0, 0, 1), (0, 0, 2), @@ -91,6 +94,12 @@ def __call__(self): raise IndexError # Emergency stop return i +class EmptyIterClass: + def __len__(self): + return 0 + def __getitem__(self, i): + raise StopIteration + # Main test suite class TestCase(unittest.TestCase): @@ -238,6 +247,78 @@ def test_mutating_seq_class_exhausted_iter(self): self.assertEqual(list(empit), [5, 6]) self.assertEqual(list(a), [0, 1, 2, 3, 4, 5, 6]) + def test_reduce_mutating_builtins_iter(self): + # This is a reproducer of issue #101765 + # where iter `__reduce__` calls could lead to a segfault or SystemError + # depending on the order of C argument evaluation, which is undefined + + # Backup builtins + builtins_dict = builtins.__dict__ + orig = {"iter": iter, "reversed": reversed} + + def run(builtin_name, item, sentinel=None): + it = iter(item) if sentinel is None else iter(item, sentinel) + + class CustomStr: + def __init__(self, name, iterator): + self.name = name + self.iterator = iterator + def __hash__(self): + return hash(self.name) + def __eq__(self, other): + # Here we exhaust our iterator, possibly changing + # its `it_seq` pointer to NULL + # The `__reduce__` call should correctly get + # the pointers after this call + list(self.iterator) + return other == self.name + + # del is required here + # to not prematurely call __eq__ from + # the hash collision with the old key + del builtins_dict[builtin_name] + builtins_dict[CustomStr(builtin_name, it)] = orig[builtin_name] + + return it.__reduce__() + + types = [ + (EmptyIterClass(),), + (bytes(8),), + (bytearray(8),), + ((1, 2, 3),), + (lambda: 0, 0), + (tuple[int],) # GenericAlias + ] + + try: + run_iter = functools.partial(run, "iter") + # The returned value of `__reduce__` should not only be valid + # but also *empty*, as `it` was exhausted during `__eq__` + # i.e "xyz" returns (iter, ("",)) + self.assertEqual(run_iter("xyz"), (orig["iter"], ("",))) + self.assertEqual(run_iter([1, 2, 3]), (orig["iter"], ([],))) + + # _PyEval_GetBuiltin is also called for `reversed` in a branch of + # listiter_reduce_general + self.assertEqual( + run("reversed", orig["reversed"](list(range(8)))), + (iter, ([],)) + ) + + for case in types: + self.assertEqual(run_iter(*case), (orig["iter"], ((),))) + finally: + # Restore original builtins + for key, func in orig.items(): + # need to suppress KeyErrors in case + # a failed test deletes the key without setting anything + with contextlib.suppress(KeyError): + # del is required here + # to not invoke our custom __eq__ from + # the hash collision with the old key + del builtins_dict[key] + builtins_dict[key] = func + # Test a new_style class with __iter__ but no next() method def test_new_style_iter_class(self): class IterClass(object): diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-10-07-21-47.gh-issue-101765.MO5LlC.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-10-07-21-47.gh-issue-101765.MO5LlC.rst new file mode 100644 index 00000000000000..cc99779a944ec6 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-10-07-21-47.gh-issue-101765.MO5LlC.rst @@ -0,0 +1 @@ +Fix SystemError / segmentation fault in iter ``__reduce__`` when internal access of ``builtins.__dict__`` keys mutates the iter object. diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c index 072089e39aa207..49d4dd524696a5 100644 --- a/Objects/bytearrayobject.c +++ b/Objects/bytearrayobject.c @@ -2391,11 +2391,16 @@ PyDoc_STRVAR(length_hint_doc, static PyObject * bytearrayiter_reduce(bytesiterobject *it, PyObject *Py_UNUSED(ignored)) { + PyObject *iter = _PyEval_GetBuiltin(&_Py_ID(iter)); + + /* _PyEval_GetBuiltin can invoke arbitrary code, + * call must be before access of iterator pointers. + * see issue #101765 */ + if (it->it_seq != NULL) { - return Py_BuildValue("N(O)n", _PyEval_GetBuiltin(&_Py_ID(iter)), - it->it_seq, it->it_index); + return Py_BuildValue("N(O)n", iter, it->it_seq, it->it_index); } else { - return Py_BuildValue("N(())", _PyEval_GetBuiltin(&_Py_ID(iter))); + return Py_BuildValue("N(())", iter); } } diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index ba2c2e978c6e42..657443f31fa709 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -3169,11 +3169,16 @@ PyDoc_STRVAR(length_hint_doc, static PyObject * striter_reduce(striterobject *it, PyObject *Py_UNUSED(ignored)) { + PyObject *iter = _PyEval_GetBuiltin(&_Py_ID(iter)); + + /* _PyEval_GetBuiltin can invoke arbitrary code, + * call must be before access of iterator pointers. + * see issue #101765 */ + if (it->it_seq != NULL) { - return Py_BuildValue("N(O)n", _PyEval_GetBuiltin(&_Py_ID(iter)), - it->it_seq, it->it_index); + return Py_BuildValue("N(O)n", iter, it->it_seq, it->it_index); } else { - return Py_BuildValue("N(())", _PyEval_GetBuiltin(&_Py_ID(iter))); + return Py_BuildValue("N(())", iter); } } diff --git a/Objects/genericaliasobject.c b/Objects/genericaliasobject.c index 675fd496eefdaf..888cb16edd1b46 100644 --- a/Objects/genericaliasobject.c +++ b/Objects/genericaliasobject.c @@ -877,8 +877,17 @@ ga_iter_clear(PyObject *self) { static PyObject * ga_iter_reduce(PyObject *self, PyObject *Py_UNUSED(ignored)) { + PyObject *iter = _PyEval_GetBuiltin(&_Py_ID(iter)); gaiterobject *gi = (gaiterobject *)self; - return Py_BuildValue("N(O)", _PyEval_GetBuiltin(&_Py_ID(iter)), gi->obj); + + /* _PyEval_GetBuiltin can invoke arbitrary code, + * call must be before access of iterator pointers. + * see issue #101765 */ + + if (gi->obj) + return Py_BuildValue("N(O)", iter, gi->obj); + else + return Py_BuildValue("N(())", iter); } static PyMethodDef ga_iter_methods[] = { diff --git a/Objects/iterobject.c b/Objects/iterobject.c index cfd6d0a7c959c9..149b701b68e91a 100644 --- a/Objects/iterobject.c +++ b/Objects/iterobject.c @@ -102,11 +102,16 @@ PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list( static PyObject * iter_reduce(seqiterobject *it, PyObject *Py_UNUSED(ignored)) { + PyObject *iter = _PyEval_GetBuiltin(&_Py_ID(iter)); + + /* _PyEval_GetBuiltin can invoke arbitrary code, + * call must be before access of iterator pointers. + * see issue #101765 */ + if (it->it_seq != NULL) - return Py_BuildValue("N(O)n", _PyEval_GetBuiltin(&_Py_ID(iter)), - it->it_seq, it->it_index); + return Py_BuildValue("N(O)n", iter, it->it_seq, it->it_index); else - return Py_BuildValue("N(())", _PyEval_GetBuiltin(&_Py_ID(iter))); + return Py_BuildValue("N(())", iter); } PyDoc_STRVAR(reduce_doc, "Return state information for pickling."); @@ -239,11 +244,16 @@ calliter_iternext(calliterobject *it) static PyObject * calliter_reduce(calliterobject *it, PyObject *Py_UNUSED(ignored)) { + PyObject *iter = _PyEval_GetBuiltin(&_Py_ID(iter)); + + /* _PyEval_GetBuiltin can invoke arbitrary code, + * call must be before access of iterator pointers. + * see issue #101765 */ + if (it->it_callable != NULL && it->it_sentinel != NULL) - return Py_BuildValue("N(OO)", _PyEval_GetBuiltin(&_Py_ID(iter)), - it->it_callable, it->it_sentinel); + return Py_BuildValue("N(OO)", iter, it->it_callable, it->it_sentinel); else - return Py_BuildValue("N(())", _PyEval_GetBuiltin(&_Py_ID(iter))); + return Py_BuildValue("N(())", iter); } static PyMethodDef calliter_methods[] = { diff --git a/Objects/listobject.c b/Objects/listobject.c index ca6b712311340a..494b40178f9f27 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -3444,18 +3444,22 @@ listiter_reduce_general(void *_it, int forward) { PyObject *list; + /* _PyEval_GetBuiltin can invoke arbitrary code, + * call must be before access of iterator pointers. + * see issue #101765 */ + /* the objects are not the same, index is of different types! */ if (forward) { + PyObject *iter = _PyEval_GetBuiltin(&_Py_ID(iter)); _PyListIterObject *it = (_PyListIterObject *)_it; if (it->it_seq) { - return Py_BuildValue("N(O)n", _PyEval_GetBuiltin(&_Py_ID(iter)), - it->it_seq, it->it_index); + return Py_BuildValue("N(O)n", iter, it->it_seq, it->it_index); } } else { + PyObject *reversed = _PyEval_GetBuiltin(&_Py_ID(reversed)); listreviterobject *it = (listreviterobject *)_it; if (it->it_seq) { - return Py_BuildValue("N(O)n", _PyEval_GetBuiltin(&_Py_ID(reversed)), - it->it_seq, it->it_index); + return Py_BuildValue("N(O)n", reversed, it->it_seq, it->it_index); } } /* empty iterator, create an empty list */ diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c index 7d6d0e1bea249e..6ee93ab5adc281 100644 --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -1048,11 +1048,16 @@ PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list( static PyObject * tupleiter_reduce(_PyTupleIterObject *it, PyObject *Py_UNUSED(ignored)) { + PyObject *iter = _PyEval_GetBuiltin(&_Py_ID(iter)); + + /* _PyEval_GetBuiltin can invoke arbitrary code, + * call must be before access of iterator pointers. + * see issue #101765 */ + if (it->it_seq) - return Py_BuildValue("N(O)n", _PyEval_GetBuiltin(&_Py_ID(iter)), - it->it_seq, it->it_index); + return Py_BuildValue("N(O)n", iter, it->it_seq, it->it_index); else - return Py_BuildValue("N(())", _PyEval_GetBuiltin(&_Py_ID(iter))); + return Py_BuildValue("N(())", iter); } static PyObject * diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index f0c7aa7707fdb5..6403e359c70714 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -14784,14 +14784,19 @@ PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list( static PyObject * unicodeiter_reduce(unicodeiterobject *it, PyObject *Py_UNUSED(ignored)) { + PyObject *iter = _PyEval_GetBuiltin(&_Py_ID(iter)); + + /* _PyEval_GetBuiltin can invoke arbitrary code, + * call must be before access of iterator pointers. + * see issue #101765 */ + if (it->it_seq != NULL) { - return Py_BuildValue("N(O)n", _PyEval_GetBuiltin(&_Py_ID(iter)), - it->it_seq, it->it_index); + return Py_BuildValue("N(O)n", iter, it->it_seq, it->it_index); } else { PyObject *u = unicode_new_empty(); if (u == NULL) return NULL; - return Py_BuildValue("N(N)", _PyEval_GetBuiltin(&_Py_ID(iter)), u); + return Py_BuildValue("N(N)", iter, u); } } From 56e93c8020e89e1712aa238574bca2076a225028 Mon Sep 17 00:00:00 2001 From: SKO <41810398+uyw4687@users.noreply.github.com> Date: Sat, 25 Feb 2023 11:26:40 +0900 Subject: [PATCH 186/247] gh-95675: fix uid and gid at test_add_dir_getmember (gh-102207) Co-authored-by: Seonkyo Ok --- Lib/test/test_tarfile.py | 13 +++++++------ Misc/ACKS | 1 + 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index f15a800976681c..75b60e9a50e78a 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -225,18 +225,19 @@ def test_add_dir_getmember(self): self.add_dir_and_getmember('bar') self.add_dir_and_getmember('a'*101) - @unittest.skipIf( - (hasattr(os, 'getuid') and os.getuid() > 0o777_7777) or - (hasattr(os, 'getgid') and os.getgid() > 0o777_7777), - "uid or gid too high for USTAR format." - ) + @unittest.skipUnless(hasattr(os, "getuid") and hasattr(os, "getgid"), + "Missing getuid or getgid implementation") def add_dir_and_getmember(self, name): + def filter(tarinfo): + tarinfo.uid = tarinfo.gid = 100 + return tarinfo + with os_helper.temp_cwd(): with tarfile.open(tmpname, 'w') as tar: tar.format = tarfile.USTAR_FORMAT try: os.mkdir(name) - tar.add(name) + tar.add(name, filter=filter) finally: os.rmdir(name) with tarfile.open(tmpname) as tar: diff --git a/Misc/ACKS b/Misc/ACKS index 43e420a1373cb7..3403aee4cc78ff 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1308,6 +1308,7 @@ Jon Oberheide Milan Oberkirch Pascal Oberndoerfer Géry Ogam +Seonkyo Ok Jeffrey Ollie Adam Olsen Bryan Olson From 5f11478ce7fda826d399530af4c5ca96c592f144 Mon Sep 17 00:00:00 2001 From: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> Date: Sat, 25 Feb 2023 12:21:36 +0530 Subject: [PATCH 187/247] GH-102126: fix deadlock at shutdown when clearing thread states (#102222) --- .../2023-02-24-17-59-39.gh-issue-102126.HTT8Vc.rst | 1 + Python/pystate.c | 13 ++++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-24-17-59-39.gh-issue-102126.HTT8Vc.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-24-17-59-39.gh-issue-102126.HTT8Vc.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-24-17-59-39.gh-issue-102126.HTT8Vc.rst new file mode 100644 index 00000000000000..68c43688c3df03 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-24-17-59-39.gh-issue-102126.HTT8Vc.rst @@ -0,0 +1 @@ +Fix deadlock at shutdown when clearing thread states if any finalizer tries to acquire the runtime head lock. Patch by Kumar Aditya. diff --git a/Python/pystate.c b/Python/pystate.c index 32b17fd19e348f..3c655bf3895850 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -754,12 +754,19 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) _PyErr_Clear(tstate); } + // Clear the current/main thread state last. HEAD_LOCK(runtime); - // XXX Clear the current/main thread state last. - for (PyThreadState *p = interp->threads.head; p != NULL; p = p->next) { + PyThreadState *p = interp->threads.head; + HEAD_UNLOCK(runtime); + while (p != NULL) { + // See https://github.com/python/cpython/issues/102126 + // Must be called without HEAD_LOCK held as it can deadlock + // if any finalizer tries to acquire that lock. PyThreadState_Clear(p); + HEAD_LOCK(runtime); + p = p->next; + HEAD_UNLOCK(runtime); } - HEAD_UNLOCK(runtime); /* It is possible that any of the objects below have a finalizer that runs Python code or otherwise relies on a thread state From b7c11264476ccc11e4bdf4bd3c3664ccd1b7c5f9 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sat, 25 Feb 2023 11:42:45 +0200 Subject: [PATCH 188/247] gh-101100: Fix Sphinx warnings in `decimal` module (#102125) Co-authored-by: C.A.M. Gerlach --- Doc/library/decimal.rst | 168 ++++++++++++++++++++-------------------- 1 file changed, 84 insertions(+), 84 deletions(-) diff --git a/Doc/library/decimal.rst b/Doc/library/decimal.rst index fec9b86864c578..6187098a752053 100644 --- a/Doc/library/decimal.rst +++ b/Doc/library/decimal.rst @@ -40,23 +40,23 @@ decimal floating point arithmetic. It offers several advantages over the people learn at school." -- excerpt from the decimal arithmetic specification. * Decimal numbers can be represented exactly. In contrast, numbers like - :const:`1.1` and :const:`2.2` do not have exact representations in binary + ``1.1`` and ``2.2`` do not have exact representations in binary floating point. End users typically would not expect ``1.1 + 2.2`` to display - as :const:`3.3000000000000003` as it does with binary floating point. + as ``3.3000000000000003`` as it does with binary floating point. * The exactness carries over into arithmetic. In decimal floating point, ``0.1 + 0.1 + 0.1 - 0.3`` is exactly equal to zero. In binary floating point, the result - is :const:`5.5511151231257827e-017`. While near to zero, the differences + is ``5.5511151231257827e-017``. While near to zero, the differences prevent reliable equality testing and differences can accumulate. For this reason, decimal is preferred in accounting applications which have strict equality invariants. * The decimal module incorporates a notion of significant places so that ``1.30 - + 1.20`` is :const:`2.50`. The trailing zero is kept to indicate significance. + + 1.20`` is ``2.50``. The trailing zero is kept to indicate significance. This is the customary presentation for monetary applications. For multiplication, the "schoolbook" approach uses all the figures in the - multiplicands. For instance, ``1.3 * 1.2`` gives :const:`1.56` while ``1.30 * - 1.20`` gives :const:`1.5600`. + multiplicands. For instance, ``1.3 * 1.2`` gives ``1.56`` while ``1.30 * + 1.20`` gives ``1.5600``. * Unlike hardware based binary floating point, the decimal module has a user alterable precision (defaulting to 28 places) which can be as large as needed for @@ -88,8 +88,8 @@ context for arithmetic, and signals. A decimal number is immutable. It has a sign, coefficient digits, and an exponent. To preserve significance, the coefficient digits do not truncate trailing zeros. Decimals also include special values such as -:const:`Infinity`, :const:`-Infinity`, and :const:`NaN`. The standard also -differentiates :const:`-0` from :const:`+0`. +``Infinity``, ``-Infinity``, and ``NaN``. The standard also +differentiates ``-0`` from ``+0``. The context for arithmetic is an environment specifying precision, rounding rules, limits on exponents, flags indicating the results of operations, and trap @@ -139,8 +139,8 @@ precision, rounding, or enabled traps:: Decimal instances can be constructed from integers, strings, floats, or tuples. Construction from an integer or a float performs an exact conversion of the value of that integer or float. Decimal numbers include special values such as -:const:`NaN` which stands for "Not a number", positive and negative -:const:`Infinity`, and :const:`-0`:: +``NaN`` which stands for "Not a number", positive and negative +``Infinity``, and ``-0``:: >>> getcontext().prec = 28 >>> Decimal(10) @@ -250,7 +250,7 @@ And some mathematical functions are also available to Decimal: >>> Decimal('10').log10() Decimal('1') -The :meth:`quantize` method rounds a number to a fixed exponent. This method is +The :meth:`~Decimal.quantize` method rounds a number to a fixed exponent. This method is useful for monetary applications that often round results to a fixed number of places: @@ -299,7 +299,7 @@ enabled: Contexts also have signal flags for monitoring exceptional conditions encountered during computations. The flags remain set until explicitly cleared, so it is best to clear the flags before each set of monitored computations by -using the :meth:`clear_flags` method. :: +using the :meth:`~Context.clear_flags` method. :: >>> setcontext(ExtendedContext) >>> getcontext().clear_flags() @@ -309,12 +309,12 @@ using the :meth:`clear_flags` method. :: Context(prec=9, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[Inexact, Rounded], traps=[]) -The *flags* entry shows that the rational approximation to :const:`Pi` was +The *flags* entry shows that the rational approximation to pi was rounded (digits beyond the context precision were thrown away) and that the result is inexact (some of the discarded digits were non-zero). -Individual traps are set using the dictionary in the :attr:`traps` field of a -context: +Individual traps are set using the dictionary in the :attr:`~Context.traps` +attribute of a context: .. doctest:: newcontext @@ -369,7 +369,7 @@ Decimal objects with the fullwidth digits ``'\uff10'`` through ``'\uff19'``. If *value* is a :class:`tuple`, it should have three components, a sign - (:const:`0` for positive or :const:`1` for negative), a :class:`tuple` of + (``0`` for positive or ``1`` for negative), a :class:`tuple` of digits, and an integer exponent. For example, ``Decimal((0, (1, 4, 1, 4), -3))`` returns ``Decimal('1.414')``. @@ -387,7 +387,7 @@ Decimal objects The purpose of the *context* argument is determining what to do if *value* is a malformed string. If the context traps :const:`InvalidOperation`, an exception is raised; otherwise, the constructor returns a new Decimal with the value of - :const:`NaN`. + ``NaN``. Once constructed, :class:`Decimal` objects are immutable. @@ -701,7 +701,7 @@ Decimal objects .. method:: max(other, context=None) Like ``max(self, other)`` except that the context rounding rule is applied - before returning and that :const:`NaN` values are either signaled or + before returning and that ``NaN`` values are either signaled or ignored (depending on the context and whether they are signaling or quiet). @@ -713,7 +713,7 @@ Decimal objects .. method:: min(other, context=None) Like ``min(self, other)`` except that the context rounding rule is applied - before returning and that :const:`NaN` values are either signaled or + before returning and that ``NaN`` values are either signaled or ignored (depending on the context and whether they are signaling or quiet). @@ -744,8 +744,8 @@ Decimal objects .. method:: normalize(context=None) Normalize the number by stripping the rightmost trailing zeros and - converting any result equal to :const:`Decimal('0')` to - :const:`Decimal('0e0')`. Used for producing canonical values for attributes + converting any result equal to ``Decimal('0')`` to + ``Decimal('0e0')``. Used for producing canonical values for attributes of an equivalence class. For example, ``Decimal('32.100')`` and ``Decimal('0.321000e+2')`` both normalize to the equivalent value ``Decimal('32.1')``. @@ -790,7 +790,7 @@ Decimal objects the current thread's context is used. An error is returned whenever the resulting exponent is greater than - :attr:`Emax` or less than :attr:`Etiny`. + :attr:`~Context.Emax` or less than :meth:`~Context.Etiny`. .. method:: radix() @@ -830,7 +830,7 @@ Decimal objects .. method:: same_quantum(other, context=None) Test whether self and other have the same exponent or whether both are - :const:`NaN`. + ``NaN``. This operation is unaffected by context and is quiet: no flags are changed and no rounding is performed. As an exception, the C version may raise @@ -892,11 +892,11 @@ Decimal objects Logical operands ^^^^^^^^^^^^^^^^ -The :meth:`logical_and`, :meth:`logical_invert`, :meth:`logical_or`, -and :meth:`logical_xor` methods expect their arguments to be *logical +The :meth:`~Decimal.logical_and`, :meth:`~Decimal.logical_invert`, :meth:`~Decimal.logical_or`, +and :meth:`~Decimal.logical_xor` methods expect their arguments to be *logical operands*. A *logical operand* is a :class:`Decimal` instance whose exponent and sign are both zero, and whose digits are all either -:const:`0` or :const:`1`. +``0`` or ``1``. .. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -982,7 +982,7 @@ described below. In addition, the module provides three pre-made contexts: exceptions are not raised during computations). Because the traps are disabled, this context is useful for applications that - prefer to have result value of :const:`NaN` or :const:`Infinity` instead of + prefer to have result value of ``NaN`` or ``Infinity`` instead of raising exceptions. This allows an application to complete a run in the presence of conditions that would otherwise halt the program. @@ -1001,8 +1001,8 @@ described below. In addition, the module provides three pre-made contexts: In single threaded environments, it is preferable to not use this context at all. Instead, simply create contexts explicitly as described below. - The default values are :attr:`prec`\ =\ :const:`28`, - :attr:`rounding`\ =\ :const:`ROUND_HALF_EVEN`, + The default values are :attr:`Context.prec`\ =\ ``28``, + :attr:`Context.rounding`\ =\ :const:`ROUND_HALF_EVEN`, and enabled traps for :class:`Overflow`, :class:`InvalidOperation`, and :class:`DivisionByZero`. @@ -1016,7 +1016,7 @@ In addition to the three supplied contexts, new contexts can be created with the default values are copied from the :const:`DefaultContext`. If the *flags* field is not specified or is :const:`None`, all flags are cleared. - *prec* is an integer in the range [:const:`1`, :const:`MAX_PREC`] that sets + *prec* is an integer in the range [``1``, :const:`MAX_PREC`] that sets the precision for arithmetic operations in the context. The *rounding* option is one of the constants listed in the section @@ -1026,20 +1026,20 @@ In addition to the three supplied contexts, new contexts can be created with the contexts should only set traps and leave the flags clear. The *Emin* and *Emax* fields are integers specifying the outer limits allowable - for exponents. *Emin* must be in the range [:const:`MIN_EMIN`, :const:`0`], - *Emax* in the range [:const:`0`, :const:`MAX_EMAX`]. + for exponents. *Emin* must be in the range [:const:`MIN_EMIN`, ``0``], + *Emax* in the range [``0``, :const:`MAX_EMAX`]. - The *capitals* field is either :const:`0` or :const:`1` (the default). If set to - :const:`1`, exponents are printed with a capital :const:`E`; otherwise, a - lowercase :const:`e` is used: :const:`Decimal('6.02e+23')`. + The *capitals* field is either ``0`` or ``1`` (the default). If set to + ``1``, exponents are printed with a capital ``E``; otherwise, a + lowercase ``e`` is used: ``Decimal('6.02e+23')``. - The *clamp* field is either :const:`0` (the default) or :const:`1`. - If set to :const:`1`, the exponent ``e`` of a :class:`Decimal` + The *clamp* field is either ``0`` (the default) or ``1``. + If set to ``1``, the exponent ``e`` of a :class:`Decimal` instance representable in this context is strictly limited to the range ``Emin - prec + 1 <= e <= Emax - prec + 1``. If *clamp* is - :const:`0` then a weaker condition holds: the adjusted exponent of - the :class:`Decimal` instance is at most ``Emax``. When *clamp* is - :const:`1`, a large normal number will, where possible, have its + ``0`` then a weaker condition holds: the adjusted exponent of + the :class:`Decimal` instance is at most :attr:`~Context.Emax`. When *clamp* is + ``1``, a large normal number will, where possible, have its exponent reduced and a corresponding number of zeros added to its coefficient, in order to fit the exponent constraints; this preserves the value of the number but loses information about @@ -1048,13 +1048,13 @@ In addition to the three supplied contexts, new contexts can be created with the >>> Context(prec=6, Emax=999, clamp=1).create_decimal('1.23e999') Decimal('1.23000E+999') - A *clamp* value of :const:`1` allows compatibility with the + A *clamp* value of ``1`` allows compatibility with the fixed-width decimal interchange formats specified in IEEE 754. The :class:`Context` class defines several general purpose methods as well as a large number of methods for doing arithmetic directly in a given context. In addition, for each of the :class:`Decimal` methods described above (with - the exception of the :meth:`adjusted` and :meth:`as_tuple` methods) there is + the exception of the :meth:`~Decimal.adjusted` and :meth:`~Decimal.as_tuple` methods) there is a corresponding :class:`Context` method. For example, for a :class:`Context` instance ``C`` and :class:`Decimal` instance ``x``, ``C.exp(x)`` is equivalent to ``x.exp(context=C)``. Each :class:`Context` method accepts a @@ -1064,11 +1064,11 @@ In addition to the three supplied contexts, new contexts can be created with the .. method:: clear_flags() - Resets all of the flags to :const:`0`. + Resets all of the flags to ``0``. .. method:: clear_traps() - Resets all of the traps to :const:`0`. + Resets all of the traps to ``0``. .. versionadded:: 3.3 @@ -1483,13 +1483,13 @@ are also included in the pure Python version for compatibility. +---------------------+---------------------+-------------------------------+ | | 32-bit | 64-bit | +=====================+=====================+===============================+ -| .. data:: MAX_PREC | :const:`425000000` | :const:`999999999999999999` | +| .. data:: MAX_PREC | ``425000000`` | ``999999999999999999`` | +---------------------+---------------------+-------------------------------+ -| .. data:: MAX_EMAX | :const:`425000000` | :const:`999999999999999999` | +| .. data:: MAX_EMAX | ``425000000`` | ``999999999999999999`` | +---------------------+---------------------+-------------------------------+ -| .. data:: MIN_EMIN | :const:`-425000000` | :const:`-999999999999999999` | +| .. data:: MIN_EMIN | ``-425000000`` | ``-999999999999999999`` | +---------------------+---------------------+-------------------------------+ -| .. data:: MIN_ETINY | :const:`-849999999` | :const:`-1999999999999999997` | +| .. data:: MIN_ETINY | ``-849999999`` | ``-1999999999999999997`` | +---------------------+---------------------+-------------------------------+ @@ -1514,7 +1514,7 @@ Rounding modes .. data:: ROUND_CEILING - Round towards :const:`Infinity`. + Round towards ``Infinity``. .. data:: ROUND_DOWN @@ -1522,7 +1522,7 @@ Rounding modes .. data:: ROUND_FLOOR - Round towards :const:`-Infinity`. + Round towards ``-Infinity``. .. data:: ROUND_HALF_DOWN @@ -1570,7 +1570,7 @@ condition. Altered an exponent to fit representation constraints. Typically, clamping occurs when an exponent falls outside the context's - :attr:`Emin` and :attr:`Emax` limits. If possible, the exponent is reduced to + :attr:`~Context.Emin` and :attr:`~Context.Emax` limits. If possible, the exponent is reduced to fit by adding zeros to the coefficient. @@ -1584,8 +1584,8 @@ condition. Signals the division of a non-infinite number by zero. Can occur with division, modulo division, or when raising a number to a negative - power. If this signal is not trapped, returns :const:`Infinity` or - :const:`-Infinity` with the sign determined by the inputs to the calculation. + power. If this signal is not trapped, returns ``Infinity`` or + ``-Infinity`` with the sign determined by the inputs to the calculation. .. class:: Inexact @@ -1602,7 +1602,7 @@ condition. An invalid operation was performed. Indicates that an operation was requested that does not make sense. If not - trapped, returns :const:`NaN`. Possible causes include:: + trapped, returns ``NaN``. Possible causes include:: Infinity - Infinity 0 * Infinity @@ -1619,10 +1619,10 @@ condition. Numerical overflow. - Indicates the exponent is larger than :attr:`Emax` after rounding has + Indicates the exponent is larger than :attr:`Context.Emax` after rounding has occurred. If not trapped, the result depends on the rounding mode, either pulling inward to the largest representable finite number or rounding outward - to :const:`Infinity`. In either case, :class:`Inexact` and :class:`Rounded` + to ``Infinity``. In either case, :class:`Inexact` and :class:`Rounded` are also signaled. @@ -1631,14 +1631,14 @@ condition. Rounding occurred though possibly no information was lost. Signaled whenever rounding discards digits; even if those digits are zero - (such as rounding :const:`5.00` to :const:`5.0`). If not trapped, returns + (such as rounding ``5.00`` to ``5.0``). If not trapped, returns the result unchanged. This signal is used to detect loss of significant digits. .. class:: Subnormal - Exponent was lower than :attr:`Emin` prior to rounding. + Exponent was lower than :attr:`~Context.Emin` prior to rounding. Occurs when an operation result is subnormal (the exponent is too small). If not trapped, returns the result unchanged. @@ -1696,7 +1696,7 @@ Mitigating round-off error with increased precision ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The use of decimal floating point eliminates decimal representation error -(making it possible to represent :const:`0.1` exactly); however, some operations +(making it possible to represent ``0.1`` exactly); however, some operations can still incur round-off error when non-zero digits exceed the fixed precision. The effects of round-off error can be amplified by the addition or subtraction @@ -1746,8 +1746,8 @@ Special values ^^^^^^^^^^^^^^ The number system for the :mod:`decimal` module provides special values -including :const:`NaN`, :const:`sNaN`, :const:`-Infinity`, :const:`Infinity`, -and two zeros, :const:`+0` and :const:`-0`. +including ``NaN``, ``sNaN``, ``-Infinity``, ``Infinity``, +and two zeros, ``+0`` and ``-0``. Infinities can be constructed directly with: ``Decimal('Infinity')``. Also, they can arise from dividing by zero when the :exc:`DivisionByZero` signal is @@ -1758,30 +1758,30 @@ The infinities are signed (affine) and can be used in arithmetic operations where they get treated as very large, indeterminate numbers. For instance, adding a constant to infinity gives another infinite result. -Some operations are indeterminate and return :const:`NaN`, or if the +Some operations are indeterminate and return ``NaN``, or if the :exc:`InvalidOperation` signal is trapped, raise an exception. For example, -``0/0`` returns :const:`NaN` which means "not a number". This variety of -:const:`NaN` is quiet and, once created, will flow through other computations -always resulting in another :const:`NaN`. This behavior can be useful for a +``0/0`` returns ``NaN`` which means "not a number". This variety of +``NaN`` is quiet and, once created, will flow through other computations +always resulting in another ``NaN``. This behavior can be useful for a series of computations that occasionally have missing inputs --- it allows the calculation to proceed while flagging specific results as invalid. -A variant is :const:`sNaN` which signals rather than remaining quiet after every +A variant is ``sNaN`` which signals rather than remaining quiet after every operation. This is a useful return value when an invalid result needs to interrupt a calculation for special handling. The behavior of Python's comparison operators can be a little surprising where a -:const:`NaN` is involved. A test for equality where one of the operands is a -quiet or signaling :const:`NaN` always returns :const:`False` (even when doing +``NaN`` is involved. A test for equality where one of the operands is a +quiet or signaling ``NaN`` always returns :const:`False` (even when doing ``Decimal('NaN')==Decimal('NaN')``), while a test for inequality always returns :const:`True`. An attempt to compare two Decimals using any of the ``<``, ``<=``, ``>`` or ``>=`` operators will raise the :exc:`InvalidOperation` signal -if either operand is a :const:`NaN`, and return :const:`False` if this signal is +if either operand is a ``NaN``, and return :const:`False` if this signal is not trapped. Note that the General Decimal Arithmetic specification does not specify the behavior of direct comparisons; these rules for comparisons -involving a :const:`NaN` were taken from the IEEE 854 standard (see Table 3 in -section 5.7). To ensure strict standards-compliance, use the :meth:`compare` -and :meth:`compare-signal` methods instead. +involving a ``NaN`` were taken from the IEEE 854 standard (see Table 3 in +section 5.7). To ensure strict standards-compliance, use the :meth:`~Decimal.compare` +and :meth:`~Decimal.compare_signal` methods instead. The signed zeros can result from calculations that underflow. They keep the sign that would have resulted if the calculation had been carried out to greater @@ -2013,7 +2013,7 @@ Q. In a fixed-point application with two decimal places, some inputs have many places and need to be rounded. Others are not supposed to have excess digits and need to be validated. What methods should be used? -A. The :meth:`quantize` method rounds to a fixed number of decimal places. If +A. The :meth:`~Decimal.quantize` method rounds to a fixed number of decimal places. If the :const:`Inexact` trap is set, it is also useful for validation: >>> TWOPLACES = Decimal(10) ** -2 # same as Decimal('0.01') @@ -2037,7 +2037,7 @@ throughout an application? A. Some operations like addition, subtraction, and multiplication by an integer will automatically preserve fixed point. Others operations, like division and non-integer multiplication, will change the number of decimal places and need to -be followed-up with a :meth:`quantize` step: +be followed-up with a :meth:`~Decimal.quantize` step: >>> a = Decimal('102.72') # Initial fixed-point values >>> b = Decimal('3.17') @@ -2053,7 +2053,7 @@ be followed-up with a :meth:`quantize` step: Decimal('0.03') In developing fixed-point applications, it is convenient to define functions -to handle the :meth:`quantize` step: +to handle the :meth:`~Decimal.quantize` step: >>> def mul(x, y, fp=TWOPLACES): ... return (x * y).quantize(fp) @@ -2066,12 +2066,12 @@ to handle the :meth:`quantize` step: >>> div(b, a) Decimal('0.03') -Q. There are many ways to express the same value. The numbers :const:`200`, -:const:`200.000`, :const:`2E2`, and :const:`.02E+4` all have the same value at +Q. There are many ways to express the same value. The numbers ``200``, +``200.000``, ``2E2``, and ``.02E+4`` all have the same value at various precisions. Is there a way to transform them to a single recognizable canonical value? -A. The :meth:`normalize` method maps all equivalent values to a single +A. The :meth:`~Decimal.normalize` method maps all equivalent values to a single representative: >>> values = map(Decimal, '200 200.000 2E2 .02E+4'.split()) @@ -2083,7 +2083,7 @@ to get a non-exponential representation? A. For some values, exponential notation is the only way to express the number of significant places in the coefficient. For example, expressing -:const:`5.0E+3` as :const:`5000` keeps the value constant but cannot show the +``5.0E+3`` as ``5000`` keeps the value constant but cannot show the original's two-place significance. If an application does not care about tracking significance, it is easy to @@ -2159,12 +2159,12 @@ for medium-sized numbers and the `Number Theoretic Transform `_ for very large numbers. -The context must be adapted for exact arbitrary precision arithmetic. :attr:`Emin` -and :attr:`Emax` should always be set to the maximum values, :attr:`clamp` -should always be 0 (the default). Setting :attr:`prec` requires some care. +The context must be adapted for exact arbitrary precision arithmetic. :attr:`~Context.Emin` +and :attr:`~Context.Emax` should always be set to the maximum values, :attr:`~Context.clamp` +should always be 0 (the default). Setting :attr:`~Context.prec` requires some care. The easiest approach for trying out bignum arithmetic is to use the maximum -value for :attr:`prec` as well [#]_:: +value for :attr:`~Context.prec` as well [#]_:: >>> setcontext(Context(prec=MAX_PREC, Emax=MAX_EMAX, Emin=MIN_EMIN)) >>> x = Decimal(2) ** 256 @@ -2181,7 +2181,7 @@ the available memory will be insufficient:: MemoryError On systems with overallocation (e.g. Linux), a more sophisticated approach is to -adjust :attr:`prec` to the amount of available RAM. Suppose that you have 8GB of +adjust :attr:`~Context.prec` to the amount of available RAM. Suppose that you have 8GB of RAM and expect 10 simultaneous operands using a maximum of 500MB each:: >>> import sys From 89d9ff0f48c51a85920c7372a7df4a2204e32ea5 Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Sat, 25 Feb 2023 12:00:12 +0000 Subject: [PATCH 189/247] gh-101997: Update bundled pip version to 23.0.1 (#101998) --- Lib/ensurepip/__init__.py | 2 +- ...ne-any.whl => pip-23.0.1-py3-none-any.whl} | Bin 2056044 -> 2055563 bytes ...-02-17-18-44-27.gh-issue-101997.A6_blD.rst | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) rename Lib/ensurepip/_bundled/{pip-23.0-py3-none-any.whl => pip-23.0.1-py3-none-any.whl} (93%) create mode 100644 Misc/NEWS.d/next/Library/2023-02-17-18-44-27.gh-issue-101997.A6_blD.rst diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py index 052b7bca28ddd3..00e77749e25e77 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.0" +_PIP_VERSION = "23.0.1" _PROJECTS = [ ("setuptools", _SETUPTOOLS_VERSION, "py3"), ("pip", _PIP_VERSION, "py3"), diff --git a/Lib/ensurepip/_bundled/pip-23.0-py3-none-any.whl b/Lib/ensurepip/_bundled/pip-23.0.1-py3-none-any.whl similarity index 93% rename from Lib/ensurepip/_bundled/pip-23.0-py3-none-any.whl rename to Lib/ensurepip/_bundled/pip-23.0.1-py3-none-any.whl index bb9aebf474cfe981dbf984bd9e36e23560111288..a855dc40e8630d1e3953e162af76cc5b0a11952b 100644 GIT binary patch delta 86974 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�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 delta 86927 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%0AG&=#Ciy~rM9b&42^c75 z1#6Mp1CuR;m-^7G<-?qW*uC=#(ov=DEg*%Y$Tt`UVV^@Y z(^=DO`I{iT?5UrYE<5_qs;)BKr6|w4%%nbw=-XFAOrZEMm6uJ_QAht^f1R4 z_^1!(q`K^!5<2mSzmVAKW!SqTtf0StJRj!SGALxHf22P!Vo-SL0+VqDx9(+2l@YAd z<8oXgs!2t5+#nG=~c%Ok9!W4BQ=J z^#tN&eCZeF<6Y0^kyfmj(K_IxTRZpsc`ee6wm9YmTSeeb=b@HlFkcsu&V`7Te!ukE z{vf`je{#U-!D5#}pt)k;doSi zi4bBRKqa{QX}fjju}V+X?ZFv|51;<)Rk4@#GET;FOKB3y37Kv{)-0ue7N^ETi(Pj^ zYVQY=67MQ)BSY$=IiPo?KVM*%7TY#UnyA?X^?%UMW9e>HTCHyH5Jvi*XL+@kQ@Z*MWkvbL)TY#Xt( zQViQxDa1eofz^^(On0=2WLOFG@EY;s$Fw2(oqGNq=w9{fdiC&K!9z^xCm9;S+L&3w zVdR?%nNA0^yCm}xm4Roj_ICiBKx_4bfj=U@57m%-j*17IpOQp~{au+F*CCg-e}daz z50er|LkwiKlCXNd#;qThyfV!iUXk~oWvtRPMXm)lgP zh^FVEQ{TD{LMtdq1xDhv{=SeU)9QGf-)4VJ zIQWm)zpqIT?KiK!t+7Tby({;4e>f4?tt!)DswQsgZE;Y=I-3WIr|l4(ALmX8{FA!h z*)zq_vTqUc4ahAY#Q}a8lyiO`ZW&c1L*(uupX?aJD=}n^=fqFy{^9PP)b9#~?(3|n zz>~ocXewrk)EVUvWsP2pl|*e#4=GWJ+924=Y5qUxkB42;DE+>2K~sVJf6%dQf!-qj zAno^DdpcNMbz)V6Gc7VYb#8uc+uMEv1Ap(Fyn(;mCM(LWI!Y-=Fv!kPB2w{0;1+xK zeMjacO%{z!tA_2u+T-{N#Ypl8*w=bH;Ih!|VOrmwtqRmy<8T>=$PnGm>lY!iw0d?} zb1#Jlb%Q{_Ppf_BkWM6Me^PsBC(CCq(OeVvoXm(0<3vJorcR$OhZfVg*{|1X0>NI! zX}`UO_c4%GjS9yY&9F-&#%oIoJ^YDpp=iJ1wLg%GuWBYu^o*YN<%ETcNfhb#2ok_}r)&lN@S~rE;e?Oi!dt7Dw_}<>S zOq)o#olOHlR#e!+vSX`Ut__VMXJ(H5D%ozuA$z=_1CcQHZ|HBV&0cNgJniMFKi@}p zYdM$5^&C!uO~hm_p+uO#JkR9z_88819ai}U?p8rtr_Zy@M?PJWS;zOfmKqp75Jp9F zLWy8;?G7Xf<9k0bf3KcG7mp(jjQAJq=DAHPnmlXD0vsmJwr}-1-*Uz|qcms9%9208 zZKmxn6rCwVw}#hOuX)BTUB7;|@GWeZYB+jlU=|ZG$QR9w%p+CxrEp?#wMSf%Eh6HC zuAT#7FQ@O{kk``cIkGC-tD5P08t!3YXJ+Eg1$)F~tCfqQe`&CH`7@x*=2#NEy1~eo z#=$q>ckj8m(l#2Mq(8;HaWQLZp!S!TpVR^FZSuBL1`Ydu{-yU0}{GC0mqiTE6Y- z%-v6*-;!&6N*+|(bk0_RBZazwU&eO+;`(J!e4oo}eIRlZB+M)Ac8Du*BjAt%Pg2m zJvkQ=1%}#OK3>9q(Qk}&ZD?5`IWzawMYvkqBAV~FAPWYooVY(bf52WD$v>>ONoqa1it%Eg?z?i~2T zf{V3!e||=_71}u;E9bVfW*umUaUUql!sVD?(=|yKkv_wP>-*ReKQ8+MKj`<@l*0F& zi%*`YTWzLal^m$4^MYT=yGUEPF`s!z_JhmJXKB6D?$lcB{$RmPF~r}#-2-ivXdCnw0Hu>hUImUNp ze|F%@)g3wRMpbeHsC|EE)P1lra|=>tV5I~vV-0U-Egxj ze?1oQPCV@o2g(z#^4+#pg7i|O|AXAm-~2+;s-f#l&`NpI#@Vzv-TAsbmD2!k*(whj zg^`hggwQjv!Jt>#|ApGudHC(p76r``d*o;~x*?gQ>TZHF>2Aw#LDU7v3SbY&Q?v`)2Qm!4 zQjEXQ-7O7 z8cdpO-LlbVXzPnTKONOt+(qpXad!UwGTQBOE@*76-~sq2|M(jyJ_@nUw`0iae;^)7 zCL@S;6X7*8(F`xpq$PAVaiY5Hn7HE{ZxG_gPw^z9berb8{v*bY@12Ml`@uGjIENK2 zf`^x`L+gDBtyCWmQ~j{BPcl63(dK!cYikAMbk3wLzbO8oHi}3`U{k_y zc!j#1A+^MH(QAt9IBEmK;D447f46lMJ$F-D`A`d<8|f4|u$?Ywbv)ywWBbVQ$`QTD zLkSa{IjtKvDDdCdili@}jY(azNz*FvRC@dbiQ6jLjkN}Pr+6*XJEK`;-GxfBy7%eeORd zofW@*ZnLFhj`llFo;>D)Av#mahxwj6&V*?@3~*|tBoH~dy%ua?Sd%W?z$!&*5E__o#7_*y5FN^>C}y{V%0@t zdPL(fx#?}n%G!Q|!|)GO;0gY5u$xxjZUsxt{&*jA1`}dYP|w+AIkudcrLgJm&tT`F z95Ylpv(#GssOtaoNBEMlfIi;2&)1kASzAlVe+x zJ2I7YnF0xGzQl=)}Kh9sJ6fQYa|!Z?Gk7>9=?E=%)8@*yQEO_Xfl)5Q9g}L9Zq5DU(NP#i zUTyFT_hYLX7ChaJb}%Ga9(Z1Q0zI-)p6n?D4DI~fe@l7}A-*Dp<*Mb?{(iuIe_lv! z3^}_3+orn@p*UVW(p!H8RrQT$?j+%i)d*hnMGr3Z z8(}1j5PbcEtg3w^rD{jKhW1HpUBnqwQ+vnL>~ecJl5*l~R-igYf2BQZI(=d257F}XrT+8x(`q#wB51TqRnf*; z;N;_mD+*OFQN)3gij+cbM+A9~~u^255Jy)8=%p&_;0(U;SL{e;0d> zeQn?ABwrK-9<*C}1^Wy19|jTsJv<-iTD3z*E+10)!3VP^NrZf%AH!;;Bu09jWS!Bc zp{C7sQxNJed-9t$;u(0q;}`7GdV%6`Bz^~V!I3Qn(|JnC`id_v>}jdu%W<-lje;LV z{(}4x#EndfzImO-Sy0;H1;rnC)L(QdM9cJQhU;8?QAP0m) zWj&~JAn|11^6r&G9FWzs z>r$-(_zNQ6Yp(wR{-}<3G~i#XiSghvcY^U84M#tj1WSgo5J*dqUgiZdgcB%llP=)D z+=REWjJt-WFV&q?_O*Wte_^wzxKpO%6&RVrP_R4MEFa*~z|L{?tjV`J&i{aJvg0`? zQ;c(82sA=jn3+Z_4{?l74Sr_GJ{4Elp-Eap6*F!FY|%)qg| zQS(d@k;zMWhfIx&*LUD?_7g4ht91h~nEZ=?_+6mzjNFt`Z564De+GIqj5eZa7{5ES zkCLC1GpFK`pC)@amhE2r!l}Ppvfl$EUPhmoHz_)w5AFS!q-Ia(k?QEC#YU+f%*M_h z!a^c4Rj&+jOEQvwxekAsX}h}%wSkQS;EzQ;oEIz?D2ojT!m@y4Qos^5Uc;osOEUXH z;lByBj|pQy4C~xle?VLZut3wj(HwFQ2XKyZ)bczU8yQyUf+*x{1E1f#*ngnkr6iaw zy1KdmZh%$=9SV&wP6Jvh1X(St=e67}yVSkd9L;R2l}F#V%%|3@3uVaTafv zbUz&g`S5UoL&;Qay$}=%P26o^Ao1(HKlTGF?(=7NtyS+Be{ncTpmE8MV}N5hd5DkA zOsvxVX7P?UQsAl3&~^)cyl=)2CfsN2)TW@LoC1|hI!B_w-PrG<<$#?M)j;|Pr{(+_ z82oDRtWpyr-=E44M%rg=cybqO&+L;ftRwL%kkPKrbU8cdys?mFJu%9@WLHygV+JBH z>aQRE>xw)>e~Xsrwkh7z{t@-~f`?eh%ooE!DJ<;Rb!XT(yWy;RgUO9x1N|HFFTwo3 z+Z^)~{`UyQ?Q#0S3-RPQlMmAajvPNx5I&5V>C~iC%ibgsz4T9+w@|e>t<5sc^k$n{5Mr0{xL&GYqm1 zDa~m5M$YqYKMI$_5iH{)7!PtjXcxYt*dpdaP5C0xciZS2G)k( zElR7;f6vt_4-@~6#N~)6bqx2i7q5QJ>M*?vs3++4@+pNB}#^#Hy^NP*7>PR z6Xh5ILz3+C>!_cw9cgJyKi|1_YtleJK|i+V=>G~NJwpq0cT=lUtlS1ac4Kb$be{vM z&=Rf0A~VHs(o6@y$vtf}vY(-UQ>;B#4-F`Be+nX2p4@I+O0EDhmAMfxDxDLqq}ZBz zm<5)00D^A$?GLCwM-;c7w4V?t!O?hfbhhre=I(`)6+)hz+o?%TB4XKXUP_lQ>WAX` z2Cdqy!s2ZJD8$SnsL_yjeRwXy?j=>m(7dc}r~@#IAGTsC-q@Iwn|>Qm{28}>wli|e zf5+KF+-fy=4`A57PUs@v6!7f!3u1T=rv$ZD2dISG0wBnzTh@&0{e7F%5E+5Nn*L+t zoc%taikl2vJaCL-kJaxAaLyDs*ejz_2>r$(hJ1j%230b=@~ua4a)+do?19p~%0`PS zu0pmnM%>&haWQus6N2IZYfCA95g`5%f3P*q8REcedVqjRc z7ZLBE@gi-V50bg5>JNb5vpb&OZFSJA;sC;!3%E)er~7amE+;Oz=h4jFB7hrA*QRQd z*m%v~mC`q4e=W952n23O-AWBlZY2eh`%nRrP;>N7~d_9+> zdV~hvYCEqvc9X4LBj_@i32gvYgr?&zI(vuH{xB#T^BVC1@mJ&5&yoNQygx)Zjhc9_ zA=89(gmCEPw49pD>=N#cmg`3if1`djW*hk<{7HEKEaiyr$BHuQR9lYLqmVC{&FzYu z@4SssBZEzr0_6REPM!L)?kfE0^8W_?{ZwCeV<1rPfUHk|)sUt(uGCe44%nd(;V8IB zn z*|RL&S-8K~0dma6*(#JKL-LDg>$||nls1Ks-> z%kra0dwDW#%pM<}`=swVW zUnmBCxHjKye9y58Lo~89Em1;k5LbrozK9txtF1~(Z)Ur=Y9i;m7uUIMEjb|cL2Z0P z`a@S-tCtBkNR^*1u@v5ItvzrmKsT;f({?T0EiA4w_sCivMX2ZCxDlsdAGUKv9_tt9 zOfVf#j+B~;d-+&Ee^6?Kh2rOM!3`BT>ug{}kM+Xy%WY#=L_P?npYmfu1}W{tgLl;t zjGDUREvVvsJzo%w&-vOSwG7VSed*ZZ7e!F-CifrEp$lG0ULZ@bjhWh)sLGJHEeBhZXZx+6Qu0Vxh2UA@5PEKd^5DVfBk(n5RSAr8NVaK4NoC@BRe6K zr}ZJdm!y1S#z`8C)!_e1EImQnH!BCu+L7-cp#4aZG}rx)S$ow#jN^M+JRAH@$++2bV&Vvrom@`K;GaC7Au}&+nsvreT>s}&p${&SsiTs8X{g}BuL}DA*0e#R)--8EUX-lcpIM5nPPeRQ&@%c{3 zo(WNCsb|OT9OCwaIEyWl-4c0Yp9DYL)V#>@Rov%qBV%kE*JyTM-M|EKxiY;K3UdNu z*PBM-e;Tzr*UY3DgqjfiqA>DjIvAPKEjCO1>NI18xLh1x5 z@KfjVCB5~1eh`1x~4M7pjl)CO*-K4P=q|+)hH5~myaQxGk{uqNWGC;%t$O#Q=hdUgup=w|1 zf8D*9fL)v*)G{j;KFAVm3#ToQ@<~hmsL*&UJ9MOjwBQro-Ku zE9k#q@>3L}kGkZg+SYB8bHeLp{vgoxL55(h>mgNTc|P}cy%XHL4;AZFIx6`Ey%$OU zf=s>(e4g{OPV9i&3nvGY4NRM}n@GGp!0CEJ_C;`8a#te=O1huve~r@{{_u)^ha8^k zDne3Wt3*fB0L29dq?a8|oDsTUp5OY{~Q zJ9U1X&9cX>E_X22>uB)!5Kln-$k}FU*AxE~y?U>RZeu2-t1gTAkS>X9J!s)+u%)9; zt~S7N-BSRNdt9Dqe|dGWaRm9GGru99J9HB7*ev@_L@$Mpq1Vhs)Dkp+2U0y^$8dAh z?`DpH(TOovn;!chjr)1E>7SiM8+r^eLqh;>X~2}YZ7MrsZ&ntzWI3zep~hAOAwAZ` zDvVL;Q#|l}8ZcllNq+z=N$!J!Gm&wQmaC))Gj6ego=l7@e@zeaJjSbHTg5y1;e7s- z=lMgPyi7=gN(>lNDQ)f*-D0P{@M+d79Il=eXSrqdj+xcapnzH0bjK$JH@ud^2Tkk* z58L)9=AI~p=Urp$UYw>evRgve+H0gB9(zhuFj`$&{5`GvjrjJBd-*t=a*pw2df@^W zyq~PoX}5$Ce>mFdF2PyJp@Dujh~=~zHSfg^{~*3(!5*1$XqPkylh7R^Y2acOJ6#-1 zv`jv@7dOt7q#Sjv-R0|@W#boO7evWhuP%PL?6Z;O>4$tL@8qMFQzdaVO@d6vIJBO*zaXhvX>OQ8+WT$?YPs2dodR==8Hp{6E0&X-iEU z)N<>JFWJqlC=*|GGxg@({R7^-1RK+{eQy>h5SLKAQS=qb=nL&-oPf72wmg*1#68tw z<{Kz(Wmw2(%xyN?%Xsk_15v$4%2Ga4g&hYy0}C&p)NI-o{4}PM;$@^1$1b;CWzZ9 z37?>Ova(gUnG!sfvX%jku#lgwoqOO8jBmzzwrlPHf5azwqenoo&(KrwGS_K{4+gKF zYmA2x!V>nN{$M%h9Dtl4YqE8NN$k>Be}ZM6qwk?Uub^%HnQ~Fe&B?-7;|I!6;XXj- ziM5nCWk*2FXp7uA^_?@Rwt2+p*Sp348+0o!eC7J2)*lzp2OVI@xV^Ync3jL|P}!?X zNEm=Fx=MNQbh{grFL=|ipHN@kK^&GD#^#;@BXU436ep-o8V~wao6I!M6sIZ4f1}z~ zGcbL@6#5bHee@x)H}E9$;H0&Eii`r;LOZW=VC<@@ z2$7F<(#|1r3u)FHaDNPKRrNTIzGln;k}?fNd5i3w|2!VC9{1<_~JruC`$GHm8E52z)x={XVf285*HJSN4(*xpv0#62^bL9w1J9(s2>LTpV=M2z5MriP<+ojXU?2Cb0&7ZqcKK3chas(Mcw^>CRn+a z9v(QZsyRk7WneUVuy^Q*tr}5ltf${k`R)-MB{8GZ?evZXt@$U`t?g(?PKlSua8!C5 zv>^;l-=q6d_Tt7Tr$XeNSBmTDJyD5y8`6H`QF+}9w;vjFc%hSbFS|5O;mqWH`Rxh` z4(aeBaO0BIACz7Wdf(Ub;n>%PW7WRRS=WB6W0uE%Yd$wD`Bs-;8OithHu^~OQlq%o zy`xswg6Fm-g(f&1JbLZipYqggkd>62^XIs{&Jc|+8Z+TcvhKIyoF6&))pPUXi?8y_rhIy{Z^Ms=);Cm!OuhLg zuU#g^Cg%6aE%nYdL%bL7{`hg|vsc@ezW#XZT7P@~q~M`e@4p$Wb*M8MubwKqxh^)a_{aQWiBVB+JZASvI+CrkP$Q=9 z!Tb9g(o>F{TfHED+{>-ymLERFs;IPeUJfoDAGf_ z=q0vsap}ZR>dLVR+m_UpYbt*K@lHL)tM8w$YIpA+8*I~f^iM(Aidof>>mBxtmjAc^ z#LSxqD!jEOyKX+n^NT&Y`LG~&`qZ-}t+p%$iwr{do3-n7gx=N>WdpBG%5IRBM%xn3x&U znt#R5@k?aAPQ2l78#<@oi*KR1C41dlHj4dz@xEi8UQ)jIKi89Hz&8NwtfSuumaZDU z;K=Ecy7OmS`gEK+Kiy}Lr-kGpr>|zIzX%p?LLNCT& z?l8=Vo89SC5&LPM!OAH&f{xe#nR@ z6SomqWV;)cyeZt5leBlt=GCY9cLtv9w{cIV*8uCbmx~^s`aZCaFqqeBi;L2GLDwwvKHFSI-Mm8}j00d}T*->IbPO0;i_$u8Z@=n9q2t zIe6mr-8HNf(Ia)Mf4avU+IR5E;~843!P&m^2M^MpZuiU)rrcDmxd-77k9iE z^vC49e_rmX{L}>n>*CvktbZJpJ@#v}e8X4Cht<<}l~qmK6j^u9DDuJkpf!cro%wcc z0Y6)7x2<~m=Ue*PeMqW3vnt%_OxBOH-j6JQIr%Lf(75Naa^Bs~Qq}v!X@SBY@ry2obkMI+o_}v=>dU* zv`=~HT?)ue-&0k)qqJjR`+}L_2jb(rUE=f2FIY=V^F>nx7-~@^=TlaZC0v zKXCPjL2}{tw$*X(ld2A{|JhQ$CN_W8y+vamdp#>w2^_z#E+k~1rDITbicyN=45Q?C z_Tzt5nzy!WSW!2*|xdLtw@q*%9%R0T$+1r#EQ(-wSmOXu^!weHd-Jw`r zR(Al*3_+fEC`*R590SJ}?|c%ihSKj)e5OIJi2=v@z|}@-pdEiXb=N0NFb37!q53nV z64O!pcYzg0;FG?PBZ^1P~>!Ch<4pm;BrLHQV)R{QReb8F-78-j3G~;V8x;|+FIh0*MF&EaM+5#}h zLX9oTFQinNTC8N}=0An&F!rek`^i^>rMmJ(q|kyw(qk(MK}(=6D>_?9X)szul|nY{ z0XNsL5{~8ueU{qUMRgHF?4f!$+c{0DfD!Pg7C=v?nlg=B_fAhACbm- zKZ3?e1+${HkJuZc&;dZjj?NcBi=ssIP}w6Aw%;&p6i#5vVoKEjCqh2@6XONh@)h)t z8K)5oA{nkZ+E`4JFeVyIIuhR9r(fy}Ce z$cB}G%wxF-%1RVbM}aFu{u(nIvFu2f?B)t+^aCiz7!ls78nqqr*0-HJUSE#Er?xW6 zOpwlFQb)hXpl4M!sbkh-N`q1Hz$|oM3Pkv74`Dx_ybQKJ7tuy%y13g74RhTfGiAXy z{d22ZS46m-k10(?uf`9_hlAkCo>B-kE?4FS!!<$vPl#y|Pr$U*H4LnUQmRBGQAU^S z8kV`f7eTk4Qktmg3F$$ZrvTU16WH}BrN-1+6VNfZ6?)`muyBAiH4^UDrzB*XEle&* z!GN{ul^{%|QQlK(5EF+9{i8>W2iGouA4`slm#88A=aeyWct%E+AhE1@I%I(v5aAXa zsvO)Mi>W)GQOqquDB~GK>lsZJ{ql@t97lsbs|~@w26{Z6IY&XdA*K#t0%^u`Xu7JU zD5@PGs(})pQ+%fB>b8fxmqYMYfdvMf!dtqy70*fMSmme2TY;7Rz*XGTHo}_d^|KU} zNA{&8(1Jg1ojnf}zXa>RUEQBFLG~b~fGOGnT*kirrC{GI2coB>lyps}bp}rg2DAeN z1=l+>h7#@}2WXJ(3!=y61?b6eA+qQfWa8ThFf4Jty?_Lq^MWYvx95#}7>q_^NR8al zIByzJaAu0ABGN1)4cfH&WL*gi^JwsbX?JsQzETY!1!k6!Mb^qPFvK*Nz-PoCVr3^e1ea}Q)&6TFkD#A z_RqvVHN&px!~O$uA2%K4boLZrmyL=w5IZ;Zk#a4ihFoh%_sqcPR$~@@Y$&FP_SF!h zPGM2uU@X!^RW)QR=v2n-4uk=GezWj0qK_A*vdf6X#3z&o^hiHhxV5djKk(p9i((bV z)Kbh{jc5x*uc%Qpi_TvpCWju?5({u(i>yZ9-cY)zXp5LEqTdjS^&61fV{5GEO<=+ygTfYS@dFfb-37Tw2 zvuo+i7*)(*(c-tnBQ0KmM*`v2X;sf&(RUV8U6{(3-5mJ+KiDMIh4XmU9FKVCvkKR} zBO@slOc&i&#iH*jU?vgD8B^w}DLEU!MSj;H)7x;A9eR%p-cwFYnh$ypdK7-JdLj;M zN}GiV z(G-HV)e(P)EgqhaVE76k+jFyJrW~ul;{$1_{|8VIugId?KahNO3Ztp2EL!n_Vs76{ z{s`c7Z4B~Js;0OMa{fq+8;3>KI;=?Wkz#ITL@5~kXoyiwWMd%S+ly1h>(2gwEHD$Y z0CxeTYl@2vU^>pC#S?BmHu&W2gp8?ng4dmQky!WJ$fEh-^SjId@aXFM*PrOl#MOU3zQO8hm zDJ1=wl4I-}v&Qbi8|am4n1f*f)BQ;k7`n53ug{b^bDv{byzUZ=XqD%6tK|dOC|beIdax^n%>k7|?SHT*9q#`BIje6D=-{wtXSi zAH-TLVp!3_rO>d-F3}e(a)@O`bOXiQ;)&cE05w|8qVp2|kFSz2!#t5?L~aq6Ma2!I zwn{7-wTBfcHImeB(Fo}5BP{CQNLn)PQr$)xW~2ldcLv?A!zr!w7~0-Q*)T3{R53g0 z1unh|34xpV24t`be>RfIT>dLmb0r)3G(kdk{z^8g?J{x+$G};+Pz|Ju?ho|Qd6v5I zD;Xz;u>#%|7Jd4aY|DJhY9oGvo&Ug=fjj&@Jz}YrMdEV3Qpy+iWa|*v$vv?Wj$NJS zxELF|2~AMtjW?{G&Khy$Ua|WG7-E7}ag_Z>T$9PYL3rc#wE10$2)M0z9ghdm*ZoNo zsEJD8i^6OI{t1l?iCSzE9k$GR;d0@Ox1{Dq!1OUZ-gkQuo_=>}?0pX-3AEHe!k|}+ zri<7Qvw+a6fFJHrSVcIx^^G!S78@eb2QDiC;|cs2a}*Dalt4{KB;cCV?k?_^WD^V8VePVo3Cxa-%5qe5{p;pTlOqvRE+5cOV$wl!1wNaF{k$VAQYL8q>H zg8?!y+6*}wKCi%3U9{r^*?|OTjJEp-!KLpiyv|>-lBH(VITabwGJ!aDQk; zA7};F4|!=!0pp(fwVJ^X1CC>h4bik_68!eA_In~=y^kPvAr8AgX#(97m^zfnKaX(P z=V#cmaz82NDsp806IxVzRstz!N$4|y$Mmm_{^{C-pr{jmxH&m0o8j_N#!pI}=?0sp zxpRhjit-F#UC13ep65`_PjXDD_zNn^Z<9o|KOsH4|Dsfwips~J7Y`u{ZuUC+xeEsg zZgZs6(dJ)d!5SYuRkz&%mPadujVhERT%|rzvc0-wrNO@~!@;|6A;TDSdk2pKiQzrv z#L;+HDQQ&DLi*b6HyFImgAnxpP~xb>T}l!ChC^e<^ntZG26>QkyxS zdiH3V*e)1!T(e7?~bg+12dL7`9%(z#1sGg$&|X7=FEw!1_^AlD#sAX>Iyi zKj=z-@CSD|u8l#it&}YjE%OSm6-2-}OUFRrm9W7ol+;QNly9{{bw#_SkZ-J%Hu}>_ za<_6Dz@B>ifzGH(OQXtm5+Yx)?5+oq>34J`ybgem#Pq=8=*vNwisZ!odD~nib9NB(03c z=%c$B)j^j5WxVe`d-6U>$V@*r3+w(|fvL<%%$5jC*#+1NM_fPsC_A z%E*)!M=q31uQk~2C%~FA^e++^%7_-uVNs<}85Lyvhq#Cm1<@EdpupBA&IeJ|Z?d{G zpHlbT7iRFw{|GO&`YmMnQIRsz=!_^i>c5FqU4jD)tg1b<0WTDKJ=0z+mC#q!Edj&#sF+Eg~IW0E6c!d~o{;jy|vj)D9Un zi4&7sFlv&`qE-?#yBNpm5t0n^;m$@n=U8gs zSsBHi-QmfLLw5avB$y7z>D>PD@8OH1VhK`fy#&;{?=q{Z`XU$~r%a4@?E|7WJtApj zdr3wPZRkU@`F6Mew$b;%Fd0aM`mBY_pJ{?A4`q;VrHlqTa9>82QOG3d39nAC+XQ+4 zHB86cRppEVQsB@78A+rsNi49DgkYzNdt~Bht|ZMK)Ffcp{?|lyL6WrRF@_DQ8JI$H zT@QqGqyV;m&%jz}f)vdj#JAt`RvrgNd%=$hrwPGdh~}nWGJVjQdT2-Y)1@0&*^h35 z6wN*(Rh9-yT^yK+FJ67^%6|A8PY}sN$|9 zj3rT!9E5PI49z}mI&5;+exwDQG8`5jin&rOb+jzao-+r^LaewBWYL|nMb&%A#CF5@*F&c2!H6t*V2sEQzF)NaWs`d)ZzW9s#e&5FXI3 z)ue!@66t~TKA+2`b^G0-f70X`Zkts;&5ZjtHX4~WDfWK~d#w2EG@Kv=u% z+DToAU|&cj+%$6C9(_}$RZ;t36*lcK{@@}xo))%XnI~((wb5WfiVE>zCf4#e ziWOC;kU$&MW_BB9Q69t^Prtibq_nJ8*!M-kI*SmHVoOzN z_H;K%73!U)gtn`}uuf4>g);+HVp1g*ZBSuFN@^r~TB-pG&zqP60cylm`$xxlG=Qhu z*9nij3>{2mN8T-5w$vUi(twetJ4ChDVgxCx10&XvWsFoONg@!VXNI!qZgpZwHb!GS zSu|&~s=S5p#?JQW_Z@-Ic?f20?w0o>TBt#5AsZi6g={DngA0BT6?1H$nsJlOF(k4GuMWh4=i%dWa+R@bKBSg``rNRrSWuYuTcebj0 zFJrDrM?`OdUGPLF;RJg)fh`uxr`gvqw)4Rs^LDUkR+6d|D&}`}B^E8-!-`ZiNq(`= zgfWzQQWcdPS2aOVnuHvO;p4h$7}i0THEDJo>Q`1COMxRd^cQyKUeAj+;^~e;x1!G^=0{4S|q9RrKc=x z1FAKgLvZu_H*yUq%l7KzqcCaTfv1~X zezF70uK|9qUN7O>$sB?PUX~R$W`!GCq(h9U9^4f02@DAWFYCkKU7s|;w2_#lg$|8S z`zvQQ=|B|3kHR88GS($g;G_%a+5i?UoCVg$>XJy1 z8t9}Hvi{r>pcHTydM;iSS1|HbLX=qZDlnikiQWO*S&0eWFOi#k(eeKP#YZ}|kURSnj<+(>awM#?m#p{*2YmZ6?s zu0O=L1`KF!vs(AD)Yn~F|4|=BAA(p}1e;>+S?``hn99B&=R$);(I;4oqG>Y8ZNjKo z7K`S91x%Y>VN|b;MdkI$OmD1q{l^(FJ3mEuFJUQ#T=nU}Oxfl7Fkbb|)KP7Ux&gYP zPqPo~Vo`_4?9MsK}TEbrsh0 z>ROMCJ=JsU3$V(428QjrtcD~CxC`Br(3hCC4HKR}`;&YS73v?4+lB^0Q!)V zBZjNr6WGv-G|N6_PUT84wa`>}6&?MBa2HsSs1aC!3iICtj{3=$LN~2QGm5b6<{ws; zY)!K-71;P>?z{<^EMc_pF?{XIXx*!c?QPKF$(jaNb#vg1Zz#q4tC+>#g836Q)t!yLg4 zvBFSv*cMh0^WYCgp|8r=tN8hGAan=U0b9c{l|3r{w5v6L2%J#76bR3ZW{c1_TQW1! zcF-OB_GzM;y_%XmZyiKo`0RcLHtrQUd05)_s3yugs;P~l4r|KvqA2$VO42cgV%%#{ zHL2q9!ig}!U+Xo&t6f3y{b?I!HqFL5;P!adw#Mt4?0XeVuGcBEo;w>{4_hmqEvE~< zb`@|QK=)<1LI0k*3op>~3Sj@nje2+Wnxhur@AK39w*=yg_@gtBsB0fYvX0hZfPaJ#*mE!$V1XF+obYlk zdQt%7h!Qw6gkhU?1m0m!PKvWJ z?6Z%+Z|q4(wPJWw3V|&hX!a$7@eTmbJw)Jm2eQyqSm5Ei6&k$)Ivt-kbba8zT^vQ{ z9BB4+2>8}n9nt^N&@Tttlo=}qjsQG6jhr266DCO%G*+78B#{TpZLXf97ttC=q9+p< z8*mLh>;k?4Aa?(b7TR73!@_VVIeKkbS5Uqc25Z1b;qE9ZV7TgN!BAS6F(rN|s7R_L z@VTKREQ&E4RL#J;NNN~4mEzSqiAIg?@6iS=IyLb@*GEN83>0R1CSm<#E9`&-ii(n&64qj>3FCn< zS13Di7Y6t!#g)v=I#F@Y`A|0ATDac4J!~}}T*)4>6BLLkozOw(d0h8b!^^Jp*N- z>)ylWP`}$-cnK#>p#XQX3Qlr|>L-|DKm*-&Cr5SD_Ow28fHg!Ew3u5@RDYH#JA!n} zto$=&uzuhjhac_%=$nDaWdw<|`6Hm3ZG$kNjt-0<1M)0JD;?3J5wr<2sVJ^(wpar3 zp#zDYn_Safbx`dp9aYretRso6JxItodC)o{lhI)hP#ft%@@t`gpp^~u(;0lj6IE5Pj);b)9JN5nbY^mzah|{;xL19{c|c6 z*^Y*InLCQiJ*!56Z%5>^=$TPuCU}I=)wfx+V-)FbE&hd~Ghm?w3=yu{U-g*EX7U-M zp=`xh3}~WlqiH2(ShlU%QT`Cdl1!KStT{9NF*oBs0RMLt?$64q~BRNk} zrH&_v@@!d=wHs%baI`~#J zY@FpF-QGv+$HUJ(tAHBjLJi!UvB?n^W1s7c^8)Kqomf2rFJfKult)WXL6vo|x#NyN zy%8+6$cs$OdR2xFnLx!a1ffM#IP-Jm=ZQkxUBJ1^lmJfft1HVvHA#PJ&p|N^Iuiw-UXj^zz^36 z2N&z2;)S}pz2>PySjT~AWak4p>Df5a?F|@pU(TZX;~|Rh6H#aQMReUd7A;((D~nc+ zC*8goi^SHmqMY$$wttP$(FeOx8N~M{O}6p|(Xt1uDA1b}Sc%aiPgyj}n}o#rjNGGf z;PegPO>k!SCr!{^!BShi$-#@A$<%Y<;5biM?s5C)N)4v6yP~#jjiM(YzJg#)#0|%g zUr69Xo_3w}0b@pWVn7omwnLOR`jFl1FGFRjlwGuszb^fzQ+Jru96Cx?FflBq{M?9-45unjy58PSxZr^1>PFt^f^ zMdKzAd;BJVvgkoXmN!7J_nQ8$BC`;im# zdc&#%=1?GY$yAM&uP62}L?Y{{tZt2OR!mda^gb50Te-mC#ArAVAWf_lp zWb(*nDyi9dD#+@Jdt?&G_7S$ai?9t7o<9GZp!-Zuw%3TQ#k?i2|K{bq)RRIRy+}>A z(}0lm?Qg=oX=K>NVc9Mz3I{G}e^nn!9TiR^g5-D$@bN8Q2i{%p|o0V0iCL!bUS`y?$fi1>rvbPO%Wc z%;5o(GB+8l3#9N1!dCqUSlgmo{*=bxzaMDgvi;849={JWP8}RAaTBil?7zxd`_VL0 z*WqJ!jeamh@DF!g?H=|Qf4Lv6+s_O1Kj#I$917f(@M8o&T_1S$`)~d&KiZVx-=`6N zX2yf22+i!i^Z(MXHj6g)#rE%y^=}e`ImZ4RcQJB7WcOcu>yWVMC01dPbE0e}c({8{ Y7~$h3fotKlh_r~TKLDHSeC)XY0hz82$N&HU diff --git a/Misc/NEWS.d/next/Library/2023-02-17-18-44-27.gh-issue-101997.A6_blD.rst b/Misc/NEWS.d/next/Library/2023-02-17-18-44-27.gh-issue-101997.A6_blD.rst new file mode 100644 index 00000000000000..f9dfd46d1ed430 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-17-18-44-27.gh-issue-101997.A6_blD.rst @@ -0,0 +1 @@ +Upgrade pip wheel bundled with ensurepip (pip 23.0.1) From 207e1c5cae11108213dff5ff07443ee4cfa0d2ea Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 25 Feb 2023 05:21:32 -0800 Subject: [PATCH 190/247] asyncio docs: Fix dangling hyphen (#102227) Currently this gets rendered with a dangling hyphen. --- Doc/library/asyncio-eventloop.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index f86e784288029c..5138afc2bbe47b 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -524,8 +524,8 @@ Opening network connections When a server's IPv4 path and protocol are working, but the server's IPv6 path and protocol are not working, a dual-stack client application experiences significant connection delay compared to an - IPv4-only client. This is undesirable because it causes the dual- - stack client to have a worse user experience. This document + IPv4-only client. This is undesirable because it causes the + dual-stack client to have a worse user experience. This document specifies requirements for algorithms that reduce this user-visible delay and provides an algorithm. From a35fd38b57d3eb05074ca36f3d57e1993c44ddc9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 25 Feb 2023 11:15:48 -0500 Subject: [PATCH 191/247] gh-102209: Sync with zipp 3.15 moving complexity tests into dedicated module (#102232) Sync with jaraco/zipp@757a4e1a. --- Lib/test/test_zipfile/_context.py | 30 --------------- Lib/test/test_zipfile/_func_timeout_compat.py | 8 ---- Lib/test/test_zipfile/_itertools.py | 38 +++++++++++++++++++ Lib/test/test_zipfile/_support.py | 9 +++++ Lib/test/test_zipfile/test_complexity.py | 24 ++++++++++++ Lib/test/test_zipfile/test_path.py | 22 +++++------ Lib/zipfile/_path.py | 7 +++- 7 files changed, 87 insertions(+), 51 deletions(-) delete mode 100644 Lib/test/test_zipfile/_context.py delete mode 100644 Lib/test/test_zipfile/_func_timeout_compat.py create mode 100644 Lib/test/test_zipfile/_support.py create mode 100644 Lib/test/test_zipfile/test_complexity.py diff --git a/Lib/test/test_zipfile/_context.py b/Lib/test/test_zipfile/_context.py deleted file mode 100644 index 348798aa08d452..00000000000000 --- a/Lib/test/test_zipfile/_context.py +++ /dev/null @@ -1,30 +0,0 @@ -import contextlib -import time - - -class DeadlineExceeded(Exception): - pass - - -class TimedContext(contextlib.ContextDecorator): - """ - A context that will raise DeadlineExceeded if the - max duration is reached during the execution. - - >>> TimedContext(1)(time.sleep)(.1) - >>> TimedContext(0)(time.sleep)(.1) - Traceback (most recent call last): - ... - tests._context.DeadlineExceeded: (..., 0) - """ - - def __init__(self, max_duration: int): - self.max_duration = max_duration - - def __enter__(self): - self.start = time.monotonic() - - def __exit__(self, *err): - duration = time.monotonic() - self.start - if duration > self.max_duration: - raise DeadlineExceeded(duration, self.max_duration) diff --git a/Lib/test/test_zipfile/_func_timeout_compat.py b/Lib/test/test_zipfile/_func_timeout_compat.py deleted file mode 100644 index b1f2b2698a8538..00000000000000 --- a/Lib/test/test_zipfile/_func_timeout_compat.py +++ /dev/null @@ -1,8 +0,0 @@ -try: - from func_timeout import func_set_timeout as set_timeout -except ImportError: # pragma: no cover - # provide a fallback that doesn't actually time out - from ._context import TimedContext as set_timeout - - -__all__ = ['set_timeout'] diff --git a/Lib/test/test_zipfile/_itertools.py b/Lib/test/test_zipfile/_itertools.py index 74f01fe5ba3de2..f735dd21733006 100644 --- a/Lib/test/test_zipfile/_itertools.py +++ b/Lib/test/test_zipfile/_itertools.py @@ -1,4 +1,6 @@ import itertools +from collections import deque +from itertools import islice # from jaraco.itertools 6.3.0 @@ -39,3 +41,39 @@ def always_iterable(obj, base_type=(str, bytes)): return iter(obj) except TypeError: return iter((obj,)) + + +# from more_itertools v9.0.0 +def consume(iterator, n=None): + """Advance *iterable* by *n* steps. If *n* is ``None``, consume it + entirely. + Efficiently exhausts an iterator without returning values. Defaults to + consuming the whole iterator, but an optional second argument may be + provided to limit consumption. + >>> i = (x for x in range(10)) + >>> next(i) + 0 + >>> consume(i, 3) + >>> next(i) + 4 + >>> consume(i) + >>> next(i) + Traceback (most recent call last): + File "", line 1, in + StopIteration + If the iterator has fewer items remaining than the provided limit, the + whole iterator will be consumed. + >>> i = (x for x in range(3)) + >>> consume(i, 5) + >>> next(i) + Traceback (most recent call last): + File "", line 1, in + StopIteration + """ + # Use functions that consume iterators at C speed. + if n is None: + # feed the entire iterator into a zero-length deque + deque(iterator, maxlen=0) + else: + # advance to the empty slice starting at position n + next(islice(iterator, n, n), None) diff --git a/Lib/test/test_zipfile/_support.py b/Lib/test/test_zipfile/_support.py new file mode 100644 index 00000000000000..1afdf3b3a773d7 --- /dev/null +++ b/Lib/test/test_zipfile/_support.py @@ -0,0 +1,9 @@ +import importlib +import unittest + + +def import_or_skip(name): + try: + return importlib.import_module(name) + except ImportError: # pragma: no cover + raise unittest.SkipTest(f'Unable to import {name}') diff --git a/Lib/test/test_zipfile/test_complexity.py b/Lib/test/test_zipfile/test_complexity.py new file mode 100644 index 00000000000000..3432dc39e56c4e --- /dev/null +++ b/Lib/test/test_zipfile/test_complexity.py @@ -0,0 +1,24 @@ +import unittest +import string +import zipfile + +from ._functools import compose +from ._itertools import consume + +from ._support import import_or_skip + + +big_o = import_or_skip('big_o') + + +class TestComplexity(unittest.TestCase): + def test_implied_dirs_performance(self): + best, others = big_o.big_o( + compose(consume, zipfile.CompleteDirs._implied_dirs), + lambda size: [ + '/'.join(string.ascii_lowercase + str(n)) for n in range(size) + ], + max_n=1000, + min_n=1, + ) + assert best <= big_o.complexities.Linear diff --git a/Lib/test/test_zipfile/test_path.py b/Lib/test/test_zipfile/test_path.py index 53cbef15a17dc6..aff91e53995875 100644 --- a/Lib/test/test_zipfile/test_path.py +++ b/Lib/test/test_zipfile/test_path.py @@ -3,7 +3,6 @@ import contextlib import pathlib import pickle -import string import sys import unittest import zipfile @@ -12,7 +11,6 @@ from ._itertools import Counter from ._test_params import parameterize, Invoked -from ._func_timeout_compat import set_timeout from test.support.os_helper import temp_dir @@ -22,9 +20,6 @@ class itertools: Counter = Counter -consume = tuple - - def add_dirs(zf): """ Given a writable zip file zf, inject directory entries for @@ -330,12 +325,6 @@ def test_joinpath_constant_time(self): # Check the file iterated all items assert entries.count == self.HUGE_ZIPFILE_NUM_ENTRIES - # timeout disabled due to #102209 - # @set_timeout(3) - def test_implied_dirs_performance(self): - data = ['/'.join(string.ascii_lowercase + str(n)) for n in range(10000)] - zipfile.CompleteDirs._implied_dirs(data) - @pass_alpharep def test_read_does_not_close(self, alpharep): alpharep = self.zipfile_ondisk(alpharep) @@ -513,7 +502,7 @@ def test_pickle(self, alpharep, path_type, subpath): saved_1 = pickle.dumps(zipfile.Path(zipfile_ondisk, at=subpath)) restored_1 = pickle.loads(saved_1) first, *rest = restored_1.iterdir() - assert first.read_text().startswith('content of ') + assert first.read_text(encoding='utf-8').startswith('content of ') @pass_alpharep def test_extract_orig_with_implied_dirs(self, alpharep): @@ -525,3 +514,12 @@ def test_extract_orig_with_implied_dirs(self, alpharep): # wrap the zipfile for its side effect zipfile.Path(zf) zf.extractall(source_path.parent) + + @pass_alpharep + def test_getinfo_missing(self, alpharep): + """ + Validate behavior of getinfo on original zipfile after wrapping. + """ + zipfile.Path(alpharep) + with self.assertRaises(KeyError): + alpharep.getinfo('does-not-exist') diff --git a/Lib/zipfile/_path.py b/Lib/zipfile/_path.py index c2c804e96d6b6c..fd49a3ea91db59 100644 --- a/Lib/zipfile/_path.py +++ b/Lib/zipfile/_path.py @@ -86,6 +86,11 @@ class CompleteDirs(InitializedState, zipfile.ZipFile): """ A ZipFile subclass that ensures that implied directories are always included in the namelist. + + >>> list(CompleteDirs._implied_dirs(['foo/bar.txt', 'foo/bar/baz.txt'])) + ['foo/', 'foo/bar/'] + >>> list(CompleteDirs._implied_dirs(['foo/bar.txt', 'foo/bar/baz.txt', 'foo/bar/'])) + ['foo/'] """ @staticmethod @@ -215,7 +220,7 @@ class Path: Read text: - >>> c.read_text() + >>> c.read_text(encoding='utf-8') 'content of c' existence: From 41970436373f4be813fe8f5a07b6da04d5f4c80e Mon Sep 17 00:00:00 2001 From: Eclips4 <80244920+Eclips4@users.noreply.github.com> Date: Sat, 25 Feb 2023 23:50:24 +0300 Subject: [PATCH 192/247] gh-102252: Improve coverage of test_bool.py (#102253) Add tests for conversion from bool to complex. --- Lib/test/test_bool.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Lib/test/test_bool.py b/Lib/test/test_bool.py index f46f21da8da351..b711ffb9a3ecd5 100644 --- a/Lib/test/test_bool.py +++ b/Lib/test/test_bool.py @@ -40,6 +40,12 @@ def test_float(self): self.assertEqual(float(True), 1.0) self.assertIsNot(float(True), True) + def test_complex(self): + self.assertEqual(complex(False), 0j) + self.assertEqual(complex(False), False) + self.assertEqual(complex(True), 1+0j) + self.assertEqual(complex(True), True) + def test_math(self): self.assertEqual(+False, 0) self.assertIsNot(+False, False) From a498de4c0ef9e264cab3320afbc4d38df6394800 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Sun, 26 Feb 2023 00:48:00 +0300 Subject: [PATCH 193/247] gh-101100: Fix sphinx warnings in `typing` module docs (#102260) --- 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 169f7196a74ec6..bbbf6920ddec88 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1588,7 +1588,7 @@ These are not used in annotations. They are building blocks for creating generic methods, not their type signatures. For example, :class:`ssl.SSLObject` is a class, therefore it passes an :func:`issubclass` check against :data:`Callable`. However, the - :meth:`ssl.SSLObject.__init__` method exists only to raise a + ``ssl.SSLObject.__init__`` method exists only to raise a :exc:`TypeError` with a more informative message, therefore making it impossible to call (instantiate) :class:`ssl.SSLObject`. From d71edbd1b7437706519a9786211597d95934331a Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 25 Feb 2023 16:01:58 -0800 Subject: [PATCH 194/247] gh-101765: Fix refcount issues in list and unicode pickling (#102265) Followup from #101769. --- Objects/listobject.c | 8 ++++++++ Objects/unicodeobject.c | 4 +++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Objects/listobject.c b/Objects/listobject.c index 494b40178f9f27..1a210e77d55c29 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -3451,16 +3451,24 @@ listiter_reduce_general(void *_it, int forward) /* the objects are not the same, index is of different types! */ if (forward) { PyObject *iter = _PyEval_GetBuiltin(&_Py_ID(iter)); + if (!iter) { + return NULL; + } _PyListIterObject *it = (_PyListIterObject *)_it; if (it->it_seq) { return Py_BuildValue("N(O)n", iter, it->it_seq, it->it_index); } + Py_DECREF(iter); } else { PyObject *reversed = _PyEval_GetBuiltin(&_Py_ID(reversed)); + if (!reversed) { + return NULL; + } listreviterobject *it = (listreviterobject *)_it; if (it->it_seq) { return Py_BuildValue("N(O)n", reversed, it->it_seq, it->it_index); } + Py_DECREF(reversed); } /* empty iterator, create an empty list */ list = PyList_New(0); diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 6403e359c70714..2f4c3d3793efd6 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -14794,8 +14794,10 @@ unicodeiter_reduce(unicodeiterobject *it, PyObject *Py_UNUSED(ignored)) return Py_BuildValue("N(O)n", iter, it->it_seq, it->it_index); } else { PyObject *u = unicode_new_empty(); - if (u == NULL) + if (u == NULL) { + Py_DECREF(iter); return NULL; + } return Py_BuildValue("N(N)", iter, u); } } From bcadcde7122f6d3d08b35671d67e105149371a2f Mon Sep 17 00:00:00 2001 From: Skip Montanaro Date: Sat, 25 Feb 2023 20:22:16 -0600 Subject: [PATCH 195/247] gh-102259: Fix re doc issue regarding right square brackets (#102264) Co-authored-by: Terry Jan Reedy --- Doc/library/re.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/library/re.rst b/Doc/library/re.rst index d0a16b95184474..b7510b93d75427 100644 --- a/Doc/library/re.rst +++ b/Doc/library/re.rst @@ -271,7 +271,8 @@ The special characters are: * To match a literal ``']'`` inside a set, precede it with a backslash, or place it at the beginning of the set. For example, both ``[()[\]{}]`` and - ``[]()[{}]`` will both match a parenthesis. + ``[]()[{}]`` will match a right bracket, as well as left bracket, braces, + and parentheses. .. .. index:: single: --; in regular expressions .. .. index:: single: &&; in regular expressions From 6daf42b28e1c6d5f0c1a6350cfcc382789e11293 Mon Sep 17 00:00:00 2001 From: VMan Date: Sun, 26 Feb 2023 13:15:27 +0000 Subject: [PATCH 196/247] [doc] Improve grammar/fix missing word (GH-102060) --- Doc/bugs.rst | 2 +- Doc/howto/logging-cookbook.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/bugs.rst b/Doc/bugs.rst index 4f30ef19ee4d8a..d98192b369603e 100644 --- a/Doc/bugs.rst +++ b/Doc/bugs.rst @@ -70,7 +70,7 @@ Click on the "New issue" button in the top bar to report a new issue. The submission form has two fields, "Title" and "Comment". For the "Title" field, enter a *very* short description of the problem; -less than ten words is good. +fewer than ten words is good. In the "Comment" field, describe the problem in detail, including what you expected to happen and what did happen. Be sure to include whether any diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index 7661249ad522fa..1a0afb6940dab9 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -2538,7 +2538,7 @@ should be logged, or the ``extra`` keyword parameter to indicate additional contextual information to be added to the log). So you cannot directly make logging calls using :meth:`str.format` or :class:`string.Template` syntax, because internally the logging package uses %-formatting to merge the format -string and the variable arguments. There would no changing this while preserving +string and the variable arguments. There would be no changing this while preserving backward compatibility, since all logging calls which are out there in existing code will be using %-format strings. From 8d0f09b1beafd95763a5da53acc58dac0bd63a53 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sun, 26 Feb 2023 14:45:37 -0800 Subject: [PATCH 197/247] gh-101765: unicodeobject: use Py_XDECREF correctly (#102283) --- Objects/unicodeobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 2f4c3d3793efd6..1ba30421c66dba 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -14795,7 +14795,7 @@ unicodeiter_reduce(unicodeiterobject *it, PyObject *Py_UNUSED(ignored)) } else { PyObject *u = unicode_new_empty(); if (u == NULL) { - Py_DECREF(iter); + Py_XDECREF(iter); return NULL; } return Py_BuildValue("N(N)", iter, u); From f3cb15c88afa2e87067de3c6106664b3f7cd4035 Mon Sep 17 00:00:00 2001 From: Rotzbua Date: Mon, 27 Feb 2023 03:10:34 +0100 Subject: [PATCH 198/247] gh-91038: Change default argument value to `False` instead of `0` (#31621) The argument is used as a switch and corresponds to a boolean logic. Therefore it is more intuitive to use the corresponding constant `False` as default value instead of the integer `0`. Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Co-authored-by: Oleg Iarygin --- Doc/library/platform.rst | 2 +- Lib/platform.py | 2 +- .../next/Library/2023-02-26-12-37-17.gh-issue-91038.S4rFH_.rst | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-02-26-12-37-17.gh-issue-91038.S4rFH_.rst diff --git a/Doc/library/platform.rst b/Doc/library/platform.rst index a0c9f63ab9f957..69c4dfc422c98e 100644 --- a/Doc/library/platform.rst +++ b/Doc/library/platform.rst @@ -63,7 +63,7 @@ Cross Platform string is returned if the value cannot be determined. -.. function:: platform(aliased=0, terse=0) +.. function:: platform(aliased=False, terse=False) Returns a single string identifying the underlying platform with as much useful information as possible. diff --git a/Lib/platform.py b/Lib/platform.py index 2dfaf76252db51..f2b0d1d1bd3f5d 100755 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -1246,7 +1246,7 @@ def python_compiler(): _platform_cache = {} -def platform(aliased=0, terse=0): +def platform(aliased=False, terse=False): """ Returns a single string identifying the underlying platform with as much useful information as possible (but no more :). diff --git a/Misc/NEWS.d/next/Library/2023-02-26-12-37-17.gh-issue-91038.S4rFH_.rst b/Misc/NEWS.d/next/Library/2023-02-26-12-37-17.gh-issue-91038.S4rFH_.rst new file mode 100644 index 00000000000000..2667ff120fd402 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-26-12-37-17.gh-issue-91038.S4rFH_.rst @@ -0,0 +1 @@ +:meth:`platform.platform` now has boolean default arguments. From 101a12c5767a8c6ca6e32b8e24a462d2606d24ca Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Mon, 27 Feb 2023 10:26:21 +0300 Subject: [PATCH 199/247] gh-101100: Fix sphinx warnings in `types` module (#102274) Co-authored-by: Alex Waygood --- Doc/library/types.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/types.rst b/Doc/library/types.rst index 415413c4cd9747..747ba58bb225d4 100644 --- a/Doc/library/types.rst +++ b/Doc/library/types.rst @@ -486,7 +486,7 @@ Coroutine Utility Functions The generator-based coroutine is still a :term:`generator iterator`, but is also considered to be a :term:`coroutine` object and is :term:`awaitable`. However, it may not necessarily implement - the :meth:`__await__` method. + the :meth:`~object.__await__` method. If *gen_func* is a generator function, it will be modified in-place. From e3c3f9fec099fe78d2f98912be337d632f6fcdd1 Mon Sep 17 00:00:00 2001 From: Dennis Sweeney <36520290+sweeneyde@users.noreply.github.com> Date: Mon, 27 Feb 2023 05:46:40 -0500 Subject: [PATCH 200/247] gh-102250: Fix double-decref in COMPARE_AND_BRANCH error case (GH-102287) --- Lib/test/test_bool.py | 20 +++++++++++++++++++ ...-02-26-23-10-32.gh-issue-102250.7MUKoC.rst | 1 + Python/bytecodes.c | 4 +--- Python/generated_cases.c.h | 4 +--- 4 files changed, 23 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-26-23-10-32.gh-issue-102250.7MUKoC.rst diff --git a/Lib/test/test_bool.py b/Lib/test/test_bool.py index b711ffb9a3ecd5..916e22a527a8e0 100644 --- a/Lib/test/test_bool.py +++ b/Lib/test/test_bool.py @@ -319,6 +319,26 @@ def __len__(self): return -1 self.assertRaises(ValueError, bool, Eggs()) + def test_interpreter_convert_to_bool_raises(self): + class SymbolicBool: + def __bool__(self): + raise TypeError + + class Symbol: + def __gt__(self, other): + return SymbolicBool() + + x = Symbol() + + with self.assertRaises(TypeError): + if x > 0: + msg = "x > 0 was true" + else: + msg = "x > 0 was false" + + # This used to create negative refcounts, see gh-102250 + del x + def test_from_bytes(self): self.assertIs(bool.from_bytes(b'\x00'*8, 'big'), False) self.assertIs(bool.from_bytes(b'abcd', 'little'), True) diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-26-23-10-32.gh-issue-102250.7MUKoC.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-26-23-10-32.gh-issue-102250.7MUKoC.rst new file mode 100644 index 00000000000000..17ab0cd4367991 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-26-23-10-32.gh-issue-102250.7MUKoC.rst @@ -0,0 +1 @@ +Fixed a segfault occurring when the interpreter calls a ``__bool__`` method that raises. diff --git a/Python/bytecodes.c b/Python/bytecodes.c index ad68c794fe7acb..7e9b36f697210a 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1754,9 +1754,7 @@ dummy_func( int offset = next_instr[1].op.arg; int err = PyObject_IsTrue(cond); Py_DECREF(cond); - if (err < 0) { - goto error; - } + ERROR_IF(err < 0, error); if (jump_on_true == (err != 0)) { JUMPBY(offset); } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 2987adc3bba566..271ba26f489521 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2205,9 +2205,7 @@ int offset = next_instr[1].op.arg; int err = PyObject_IsTrue(cond); Py_DECREF(cond); - if (err < 0) { - goto error; - } + if (err < 0) goto pop_2_error; if (jump_on_true == (err != 0)) { JUMPBY(offset); } From 0db6f442598a1994c37f24e704892a2bb71a0a1b Mon Sep 17 00:00:00 2001 From: Gouvernathor <44340603+Gouvernathor@users.noreply.github.com> Date: Mon, 27 Feb 2023 16:13:18 +0100 Subject: [PATCH 201/247] gh-102296 Document that inspect.Parameter kinds support ordering (GH-102297) Automerge-Triggered-By: GH:AlexWaygood --- Doc/library/inspect.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index 9c3be5a250a67e..789e9839d22f71 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -802,8 +802,9 @@ function. .. attribute:: Parameter.kind - Describes how argument values are bound to the parameter. Possible values - (accessible via :class:`Parameter`, like ``Parameter.KEYWORD_ONLY``): + Describes how argument values are bound to the parameter. The possible + values are accessible via :class:`Parameter` (like ``Parameter.KEYWORD_ONLY``), + and support comparison and ordering, in the following order: .. tabularcolumns:: |l|L| From bb0cf8fd60e71581a90179af63e60e8704c3814b Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 27 Feb 2023 09:21:18 -0700 Subject: [PATCH 202/247] gh-102251: Updates to test_imp Toward Fixing Some Refleaks (gh-102254) This is related to fixing the refleaks introduced by commit 096d009. I haven't been able to find the leak yet, but these changes are a consequence of that effort. This includes some cleanup, some tweaks to the existing tests, and a bunch of new test cases. The only change here that might have impact outside the tests in question is in imp.py, where I update imp.load_dynamic() to use spec_from_file_location() instead of creating a ModuleSpec directly. Also note that I've updated the tests to only skip if we're checking for refleaks (regrtest's --huntrleaks), whereas in gh-101969 I had skipped the tests entirely. The tests will be useful for some upcoming work and I'd rather the refleaks not hold that up. (It isn't clear how quickly we'll be able to fix the leaking code, though it will certainly be done in the short term.) https://github.com/python/cpython/issues/102251 --- Lib/imp.py | 4 +- Lib/test/test_imp.py | 1110 +++++++++++++++++++++++++++--------- Modules/_testsinglephase.c | 22 +- Python/import.c | 115 +++- 4 files changed, 952 insertions(+), 299 deletions(-) diff --git a/Lib/imp.py b/Lib/imp.py index fc42c15765852e..fe850f6a001814 100644 --- a/Lib/imp.py +++ b/Lib/imp.py @@ -338,8 +338,8 @@ def load_dynamic(name, path, file=None): # Issue #24748: Skip the sys.modules check in _load_module_shim; # always load new extension - spec = importlib.machinery.ModuleSpec( - name=name, loader=loader, origin=path) + spec = importlib.util.spec_from_file_location( + name, path, loader=loader) return _load(spec) else: diff --git a/Lib/test/test_imp.py b/Lib/test/test_imp.py index 2292bb20939599..03e3adba221e57 100644 --- a/Lib/test/test_imp.py +++ b/Lib/test/test_imp.py @@ -1,4 +1,5 @@ import gc +import json import importlib import importlib.util import os @@ -11,6 +12,7 @@ from test.support import script_helper from test.support import warnings_helper import textwrap +import types import unittest import warnings imp = warnings_helper.import_deprecated('imp') @@ -39,6 +41,169 @@ def requires_load_dynamic(meth): 'imp.load_dynamic() required')(meth) +class ModuleSnapshot(types.SimpleNamespace): + """A representation of a module for testing. + + Fields: + + * id - the module's object ID + * module - the actual module or an adequate substitute + * __file__ + * __spec__ + * name + * origin + * ns - a copy (dict) of the module's __dict__ (or None) + * ns_id - the object ID of the module's __dict__ + * cached - the sys.modules[mod.__spec__.name] entry (or None) + * cached_id - the object ID of the sys.modules entry (or None) + + In cases where the value is not available (e.g. due to serialization), + the value will be None. + """ + _fields = tuple('id module ns ns_id cached cached_id'.split()) + + @classmethod + def from_module(cls, mod): + name = mod.__spec__.name + cached = sys.modules.get(name) + return cls( + id=id(mod), + module=mod, + ns=types.SimpleNamespace(**mod.__dict__), + ns_id=id(mod.__dict__), + cached=cached, + cached_id=id(cached), + ) + + SCRIPT = textwrap.dedent(''' + {imports} + + name = {name!r} + + {prescript} + + mod = {name} + + {body} + + {postscript} + ''') + IMPORTS = textwrap.dedent(''' + import sys + ''').strip() + SCRIPT_BODY = textwrap.dedent(''' + # Capture the snapshot data. + cached = sys.modules.get(name) + snapshot = dict( + id=id(mod), + module=dict( + __file__=mod.__file__, + __spec__=dict( + name=mod.__spec__.name, + origin=mod.__spec__.origin, + ), + ), + ns=None, + ns_id=id(mod.__dict__), + cached=None, + cached_id=id(cached) if cached else None, + ) + ''').strip() + CLEANUP_SCRIPT = textwrap.dedent(''' + # Clean up the module. + sys.modules.pop(name, None) + ''').strip() + + @classmethod + def build_script(cls, name, *, + prescript=None, + import_first=False, + postscript=None, + postcleanup=False, + ): + if postcleanup is True: + postcleanup = cls.CLEANUP_SCRIPT + elif isinstance(postcleanup, str): + postcleanup = textwrap.dedent(postcleanup).strip() + postcleanup = cls.CLEANUP_SCRIPT + os.linesep + postcleanup + else: + postcleanup = '' + prescript = textwrap.dedent(prescript).strip() if prescript else '' + postscript = textwrap.dedent(postscript).strip() if postscript else '' + + if postcleanup: + if postscript: + postscript = postscript + os.linesep * 2 + postcleanup + else: + postscript = postcleanup + + if import_first: + prescript += textwrap.dedent(f''' + + # Now import the module. + assert name not in sys.modules + import {name}''') + + return cls.SCRIPT.format( + imports=cls.IMPORTS.strip(), + name=name, + prescript=prescript.strip(), + body=cls.SCRIPT_BODY.strip(), + postscript=postscript, + ) + + @classmethod + def parse(cls, text): + raw = json.loads(text) + mod = raw['module'] + mod['__spec__'] = types.SimpleNamespace(**mod['__spec__']) + raw['module'] = types.SimpleNamespace(**mod) + return cls(**raw) + + @classmethod + def from_subinterp(cls, name, interpid=None, *, pipe=None, **script_kwds): + if pipe is not None: + return cls._from_subinterp(name, interpid, pipe, script_kwds) + pipe = os.pipe() + try: + return cls._from_subinterp(name, interpid, pipe, script_kwds) + finally: + r, w = pipe + os.close(r) + os.close(w) + + @classmethod + def _from_subinterp(cls, name, interpid, pipe, script_kwargs): + r, w = pipe + + # Build the script. + postscript = textwrap.dedent(f''' + # Send the result over the pipe. + import json + import os + os.write({w}, json.dumps(snapshot).encode()) + + ''') + _postscript = script_kwargs.get('postscript') + if _postscript: + _postscript = textwrap.dedent(_postscript).lstrip() + postscript += _postscript + script_kwargs['postscript'] = postscript.strip() + script = cls.build_script(name, **script_kwargs) + + # Run the script. + if interpid is None: + ret = support.run_in_subinterp(script) + if ret != 0: + raise AssertionError(f'{ret} != 0') + else: + _interpreters.run_string(interpid, script) + + # Parse the results. + text = os.read(r, 1000) + return cls.parse(text.decode()) + + class LockTests(unittest.TestCase): """Very basic test of import lock functions.""" @@ -263,288 +428,6 @@ def test_issue16421_multiple_modules_in_one_dll(self): with self.assertRaises(ImportError): imp.load_dynamic('nonexistent', pathname) - @unittest.skip('known refleak (temporarily skipping)') - @requires_subinterpreters - @requires_load_dynamic - def test_singlephase_multiple_interpreters(self): - # Currently, for every single-phrase init module loaded - # in multiple interpreters, those interpreters share a - # PyModuleDef for that object, which can be a problem. - - # This single-phase module has global state, which is shared - # by the interpreters. - import _testsinglephase - name = _testsinglephase.__name__ - filename = _testsinglephase.__file__ - - del sys.modules[name] - _testsinglephase._clear_globals() - _testinternalcapi.clear_extension(name, filename) - init_count = _testsinglephase.initialized_count() - assert init_count == -1, (init_count,) - - def clean_up(): - _testsinglephase._clear_globals() - _testinternalcapi.clear_extension(name, filename) - self.addCleanup(clean_up) - - interp1 = _interpreters.create(isolated=False) - self.addCleanup(_interpreters.destroy, interp1) - interp2 = _interpreters.create(isolated=False) - self.addCleanup(_interpreters.destroy, interp2) - - script = textwrap.dedent(f''' - import _testsinglephase - - expected = %d - init_count = _testsinglephase.initialized_count() - if init_count != expected: - raise Exception(init_count) - - lookedup = _testsinglephase.look_up_self() - if lookedup is not _testsinglephase: - raise Exception((_testsinglephase, lookedup)) - - # Attrs set in the module init func are in m_copy. - _initialized = _testsinglephase._initialized - initialized = _testsinglephase.initialized() - if _initialized != initialized: - raise Exception((_initialized, initialized)) - - # Attrs set after loading are not in m_copy. - if hasattr(_testsinglephase, 'spam'): - raise Exception(_testsinglephase.spam) - _testsinglephase.spam = expected - ''') - - # Use an interpreter that gets destroyed right away. - ret = support.run_in_subinterp(script % 1) - self.assertEqual(ret, 0) - - # The module's init func gets run again. - # The module's globals did not get destroyed. - _interpreters.run_string(interp1, script % 2) - - # The module's init func is not run again. - # The second interpreter copies the module's m_copy. - # However, globals are still shared. - _interpreters.run_string(interp2, script % 2) - - @unittest.skip('known refleak (temporarily skipping)') - @requires_load_dynamic - def test_singlephase_variants(self): - # Exercise the most meaningful variants described in Python/import.c. - self.maxDiff = None - - basename = '_testsinglephase' - fileobj, pathname, _ = imp.find_module(basename) - fileobj.close() - - def clean_up(): - import _testsinglephase - _testsinglephase._clear_globals() - self.addCleanup(clean_up) - - def add_ext_cleanup(name): - def clean_up(): - _testinternalcapi.clear_extension(name, pathname) - self.addCleanup(clean_up) - - modules = {} - def load(name): - assert name not in modules - module = imp.load_dynamic(name, pathname) - self.assertNotIn(module, modules.values()) - modules[name] = module - return module - - def re_load(name, module): - assert sys.modules[name] is module - before = type(module)(module.__name__) - before.__dict__.update(vars(module)) - - reloaded = imp.load_dynamic(name, pathname) - - return before, reloaded - - def check_common(name, module): - summed = module.sum(1, 2) - lookedup = module.look_up_self() - initialized = module.initialized() - cached = sys.modules[name] - - # module.__name__ might not match, but the spec will. - self.assertEqual(module.__spec__.name, name) - if initialized is not None: - self.assertIsInstance(initialized, float) - self.assertGreater(initialized, 0) - self.assertEqual(summed, 3) - self.assertTrue(issubclass(module.error, Exception)) - self.assertEqual(module.int_const, 1969) - self.assertEqual(module.str_const, 'something different') - self.assertIs(cached, module) - - return lookedup, initialized, cached - - def check_direct(name, module, lookedup): - # The module has its own PyModuleDef, with a matching name. - self.assertEqual(module.__name__, name) - self.assertIs(lookedup, module) - - def check_indirect(name, module, lookedup, orig): - # The module re-uses another's PyModuleDef, with a different name. - assert orig is not module - assert orig.__name__ != name - self.assertNotEqual(module.__name__, name) - self.assertIs(lookedup, module) - - def check_basic(module, initialized): - init_count = module.initialized_count() - - self.assertIsNot(initialized, None) - self.assertIsInstance(init_count, int) - self.assertGreater(init_count, 0) - - return init_count - - def check_common_reloaded(name, module, cached, before, reloaded): - recached = sys.modules[name] - - self.assertEqual(reloaded.__spec__.name, name) - self.assertEqual(reloaded.__name__, before.__name__) - self.assertEqual(before.__dict__, module.__dict__) - self.assertIs(recached, reloaded) - - def check_basic_reloaded(module, lookedup, initialized, init_count, - before, reloaded): - relookedup = reloaded.look_up_self() - reinitialized = reloaded.initialized() - reinit_count = reloaded.initialized_count() - - self.assertIs(reloaded, module) - self.assertIs(reloaded.__dict__, module.__dict__) - # It only happens to be the same but that's good enough here. - # We really just want to verify that the re-loaded attrs - # didn't change. - self.assertIs(relookedup, lookedup) - self.assertEqual(reinitialized, initialized) - self.assertEqual(reinit_count, init_count) - - def check_with_reinit_reloaded(module, lookedup, initialized, - before, reloaded): - relookedup = reloaded.look_up_self() - reinitialized = reloaded.initialized() - - self.assertIsNot(reloaded, module) - self.assertIsNot(reloaded, module) - self.assertNotEqual(reloaded.__dict__, module.__dict__) - self.assertIs(relookedup, reloaded) - if initialized is None: - self.assertIs(reinitialized, None) - else: - self.assertGreater(reinitialized, initialized) - - # Check the "basic" module. - - name = basename - add_ext_cleanup(name) - expected_init_count = 1 - with self.subTest(name): - mod = load(name) - lookedup, initialized, cached = check_common(name, mod) - check_direct(name, mod, lookedup) - init_count = check_basic(mod, initialized) - self.assertEqual(init_count, expected_init_count) - - before, reloaded = re_load(name, mod) - check_common_reloaded(name, mod, cached, before, reloaded) - check_basic_reloaded(mod, lookedup, initialized, init_count, - before, reloaded) - basic = mod - - # Check its indirect variants. - - name = f'{basename}_basic_wrapper' - add_ext_cleanup(name) - expected_init_count += 1 - with self.subTest(name): - mod = load(name) - lookedup, initialized, cached = check_common(name, mod) - check_indirect(name, mod, lookedup, basic) - init_count = check_basic(mod, initialized) - self.assertEqual(init_count, expected_init_count) - - before, reloaded = re_load(name, mod) - check_common_reloaded(name, mod, cached, before, reloaded) - check_basic_reloaded(mod, lookedup, initialized, init_count, - before, reloaded) - - # Currently PyState_AddModule() always replaces the cached module. - self.assertIs(basic.look_up_self(), mod) - self.assertEqual(basic.initialized_count(), expected_init_count) - - # The cached module shouldn't be changed after this point. - basic_lookedup = mod - - # Check its direct variant. - - name = f'{basename}_basic_copy' - add_ext_cleanup(name) - expected_init_count += 1 - with self.subTest(name): - mod = load(name) - lookedup, initialized, cached = check_common(name, mod) - check_direct(name, mod, lookedup) - init_count = check_basic(mod, initialized) - self.assertEqual(init_count, expected_init_count) - - before, reloaded = re_load(name, mod) - check_common_reloaded(name, mod, cached, before, reloaded) - check_basic_reloaded(mod, lookedup, initialized, init_count, - before, reloaded) - - # This should change the cached module for _testsinglephase. - self.assertIs(basic.look_up_self(), basic_lookedup) - self.assertEqual(basic.initialized_count(), expected_init_count) - - # Check the non-basic variant that has no state. - - name = f'{basename}_with_reinit' - add_ext_cleanup(name) - with self.subTest(name): - mod = load(name) - lookedup, initialized, cached = check_common(name, mod) - self.assertIs(initialized, None) - check_direct(name, mod, lookedup) - - before, reloaded = re_load(name, mod) - check_common_reloaded(name, mod, cached, before, reloaded) - check_with_reinit_reloaded(mod, lookedup, initialized, - before, reloaded) - - # This should change the cached module for _testsinglephase. - self.assertIs(basic.look_up_self(), basic_lookedup) - self.assertEqual(basic.initialized_count(), expected_init_count) - - # Check the basic variant that has state. - - name = f'{basename}_with_state' - add_ext_cleanup(name) - with self.subTest(name): - mod = load(name) - lookedup, initialized, cached = check_common(name, mod) - self.assertIsNot(initialized, None) - check_direct(name, mod, lookedup) - - before, reloaded = re_load(name, mod) - check_common_reloaded(name, mod, cached, before, reloaded) - check_with_reinit_reloaded(mod, lookedup, initialized, - before, reloaded) - - # This should change the cached module for _testsinglephase. - self.assertIs(basic.look_up_self(), basic_lookedup) - self.assertEqual(basic.initialized_count(), expected_init_count) - @requires_load_dynamic def test_load_dynamic_ImportError_path(self): # Issue #1559549 added `name` and `path` attributes to ImportError @@ -737,6 +620,669 @@ def check_get_builtins(): check_get_builtins() +class TestSinglePhaseSnapshot(ModuleSnapshot): + + @classmethod + def from_module(cls, mod): + self = super().from_module(mod) + self.summed = mod.sum(1, 2) + self.lookedup = mod.look_up_self() + self.lookedup_id = id(self.lookedup) + self.state_initialized = mod.state_initialized() + if hasattr(mod, 'initialized_count'): + self.init_count = mod.initialized_count() + return self + + SCRIPT_BODY = ModuleSnapshot.SCRIPT_BODY + textwrap.dedent(f''' + snapshot['module'].update(dict( + int_const=mod.int_const, + str_const=mod.str_const, + _module_initialized=mod._module_initialized, + )) + snapshot.update(dict( + summed=mod.sum(1, 2), + lookedup_id=id(mod.look_up_self()), + state_initialized=mod.state_initialized(), + init_count=mod.initialized_count(), + has_spam=hasattr(mod, 'spam'), + spam=getattr(mod, 'spam', None), + )) + ''').rstrip() + + @classmethod + def parse(cls, text): + self = super().parse(text) + if not self.has_spam: + del self.spam + del self.has_spam + return self + + +@requires_load_dynamic +class SinglephaseInitTests(unittest.TestCase): + + NAME = '_testsinglephase' + + @classmethod + def setUpClass(cls): + if '-R' in sys.argv or '--huntrleaks' in sys.argv: + # https://github.com/python/cpython/issues/102251 + raise unittest.SkipTest('unresolved refleaks (see gh-102251)') + fileobj, filename, _ = imp.find_module(cls.NAME) + fileobj.close() + cls.FILE = filename + + # Start fresh. + cls.clean_up() + + def tearDown(self): + # Clean up the module. + self.clean_up() + + @classmethod + def clean_up(cls): + name = cls.NAME + filename = cls.FILE + if name in sys.modules: + if hasattr(sys.modules[name], '_clear_globals'): + assert sys.modules[name].__file__ == filename + sys.modules[name]._clear_globals() + del sys.modules[name] + # Clear all internally cached data for the extension. + _testinternalcapi.clear_extension(name, filename) + + ######################### + # helpers + + def add_module_cleanup(self, name): + def clean_up(): + # Clear all internally cached data for the extension. + _testinternalcapi.clear_extension(name, self.FILE) + self.addCleanup(clean_up) + + def load(self, name): + try: + already_loaded = self.already_loaded + except AttributeError: + already_loaded = self.already_loaded = {} + assert name not in already_loaded + mod = imp.load_dynamic(name, self.FILE) + self.assertNotIn(mod, already_loaded.values()) + already_loaded[name] = mod + return types.SimpleNamespace( + name=name, + module=mod, + snapshot=TestSinglePhaseSnapshot.from_module(mod), + ) + + def re_load(self, name, mod): + assert sys.modules[name] is mod + assert mod.__dict__ == mod.__dict__ + reloaded = imp.load_dynamic(name, self.FILE) + return types.SimpleNamespace( + name=name, + module=reloaded, + snapshot=TestSinglePhaseSnapshot.from_module(reloaded), + ) + + # subinterpreters + + def add_subinterpreter(self): + interpid = _interpreters.create(isolated=False) + _interpreters.run_string(interpid, textwrap.dedent(''' + import sys + import _testinternalcapi + ''')) + def clean_up(): + _interpreters.run_string(interpid, textwrap.dedent(f''' + name = {self.NAME!r} + if name in sys.modules: + sys.modules[name]._clear_globals() + _testinternalcapi.clear_extension(name, {self.FILE!r}) + ''')) + _interpreters.destroy(interpid) + self.addCleanup(clean_up) + return interpid + + def import_in_subinterp(self, interpid=None, *, + postscript=None, + postcleanup=False, + ): + name = self.NAME + + if postcleanup: + import_ = 'import _testinternalcapi' if interpid is None else '' + postcleanup = f''' + {import_} + mod._clear_globals() + _testinternalcapi.clear_extension(name, {self.FILE!r}) + ''' + + try: + pipe = self._pipe + except AttributeError: + r, w = pipe = self._pipe = os.pipe() + self.addCleanup(os.close, r) + self.addCleanup(os.close, w) + + snapshot = TestSinglePhaseSnapshot.from_subinterp( + name, + interpid, + pipe=pipe, + import_first=True, + postscript=postscript, + postcleanup=postcleanup, + ) + + return types.SimpleNamespace( + name=name, + module=None, + snapshot=snapshot, + ) + + # checks + + def check_common(self, loaded): + isolated = False + + mod = loaded.module + if not mod: + # It came from a subinterpreter. + isolated = True + mod = loaded.snapshot.module + # mod.__name__ might not match, but the spec will. + self.assertEqual(mod.__spec__.name, loaded.name) + self.assertEqual(mod.__file__, self.FILE) + self.assertEqual(mod.__spec__.origin, self.FILE) + if not isolated: + self.assertTrue(issubclass(mod.error, Exception)) + self.assertEqual(mod.int_const, 1969) + self.assertEqual(mod.str_const, 'something different') + self.assertIsInstance(mod._module_initialized, float) + self.assertGreater(mod._module_initialized, 0) + + snap = loaded.snapshot + self.assertEqual(snap.summed, 3) + if snap.state_initialized is not None: + self.assertIsInstance(snap.state_initialized, float) + self.assertGreater(snap.state_initialized, 0) + if isolated: + # The "looked up" module is interpreter-specific + # (interp->imports.modules_by_index was set for the module). + self.assertEqual(snap.lookedup_id, snap.id) + self.assertEqual(snap.cached_id, snap.id) + with self.assertRaises(AttributeError): + snap.spam + else: + self.assertIs(snap.lookedup, mod) + self.assertIs(snap.cached, mod) + + def check_direct(self, loaded): + # The module has its own PyModuleDef, with a matching name. + self.assertEqual(loaded.module.__name__, loaded.name) + self.assertIs(loaded.snapshot.lookedup, loaded.module) + + def check_indirect(self, loaded, orig): + # The module re-uses another's PyModuleDef, with a different name. + assert orig is not loaded.module + assert orig.__name__ != loaded.name + self.assertNotEqual(loaded.module.__name__, loaded.name) + self.assertIs(loaded.snapshot.lookedup, loaded.module) + + def check_basic(self, loaded, expected_init_count): + # m_size == -1 + # The module loads fresh the first time and copies m_copy after. + snap = loaded.snapshot + self.assertIsNot(snap.state_initialized, None) + self.assertIsInstance(snap.init_count, int) + self.assertGreater(snap.init_count, 0) + self.assertEqual(snap.init_count, expected_init_count) + + def check_with_reinit(self, loaded): + # m_size >= 0 + # The module loads fresh every time. + pass + + def check_fresh(self, loaded): + """ + The module had not been loaded before (at least since fully reset). + """ + snap = loaded.snapshot + # The module's init func was run. + # A copy of the module's __dict__ was stored in def->m_base.m_copy. + # The previous m_copy was deleted first. + # _PyRuntime.imports.extensions was set. + self.assertEqual(snap.init_count, 1) + # The global state was initialized. + # The module attrs were initialized from that state. + self.assertEqual(snap.module._module_initialized, + snap.state_initialized) + + def check_semi_fresh(self, loaded, base, prev): + """ + The module had been loaded before and then reset + (but the module global state wasn't). + """ + snap = loaded.snapshot + # The module's init func was run again. + # A copy of the module's __dict__ was stored in def->m_base.m_copy. + # The previous m_copy was deleted first. + # The module globals did not get reset. + self.assertNotEqual(snap.id, base.snapshot.id) + self.assertNotEqual(snap.id, prev.snapshot.id) + self.assertEqual(snap.init_count, prev.snapshot.init_count + 1) + # The global state was updated. + # The module attrs were initialized from that state. + self.assertEqual(snap.module._module_initialized, + snap.state_initialized) + self.assertNotEqual(snap.state_initialized, + base.snapshot.state_initialized) + self.assertNotEqual(snap.state_initialized, + prev.snapshot.state_initialized) + + def check_copied(self, loaded, base): + """ + The module had been loaded before and never reset. + """ + snap = loaded.snapshot + # The module's init func was not run again. + # The interpreter copied m_copy, as set by the other interpreter, + # with objects owned by the other interpreter. + # The module globals did not get reset. + self.assertNotEqual(snap.id, base.snapshot.id) + self.assertEqual(snap.init_count, base.snapshot.init_count) + # The global state was not updated since the init func did not run. + # The module attrs were not directly initialized from that state. + # The state and module attrs still match the previous loading. + self.assertEqual(snap.module._module_initialized, + snap.state_initialized) + self.assertEqual(snap.state_initialized, + base.snapshot.state_initialized) + + ######################### + # the tests + + def test_cleared_globals(self): + loaded = self.load(self.NAME) + _testsinglephase = loaded.module + init_before = _testsinglephase.state_initialized() + + _testsinglephase._clear_globals() + init_after = _testsinglephase.state_initialized() + init_count = _testsinglephase.initialized_count() + + self.assertGreater(init_before, 0) + self.assertEqual(init_after, 0) + self.assertEqual(init_count, -1) + + def test_variants(self): + # Exercise the most meaningful variants described in Python/import.c. + self.maxDiff = None + + # Check the "basic" module. + + name = self.NAME + expected_init_count = 1 + with self.subTest(name): + loaded = self.load(name) + + self.check_common(loaded) + self.check_direct(loaded) + self.check_basic(loaded, expected_init_count) + basic = loaded.module + + # Check its indirect variants. + + name = f'{self.NAME}_basic_wrapper' + self.add_module_cleanup(name) + expected_init_count += 1 + with self.subTest(name): + loaded = self.load(name) + + self.check_common(loaded) + self.check_indirect(loaded, basic) + self.check_basic(loaded, expected_init_count) + + # Currently PyState_AddModule() always replaces the cached module. + self.assertIs(basic.look_up_self(), loaded.module) + self.assertEqual(basic.initialized_count(), expected_init_count) + + # The cached module shouldn't change after this point. + basic_lookedup = loaded.module + + # Check its direct variant. + + name = f'{self.NAME}_basic_copy' + self.add_module_cleanup(name) + expected_init_count += 1 + with self.subTest(name): + loaded = self.load(name) + + self.check_common(loaded) + self.check_direct(loaded) + self.check_basic(loaded, expected_init_count) + + # This should change the cached module for _testsinglephase. + self.assertIs(basic.look_up_self(), basic_lookedup) + self.assertEqual(basic.initialized_count(), expected_init_count) + + # Check the non-basic variant that has no state. + + name = f'{self.NAME}_with_reinit' + self.add_module_cleanup(name) + with self.subTest(name): + loaded = self.load(name) + + self.check_common(loaded) + self.assertIs(loaded.snapshot.state_initialized, None) + self.check_direct(loaded) + self.check_with_reinit(loaded) + + # This should change the cached module for _testsinglephase. + self.assertIs(basic.look_up_self(), basic_lookedup) + self.assertEqual(basic.initialized_count(), expected_init_count) + + # Check the basic variant that has state. + + name = f'{self.NAME}_with_state' + self.add_module_cleanup(name) + with self.subTest(name): + loaded = self.load(name) + + self.check_common(loaded) + self.assertIsNot(loaded.snapshot.state_initialized, None) + self.check_direct(loaded) + self.check_with_reinit(loaded) + + # This should change the cached module for _testsinglephase. + self.assertIs(basic.look_up_self(), basic_lookedup) + self.assertEqual(basic.initialized_count(), expected_init_count) + + def test_basic_reloaded(self): + # m_copy is copied into the existing module object. + # Global state is not changed. + self.maxDiff = None + + for name in [ + self.NAME, # the "basic" module + f'{self.NAME}_basic_wrapper', # the indirect variant + f'{self.NAME}_basic_copy', # the direct variant + ]: + self.add_module_cleanup(name) + with self.subTest(name): + loaded = self.load(name) + reloaded = self.re_load(name, loaded.module) + + self.check_common(loaded) + self.check_common(reloaded) + + # Make sure the original __dict__ did not get replaced. + self.assertEqual(id(loaded.module.__dict__), + loaded.snapshot.ns_id) + self.assertEqual(loaded.snapshot.ns.__dict__, + loaded.module.__dict__) + + self.assertEqual(reloaded.module.__spec__.name, reloaded.name) + self.assertEqual(reloaded.module.__name__, + reloaded.snapshot.ns.__name__) + + self.assertIs(reloaded.module, loaded.module) + self.assertIs(reloaded.module.__dict__, loaded.module.__dict__) + # It only happens to be the same but that's good enough here. + # We really just want to verify that the re-loaded attrs + # didn't change. + self.assertIs(reloaded.snapshot.lookedup, + loaded.snapshot.lookedup) + self.assertEqual(reloaded.snapshot.state_initialized, + loaded.snapshot.state_initialized) + self.assertEqual(reloaded.snapshot.init_count, + loaded.snapshot.init_count) + + self.assertIs(reloaded.snapshot.cached, reloaded.module) + + def test_with_reinit_reloaded(self): + # The module's m_init func is run again. + self.maxDiff = None + + # Keep a reference around. + basic = self.load(self.NAME) + + for name in [ + f'{self.NAME}_with_reinit', # m_size == 0 + f'{self.NAME}_with_state', # m_size > 0 + ]: + self.add_module_cleanup(name) + with self.subTest(name): + loaded = self.load(name) + reloaded = self.re_load(name, loaded.module) + + self.check_common(loaded) + self.check_common(reloaded) + + # Make sure the original __dict__ did not get replaced. + self.assertEqual(id(loaded.module.__dict__), + loaded.snapshot.ns_id) + self.assertEqual(loaded.snapshot.ns.__dict__, + loaded.module.__dict__) + + self.assertEqual(reloaded.module.__spec__.name, reloaded.name) + self.assertEqual(reloaded.module.__name__, + reloaded.snapshot.ns.__name__) + + self.assertIsNot(reloaded.module, loaded.module) + self.assertNotEqual(reloaded.module.__dict__, + loaded.module.__dict__) + self.assertIs(reloaded.snapshot.lookedup, reloaded.module) + if loaded.snapshot.state_initialized is None: + self.assertIs(reloaded.snapshot.state_initialized, None) + else: + self.assertGreater(reloaded.snapshot.state_initialized, + loaded.snapshot.state_initialized) + + self.assertIs(reloaded.snapshot.cached, reloaded.module) + + # Currently, for every single-phrase init module loaded + # in multiple interpreters, those interpreters share a + # PyModuleDef for that object, which can be a problem. + # Also, we test with a single-phase module that has global state, + # which is shared by all interpreters. + + @requires_subinterpreters + def test_basic_multiple_interpreters_main_no_reset(self): + # without resetting; already loaded in main interpreter + + # At this point: + # * alive in 0 interpreters + # * module def may or may not be loaded already + # * module def not in _PyRuntime.imports.extensions + # * mod init func has not run yet (since reset, at least) + # * m_copy not set (hasn't been loaded yet or already cleared) + # * module's global state has not been initialized yet + # (or already cleared) + + main_loaded = self.load(self.NAME) + _testsinglephase = main_loaded.module + # Attrs set after loading are not in m_copy. + _testsinglephase.spam = 'spam, spam, spam, spam, eggs, and spam' + + self.check_common(main_loaded) + self.check_fresh(main_loaded) + + interpid1 = self.add_subinterpreter() + interpid2 = self.add_subinterpreter() + + # At this point: + # * alive in 1 interpreter (main) + # * module def in _PyRuntime.imports.extensions + # * mod init func ran for the first time (since reset, at least) + # * m_copy was copied from the main interpreter (was NULL) + # * module's global state was initialized + + # Use an interpreter that gets destroyed right away. + loaded = self.import_in_subinterp() + self.check_common(loaded) + self.check_copied(loaded, main_loaded) + + # At this point: + # * alive in 1 interpreter (main) + # * module def still in _PyRuntime.imports.extensions + # * mod init func ran again + # * m_copy is NULL (claered when the interpreter was destroyed) + # (was from main interpreter) + # * module's global state was updated, not reset + + # Use a subinterpreter that sticks around. + loaded = self.import_in_subinterp(interpid1) + self.check_common(loaded) + self.check_copied(loaded, main_loaded) + + # At this point: + # * alive in 2 interpreters (main, interp1) + # * module def still in _PyRuntime.imports.extensions + # * mod init func ran again + # * m_copy was copied from interp1 + # * module's global state was updated, not reset + + # Use a subinterpreter while the previous one is still alive. + loaded = self.import_in_subinterp(interpid2) + self.check_common(loaded) + self.check_copied(loaded, main_loaded) + + # At this point: + # * alive in 3 interpreters (main, interp1, interp2) + # * module def still in _PyRuntime.imports.extensions + # * mod init func ran again + # * m_copy was copied from interp2 (was from interp1) + # * module's global state was updated, not reset + + @requires_subinterpreters + def test_basic_multiple_interpreters_deleted_no_reset(self): + # without resetting; already loaded in a deleted interpreter + + # At this point: + # * alive in 0 interpreters + # * module def may or may not be loaded already + # * module def not in _PyRuntime.imports.extensions + # * mod init func has not run yet (since reset, at least) + # * m_copy not set (hasn't been loaded yet or already cleared) + # * module's global state has not been initialized yet + # (or already cleared) + + interpid1 = self.add_subinterpreter() + interpid2 = self.add_subinterpreter() + + # First, load in the main interpreter but then completely clear it. + loaded_main = self.load(self.NAME) + loaded_main.module._clear_globals() + _testinternalcapi.clear_extension(self.NAME, self.FILE) + + # At this point: + # * alive in 0 interpreters + # * module def loaded already + # * module def was in _PyRuntime.imports.extensions, but cleared + # * mod init func ran for the first time (since reset, at least) + # * m_copy was set, but cleared (was NULL) + # * module's global state was initialized but cleared + + # Start with an interpreter that gets destroyed right away. + base = self.import_in_subinterp(postscript=''' + # Attrs set after loading are not in m_copy. + mod.spam = 'spam, spam, mash, spam, eggs, and spam' + ''') + self.check_common(base) + self.check_fresh(base) + + # At this point: + # * alive in 0 interpreters + # * module def in _PyRuntime.imports.extensions + # * mod init func ran again + # * m_copy is NULL (claered when the interpreter was destroyed) + # * module's global state was initialized, not reset + + # Use a subinterpreter that sticks around. + loaded_interp1 = self.import_in_subinterp(interpid1) + self.check_common(loaded_interp1) + self.check_semi_fresh(loaded_interp1, loaded_main, base) + + # At this point: + # * alive in 1 interpreter (interp1) + # * module def still in _PyRuntime.imports.extensions + # * mod init func ran again + # * m_copy was copied from interp1 (was NULL) + # * module's global state was updated, not reset + + # Use a subinterpreter while the previous one is still alive. + loaded_interp2 = self.import_in_subinterp(interpid2) + self.check_common(loaded_interp2) + self.check_copied(loaded_interp2, loaded_interp1) + + # At this point: + # * alive in 2 interpreters (interp1, interp2) + # * module def still in _PyRuntime.imports.extensions + # * mod init func ran again + # * m_copy was copied from interp2 (was from interp1) + # * module's global state was updated, not reset + + @requires_subinterpreters + @requires_load_dynamic + def test_basic_multiple_interpreters_reset_each(self): + # resetting between each interpreter + + # At this point: + # * alive in 0 interpreters + # * module def may or may not be loaded already + # * module def not in _PyRuntime.imports.extensions + # * mod init func has not run yet (since reset, at least) + # * m_copy not set (hasn't been loaded yet or already cleared) + # * module's global state has not been initialized yet + # (or already cleared) + + interpid1 = self.add_subinterpreter() + interpid2 = self.add_subinterpreter() + + # Use an interpreter that gets destroyed right away. + loaded = self.import_in_subinterp( + postscript=''' + # Attrs set after loading are not in m_copy. + mod.spam = 'spam, spam, mash, spam, eggs, and spam' + ''', + postcleanup=True, + ) + self.check_common(loaded) + self.check_fresh(loaded) + + # At this point: + # * alive in 0 interpreters + # * module def in _PyRuntime.imports.extensions + # * mod init func ran for the first time (since reset, at least) + # * m_copy is NULL (claered when the interpreter was destroyed) + # * module's global state was initialized, not reset + + # Use a subinterpreter that sticks around. + loaded = self.import_in_subinterp(interpid1, postcleanup=True) + self.check_common(loaded) + self.check_fresh(loaded) + + # At this point: + # * alive in 1 interpreter (interp1) + # * module def still in _PyRuntime.imports.extensions + # * mod init func ran again + # * m_copy was copied from interp1 (was NULL) + # * module's global state was initialized, not reset + + # Use a subinterpreter while the previous one is still alive. + loaded = self.import_in_subinterp(interpid2, postcleanup=True) + self.check_common(loaded) + self.check_fresh(loaded) + + # At this point: + # * alive in 2 interpreters (interp2, interp2) + # * module def still in _PyRuntime.imports.extensions + # * mod init func ran again + # * m_copy was copied from interp2 (was from interp1) + # * module's global state was initialized, not reset + + class ReloadTests(unittest.TestCase): """Very basic tests to make sure that imp.reload() operates just like diff --git a/Modules/_testsinglephase.c b/Modules/_testsinglephase.c index 565221c887e5ae..a16157702ae789 100644 --- a/Modules/_testsinglephase.c +++ b/Modules/_testsinglephase.c @@ -140,7 +140,7 @@ init_module(PyObject *module, module_state *state) if (initialized == NULL) { return -1; } - if (PyModule_AddObjectRef(module, "_initialized", initialized) != 0) { + if (PyModule_AddObjectRef(module, "_module_initialized", initialized) != 0) { return -1; } @@ -148,13 +148,13 @@ init_module(PyObject *module, module_state *state) } -PyDoc_STRVAR(common_initialized_doc, -"initialized()\n\ +PyDoc_STRVAR(common_state_initialized_doc, +"state_initialized()\n\ \n\ -Return the seconds-since-epoch when the module was initialized."); +Return the seconds-since-epoch when the module state was initialized."); static PyObject * -common_initialized(PyObject *self, PyObject *Py_UNUSED(ignored)) +common_state_initialized(PyObject *self, PyObject *Py_UNUSED(ignored)) { module_state *state = get_module_state(self); if (state == NULL) { @@ -164,9 +164,9 @@ common_initialized(PyObject *self, PyObject *Py_UNUSED(ignored)) return PyFloat_FromDouble(d); } -#define INITIALIZED_METHODDEF \ - {"initialized", common_initialized, METH_NOARGS, \ - common_initialized_doc} +#define STATE_INITIALIZED_METHODDEF \ + {"state_initialized", common_state_initialized, METH_NOARGS, \ + common_state_initialized_doc} PyDoc_STRVAR(common_look_up_self_doc, @@ -265,7 +265,7 @@ basic__clear_globals(PyObject *self, PyObject *Py_UNUSED(ignored)) static PyMethodDef TestMethods_Basic[] = { LOOK_UP_SELF_METHODDEF, SUM_METHODDEF, - INITIALIZED_METHODDEF, + STATE_INITIALIZED_METHODDEF, INITIALIZED_COUNT_METHODDEF, _CLEAR_GLOBALS_METHODDEF, {NULL, NULL} /* sentinel */ @@ -360,7 +360,7 @@ PyInit__testsinglephase_basic_copy(void) static PyMethodDef TestMethods_Reinit[] = { LOOK_UP_SELF_METHODDEF, SUM_METHODDEF, - INITIALIZED_METHODDEF, + STATE_INITIALIZED_METHODDEF, {NULL, NULL} /* sentinel */ }; @@ -421,7 +421,7 @@ PyInit__testsinglephase_with_reinit(void) static PyMethodDef TestMethods_WithState[] = { LOOK_UP_SELF_METHODDEF, SUM_METHODDEF, - INITIALIZED_METHODDEF, + STATE_INITIALIZED_METHODDEF, {NULL, NULL} /* sentinel */ }; diff --git a/Python/import.c b/Python/import.c index fabf03b1c5d698..57d4eea148810f 100644 --- a/Python/import.c +++ b/Python/import.c @@ -465,7 +465,7 @@ _modules_by_index_set(PyInterpreterState *interp, } static int -_modules_by_index_clear(PyInterpreterState *interp, PyModuleDef *def) +_modules_by_index_clear_one(PyInterpreterState *interp, PyModuleDef *def) { Py_ssize_t index = def->m_base.m_index; const char *err = _modules_by_index_check(interp, index); @@ -546,7 +546,7 @@ PyState_RemoveModule(PyModuleDef* def) "PyState_RemoveModule called on module with slots"); return -1; } - return _modules_by_index_clear(tstate->interp, def); + return _modules_by_index_clear_one(tstate->interp, def); } @@ -584,6 +584,109 @@ _PyImport_ClearModulesByIndex(PyInterpreterState *interp) /* extension modules */ /*********************/ +/* + It may help to have a big picture view of what happens + when an extension is loaded. This includes when it is imported + for the first time or via imp.load_dynamic(). + + Here's a summary, using imp.load_dynamic() as the starting point: + + 1. imp.load_dynamic() -> importlib._bootstrap._load() + 2. _load(): acquire import lock + 3. _load() -> importlib._bootstrap._load_unlocked() + 4. _load_unlocked() -> importlib._bootstrap.module_from_spec() + 5. module_from_spec() -> ExtensionFileLoader.create_module() + 6. create_module() -> _imp.create_dynamic() + (see below) + 7. module_from_spec() -> importlib._bootstrap._init_module_attrs() + 8. _load_unlocked(): sys.modules[name] = module + 9. _load_unlocked() -> ExtensionFileLoader.exec_module() + 10. exec_module() -> _imp.exec_dynamic() + (see below) + 11. _load(): release import lock + + + ...for single-phase init modules, where m_size == -1: + + (6). first time (not found in _PyRuntime.imports.extensions): + 1. _imp_create_dynamic_impl() -> import_find_extension() + 2. _imp_create_dynamic_impl() -> _PyImport_LoadDynamicModuleWithSpec() + 3. _PyImport_LoadDynamicModuleWithSpec(): load + 4. _PyImport_LoadDynamicModuleWithSpec(): call + 5. -> PyModule_Create() -> PyModule_Create2() -> PyModule_CreateInitialized() + 6. PyModule_CreateInitialized() -> PyModule_New() + 7. PyModule_CreateInitialized(): allocate mod->md_state + 8. PyModule_CreateInitialized() -> PyModule_AddFunctions() + 9. PyModule_CreateInitialized() -> PyModule_SetDocString() + 10. PyModule_CreateInitialized(): set mod->md_def + 11. : initialize the module + 12. _PyImport_LoadDynamicModuleWithSpec() -> _PyImport_CheckSubinterpIncompatibleExtensionAllowed() + 13. _PyImport_LoadDynamicModuleWithSpec(): set def->m_base.m_init + 14. _PyImport_LoadDynamicModuleWithSpec(): set __file__ + 15. _PyImport_LoadDynamicModuleWithSpec() -> _PyImport_FixupExtensionObject() + 16. _PyImport_FixupExtensionObject(): add it to interp->imports.modules_by_index + 17. _PyImport_FixupExtensionObject(): copy __dict__ into def->m_base.m_copy + 18. _PyImport_FixupExtensionObject(): add it to _PyRuntime.imports.extensions + + (6). subsequent times (found in _PyRuntime.imports.extensions): + 1. _imp_create_dynamic_impl() -> import_find_extension() + 2. import_find_extension() -> import_add_module() + 3. if name in sys.modules: use that module + 4. else: + 1. import_add_module() -> PyModule_NewObject() + 2. import_add_module(): set it on sys.modules + 5. import_find_extension(): copy the "m_copy" dict into __dict__ + 6. _imp_create_dynamic_impl() -> _PyImport_CheckSubinterpIncompatibleExtensionAllowed() + + (10). (every time): + 1. noop + + + ...for single-phase init modules, where m_size >= 0: + + (6). not main interpreter and never loaded there - every time (not found in _PyRuntime.imports.extensions): + 1-16. (same as for m_size == -1) + + (6). main interpreter - first time (not found in _PyRuntime.imports.extensions): + 1-16. (same as for m_size == -1) + 17. _PyImport_FixupExtensionObject(): add it to _PyRuntime.imports.extensions + + (6). previously loaded in main interpreter (found in _PyRuntime.imports.extensions): + 1. _imp_create_dynamic_impl() -> import_find_extension() + 2. import_find_extension(): call def->m_base.m_init + 3. import_find_extension(): add the module to sys.modules + + (10). every time: + 1. noop + + + ...for multi-phase init modules: + + (6). every time: + 1. _imp_create_dynamic_impl() -> import_find_extension() (not found) + 2. _imp_create_dynamic_impl() -> _PyImport_LoadDynamicModuleWithSpec() + 3. _PyImport_LoadDynamicModuleWithSpec(): load module init func + 4. _PyImport_LoadDynamicModuleWithSpec(): call module init func + 5. _PyImport_LoadDynamicModuleWithSpec() -> PyModule_FromDefAndSpec() + 6. PyModule_FromDefAndSpec(): gather/check moduledef slots + 7. if there's a Py_mod_create slot: + 1. PyModule_FromDefAndSpec(): call its function + 8. else: + 1. PyModule_FromDefAndSpec() -> PyModule_NewObject() + 9: PyModule_FromDefAndSpec(): set mod->md_def + 10. PyModule_FromDefAndSpec() -> _add_methods_to_object() + 11. PyModule_FromDefAndSpec() -> PyModule_SetDocString() + + (10). every time: + 1. _imp_exec_dynamic_impl() -> exec_builtin_or_dynamic() + 2. if mod->md_state == NULL (including if m_size == 0): + 1. exec_builtin_or_dynamic() -> PyModule_ExecDef() + 2. PyModule_ExecDef(): allocate mod->md_state + 3. if there's a Py_mod_exec slot: + 1. PyModule_ExecDef(): call its function + */ + + /* Make sure name is fully qualified. This is a bit of a hack: when the shared library is loaded, @@ -1007,13 +1110,17 @@ clear_singlephase_extension(PyInterpreterState *interp, /* Clear the PyState_*Module() cache entry. */ if (_modules_by_index_check(interp, def->m_base.m_index) == NULL) { - if (_modules_by_index_clear(interp, def) < 0) { + if (_modules_by_index_clear_one(interp, def) < 0) { return -1; } } /* Clear the cached module def. */ - return _extensions_cache_delete(filename, name); + if (_extensions_cache_delete(filename, name) < 0) { + return -1; + } + + return 0; } From 4f3786b7616dd464242b88ad6914053d409fe9d2 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Mon, 27 Feb 2023 21:53:22 +0300 Subject: [PATCH 203/247] gh-101773: Optimize creation of Fractions in private methods (#101780) This PR adds a private `Fraction._from_coprime_ints` classmethod for internal creations of `Fraction` objects, replacing the use of `_normalize=False` in the existing constructor. This speeds up creation of `Fraction` objects arising from calculations. The `_normalize` argument to the `Fraction` constructor has been removed. Co-authored-by: Pieter Eendebak Co-authored-by: Mark Dickinson --- Lib/fractions.py | 79 +++++++++++-------- Lib/test/test_fractions.py | 1 + Lib/test/test_numeric_tower.py | 2 +- ...-02-10-11-59-13.gh-issue-101773.J_kI7y.rst | 2 + 4 files changed, 50 insertions(+), 34 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-02-10-11-59-13.gh-issue-101773.J_kI7y.rst diff --git a/Lib/fractions.py b/Lib/fractions.py index 49a3f2841a2ed4..f718b35639beee 100644 --- a/Lib/fractions.py +++ b/Lib/fractions.py @@ -183,7 +183,7 @@ class Fraction(numbers.Rational): __slots__ = ('_numerator', '_denominator') # We're immutable, so use __new__ not __init__ - def __new__(cls, numerator=0, denominator=None, *, _normalize=True): + def __new__(cls, numerator=0, denominator=None): """Constructs a Rational. Takes a string like '3/2' or '1.5', another Rational instance, a @@ -279,12 +279,11 @@ def __new__(cls, numerator=0, denominator=None, *, _normalize=True): if denominator == 0: raise ZeroDivisionError('Fraction(%s, 0)' % numerator) - if _normalize: - g = math.gcd(numerator, denominator) - if denominator < 0: - g = -g - numerator //= g - denominator //= g + g = math.gcd(numerator, denominator) + if denominator < 0: + g = -g + numerator //= g + denominator //= g self._numerator = numerator self._denominator = denominator return self @@ -301,7 +300,7 @@ def from_float(cls, f): elif not isinstance(f, float): raise TypeError("%s.from_float() only takes floats, not %r (%s)" % (cls.__name__, f, type(f).__name__)) - return cls(*f.as_integer_ratio()) + return cls._from_coprime_ints(*f.as_integer_ratio()) @classmethod def from_decimal(cls, dec): @@ -313,7 +312,19 @@ def from_decimal(cls, dec): raise TypeError( "%s.from_decimal() only takes Decimals, not %r (%s)" % (cls.__name__, dec, type(dec).__name__)) - return cls(*dec.as_integer_ratio()) + return cls._from_coprime_ints(*dec.as_integer_ratio()) + + @classmethod + def _from_coprime_ints(cls, numerator, denominator, /): + """Convert a pair of ints to a rational number, for internal use. + + The ratio of integers should be in lowest terms and the denominator + should be positive. + """ + obj = super(Fraction, cls).__new__(cls) + obj._numerator = numerator + obj._denominator = denominator + return obj def is_integer(self): """Return True if the Fraction is an integer.""" @@ -380,9 +391,9 @@ def limit_denominator(self, max_denominator=1000000): # the distance from p1/q1 to self is d/(q1*self._denominator). So we # need to compare 2*(q0+k*q1) with self._denominator/d. if 2*d*(q0+k*q1) <= self._denominator: - return Fraction(p1, q1, _normalize=False) + return Fraction._from_coprime_ints(p1, q1) else: - return Fraction(p0+k*p1, q0+k*q1, _normalize=False) + return Fraction._from_coprime_ints(p0+k*p1, q0+k*q1) @property def numerator(a): @@ -703,13 +714,13 @@ def _add(a, b): nb, db = b._numerator, b._denominator g = math.gcd(da, db) if g == 1: - return Fraction(na * db + da * nb, da * db, _normalize=False) + return Fraction._from_coprime_ints(na * db + da * nb, da * db) s = da // g t = na * (db // g) + nb * s g2 = math.gcd(t, g) if g2 == 1: - return Fraction(t, s * db, _normalize=False) - return Fraction(t // g2, s * (db // g2), _normalize=False) + return Fraction._from_coprime_ints(t, s * db) + return Fraction._from_coprime_ints(t // g2, s * (db // g2)) __add__, __radd__ = _operator_fallbacks(_add, operator.add) @@ -719,13 +730,13 @@ def _sub(a, b): nb, db = b._numerator, b._denominator g = math.gcd(da, db) if g == 1: - return Fraction(na * db - da * nb, da * db, _normalize=False) + return Fraction._from_coprime_ints(na * db - da * nb, da * db) s = da // g t = na * (db // g) - nb * s g2 = math.gcd(t, g) if g2 == 1: - return Fraction(t, s * db, _normalize=False) - return Fraction(t // g2, s * (db // g2), _normalize=False) + return Fraction._from_coprime_ints(t, s * db) + return Fraction._from_coprime_ints(t // g2, s * (db // g2)) __sub__, __rsub__ = _operator_fallbacks(_sub, operator.sub) @@ -741,15 +752,17 @@ def _mul(a, b): if g2 > 1: nb //= g2 da //= g2 - return Fraction(na * nb, db * da, _normalize=False) + return Fraction._from_coprime_ints(na * nb, db * da) __mul__, __rmul__ = _operator_fallbacks(_mul, operator.mul) def _div(a, b): """a / b""" # Same as _mul(), with inversed b. - na, da = a._numerator, a._denominator nb, db = b._numerator, b._denominator + if nb == 0: + raise ZeroDivisionError('Fraction(%s, 0)' % db) + na, da = a._numerator, a._denominator g1 = math.gcd(na, nb) if g1 > 1: na //= g1 @@ -761,7 +774,7 @@ def _div(a, b): n, d = na * db, nb * da if d < 0: n, d = -n, -d - return Fraction(n, d, _normalize=False) + return Fraction._from_coprime_ints(n, d) __truediv__, __rtruediv__ = _operator_fallbacks(_div, operator.truediv) @@ -798,17 +811,17 @@ def __pow__(a, b): if b.denominator == 1: power = b.numerator if power >= 0: - return Fraction(a._numerator ** power, - a._denominator ** power, - _normalize=False) - elif a._numerator >= 0: - return Fraction(a._denominator ** -power, - a._numerator ** -power, - _normalize=False) + return Fraction._from_coprime_ints(a._numerator ** power, + a._denominator ** power) + elif a._numerator > 0: + return Fraction._from_coprime_ints(a._denominator ** -power, + a._numerator ** -power) + elif a._numerator == 0: + raise ZeroDivisionError('Fraction(%s, 0)' % + a._denominator ** -power) else: - return Fraction((-a._denominator) ** -power, - (-a._numerator) ** -power, - _normalize=False) + return Fraction._from_coprime_ints((-a._denominator) ** -power, + (-a._numerator) ** -power) else: # A fractional power will generally produce an # irrational number. @@ -832,15 +845,15 @@ def __rpow__(b, a): def __pos__(a): """+a: Coerces a subclass instance to Fraction""" - return Fraction(a._numerator, a._denominator, _normalize=False) + return Fraction._from_coprime_ints(a._numerator, a._denominator) def __neg__(a): """-a""" - return Fraction(-a._numerator, a._denominator, _normalize=False) + return Fraction._from_coprime_ints(-a._numerator, a._denominator) def __abs__(a): """abs(a)""" - return Fraction(abs(a._numerator), a._denominator, _normalize=False) + return Fraction._from_coprime_ints(abs(a._numerator), a._denominator) def __int__(a, _index=operator.index): """int(a)""" diff --git a/Lib/test/test_fractions.py b/Lib/test/test_fractions.py index 3bc6b409e05dc3..e112f49d2e7944 100644 --- a/Lib/test/test_fractions.py +++ b/Lib/test/test_fractions.py @@ -488,6 +488,7 @@ def testArithmetic(self): self.assertEqual(F(5, 6), F(2, 3) * F(5, 4)) self.assertEqual(F(1, 4), F(1, 10) / F(2, 5)) self.assertEqual(F(-15, 8), F(3, 4) / F(-2, 5)) + self.assertRaises(ZeroDivisionError, operator.truediv, F(1), F(0)) self.assertTypedEquals(2, F(9, 10) // F(2, 5)) self.assertTypedEquals(10**23, F(10**23, 1) // F(1)) self.assertEqual(F(5, 6), F(7, 3) % F(3, 2)) diff --git a/Lib/test/test_numeric_tower.py b/Lib/test/test_numeric_tower.py index 9cd85e13634c2b..337682d6bac96c 100644 --- a/Lib/test/test_numeric_tower.py +++ b/Lib/test/test_numeric_tower.py @@ -145,7 +145,7 @@ def test_fractions(self): # The numbers ABC doesn't enforce that the "true" division # of integers produces a float. This tests that the # Rational.__float__() method has required type conversions. - x = F(DummyIntegral(1), DummyIntegral(2), _normalize=False) + x = F._from_coprime_ints(DummyIntegral(1), DummyIntegral(2)) self.assertRaises(TypeError, lambda: x.numerator/x.denominator) self.assertEqual(float(x), 0.5) diff --git a/Misc/NEWS.d/next/Library/2023-02-10-11-59-13.gh-issue-101773.J_kI7y.rst b/Misc/NEWS.d/next/Library/2023-02-10-11-59-13.gh-issue-101773.J_kI7y.rst new file mode 100644 index 00000000000000..b577d93d28c2ae --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-10-11-59-13.gh-issue-101773.J_kI7y.rst @@ -0,0 +1,2 @@ +Optimize :class:`fractions.Fraction` for small components. The private argument +``_normalize`` of the :class:`fractions.Fraction` constructor has been removed. From 4624987b296108c2dc1e6e3a24e65d2de7afd451 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Mon, 27 Feb 2023 22:11:28 +0300 Subject: [PATCH 204/247] gh-101825: Clarify that as_integer_ratio() output is always normalized (#101843) Make docstrings for `as_integer_ratio` consistent across types, and document that the returned pair is always normalized (coprime integers, with positive denominator). --------- Co-authored-by: Owain Davies <116417456+OTheDev@users.noreply.github.com> Co-authored-by: Mark Dickinson --- Doc/library/fractions.rst | 3 ++- Doc/library/stdtypes.rst | 6 +++--- Lib/fractions.py | 5 ++--- Objects/clinic/floatobject.c.h | 10 ++++------ Objects/clinic/longobject.c.h | 7 +++---- Objects/floatobject.c | 10 ++++------ Objects/longobject.c | 7 +++---- 7 files changed, 21 insertions(+), 27 deletions(-) diff --git a/Doc/library/fractions.rst b/Doc/library/fractions.rst index c61bbac892e0e4..fe2e8ab655edf8 100644 --- a/Doc/library/fractions.rst +++ b/Doc/library/fractions.rst @@ -118,7 +118,8 @@ another rational number, or from a string. .. method:: as_integer_ratio() Return a tuple of two integers, whose ratio is equal - to the Fraction and with a positive denominator. + to the original Fraction. The ratio is in lowest terms + and has a positive denominator. .. versionadded:: 3.8 diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 41947d66c15134..1240f80b0f11f0 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -602,8 +602,8 @@ class`. In addition, it provides a few more methods: .. method:: int.as_integer_ratio() - Return a pair of integers whose ratio is exactly equal to the original - integer and with a positive denominator. The integer ratio of integers + Return a pair of integers whose ratio is equal to the original + integer and has a positive denominator. The integer ratio of integers (whole numbers) is always the integer as the numerator and ``1`` as the denominator. @@ -624,7 +624,7 @@ class`. float also has the following additional methods. .. method:: float.as_integer_ratio() Return a pair of integers whose ratio is exactly equal to the - original float and with a positive denominator. Raises + original float. The ratio is in lowest terms and has a positive denominator. Raises :exc:`OverflowError` on infinities and a :exc:`ValueError` on NaNs. diff --git a/Lib/fractions.py b/Lib/fractions.py index f718b35639beee..c95db0730e5b6d 100644 --- a/Lib/fractions.py +++ b/Lib/fractions.py @@ -331,10 +331,9 @@ def is_integer(self): return self._denominator == 1 def as_integer_ratio(self): - """Return the integer ratio as a tuple. + """Return a pair of integers, whose ratio is equal to the original Fraction. - Return a tuple of two integers, whose ratio is equal to the - Fraction and with a positive denominator. + The ratio is in lowest terms and has a positive denominator. """ return (self._numerator, self._denominator) diff --git a/Objects/clinic/floatobject.c.h b/Objects/clinic/floatobject.c.h index 6bc25a0a409f97..a99fd74e4b621b 100644 --- a/Objects/clinic/floatobject.c.h +++ b/Objects/clinic/floatobject.c.h @@ -173,12 +173,10 @@ PyDoc_STRVAR(float_as_integer_ratio__doc__, "as_integer_ratio($self, /)\n" "--\n" "\n" -"Return integer ratio.\n" +"Return a pair of integers, whose ratio is exactly equal to the original float.\n" "\n" -"Return a pair of integers, whose ratio is exactly equal to the original float\n" -"and with a positive denominator.\n" -"\n" -"Raise OverflowError on infinities and a ValueError on NaNs.\n" +"The ratio is in lowest terms and has a positive denominator. Raise\n" +"OverflowError on infinities and a ValueError on NaNs.\n" "\n" ">>> (10.0).as_integer_ratio()\n" "(10, 1)\n" @@ -327,4 +325,4 @@ float___format__(PyObject *self, PyObject *arg) exit: return return_value; } -/*[clinic end generated code: output=74bc91bb44014df9 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=ea329577074911b9 input=a9049054013a1b77]*/ diff --git a/Objects/clinic/longobject.c.h b/Objects/clinic/longobject.c.h index 206bffdd086a5c..c26ceafbc2be0d 100644 --- a/Objects/clinic/longobject.c.h +++ b/Objects/clinic/longobject.c.h @@ -231,10 +231,9 @@ PyDoc_STRVAR(int_as_integer_ratio__doc__, "as_integer_ratio($self, /)\n" "--\n" "\n" -"Return integer ratio.\n" +"Return a pair of integers, whose ratio is equal to the original int.\n" "\n" -"Return a pair of integers, whose ratio is exactly equal to the original int\n" -"and with a positive denominator.\n" +"The ratio is in lowest terms and has a positive denominator.\n" "\n" ">>> (10).as_integer_ratio()\n" "(10, 1)\n" @@ -485,4 +484,4 @@ int_is_integer(PyObject *self, PyObject *Py_UNUSED(ignored)) { return int_is_integer_impl(self); } -/*[clinic end generated code: output=e518fe2b5d519322 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=cfdf35d916158d4f input=a9049054013a1b77]*/ diff --git a/Objects/floatobject.c b/Objects/floatobject.c index 912b742f797d24..d641311f1126cd 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -1546,12 +1546,10 @@ float_fromhex(PyTypeObject *type, PyObject *string) /*[clinic input] float.as_integer_ratio -Return integer ratio. +Return a pair of integers, whose ratio is exactly equal to the original float. -Return a pair of integers, whose ratio is exactly equal to the original float -and with a positive denominator. - -Raise OverflowError on infinities and a ValueError on NaNs. +The ratio is in lowest terms and has a positive denominator. Raise +OverflowError on infinities and a ValueError on NaNs. >>> (10.0).as_integer_ratio() (10, 1) @@ -1563,7 +1561,7 @@ Raise OverflowError on infinities and a ValueError on NaNs. static PyObject * float_as_integer_ratio_impl(PyObject *self) -/*[clinic end generated code: output=65f25f0d8d30a712 input=e21d08b4630c2e44]*/ +/*[clinic end generated code: output=65f25f0d8d30a712 input=d5ba7765655d75bd]*/ { double self_double; double float_part; diff --git a/Objects/longobject.c b/Objects/longobject.c index 8293f133bed213..51655cd0bad9ec 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -6020,10 +6020,9 @@ int_bit_count_impl(PyObject *self) /*[clinic input] int.as_integer_ratio -Return integer ratio. +Return a pair of integers, whose ratio is equal to the original int. -Return a pair of integers, whose ratio is exactly equal to the original int -and with a positive denominator. +The ratio is in lowest terms and has a positive denominator. >>> (10).as_integer_ratio() (10, 1) @@ -6035,7 +6034,7 @@ and with a positive denominator. static PyObject * int_as_integer_ratio_impl(PyObject *self) -/*[clinic end generated code: output=e60803ae1cc8621a input=55ce3058e15de393]*/ +/*[clinic end generated code: output=e60803ae1cc8621a input=384ff1766634bec2]*/ { PyObject *ratio_tuple; PyObject *numerator = long_long(self); From 0f89acf6cc4d4790f7b7a82165d0a6e7e84e4b72 Mon Sep 17 00:00:00 2001 From: Steven Troxler Date: Mon, 27 Feb 2023 13:16:11 -0800 Subject: [PATCH 205/247] gh-101561: Add typing.override decorator (#101564) Co-authored-by: Jelle Zijlstra Co-authored-by: Alex Waygood --- Doc/library/typing.rst | 38 +++++++++++++++++ Doc/whatsnew/3.12.rst | 8 ++++ Lib/test/test_typing.py | 38 +++++++++++++++++ Lib/typing.py | 41 +++++++++++++++++++ Misc/ACKS | 1 + ...-02-04-16-35-46.gh-issue-101561.Xo6pIZ.rst | 1 + 6 files changed, 127 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2023-02-04-16-35-46.gh-issue-101561.Xo6pIZ.rst diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index bbbf6920ddec88..3395e4bfb95c44 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -91,6 +91,8 @@ annotations. These include: *Introducing* :data:`LiteralString` * :pep:`681`: Data Class Transforms *Introducing* the :func:`@dataclass_transform` decorator +* :pep:`698`: Adding an override decorator to typing + *Introducing* the :func:`@override` decorator .. _type-aliases: @@ -2722,6 +2724,42 @@ Functions and decorators This wraps the decorator with something that wraps the decorated function in :func:`no_type_check`. + +.. decorator:: override + + A decorator for methods that indicates to type checkers that this method + should override a method or attribute with the same name on a base class. + This helps prevent bugs that may occur when a base class is changed without + an equivalent change to a child class. + + For example:: + + class Base: + def log_status(self) + + class Sub(Base): + @override + def log_status(self) -> None: # Okay: overrides Base.log_status + ... + + @override + def done(self) -> None: # Error reported by type checker + ... + + There is no runtime checking of this property. + + The decorator will set the ``__override__`` attribute to ``True`` on + the decorated object. Thus, a check like + ``if getattr(obj, "__override__", False)`` can be used at runtime to determine + whether an object ``obj`` has been marked as an override. If the decorated object + does not support setting attributes, the decorator returns the object unchanged + without raising an exception. + + See :pep:`698` for more details. + + .. versionadded:: 3.12 + + .. decorator:: type_check_only Decorator to mark a class or function to be unavailable at runtime. diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index e551c5b4fd06a9..1a25ec6b70613b 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -350,6 +350,14 @@ tempfile The :class:`tempfile.NamedTemporaryFile` function has a new optional parameter *delete_on_close* (Contributed by Evgeny Zorin in :gh:`58451`.) +typing +------ + +* Add :func:`typing.override`, an override decorator telling to static type + checkers to verify that a method overrides some method or attribute of the + same name on a base class, as per :pep:`698`. (Contributed by Steven Troxler in + :gh:`101564`.) + sys --- diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 7a460d94469fe7..d61dc6e2fbd70b 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -23,6 +23,7 @@ from typing import assert_type, cast, runtime_checkable from typing import get_type_hints from typing import get_origin, get_args +from typing import override from typing import is_typeddict from typing import reveal_type from typing import dataclass_transform @@ -4166,6 +4167,43 @@ def cached(self): ... self.assertIs(True, Methods.cached.__final__) +class OverrideDecoratorTests(BaseTestCase): + def test_override(self): + class Base: + def normal_method(self): ... + @staticmethod + def static_method_good_order(): ... + @staticmethod + def static_method_bad_order(): ... + @staticmethod + def decorator_with_slots(): ... + + class Derived(Base): + @override + def normal_method(self): + return 42 + + @staticmethod + @override + def static_method_good_order(): + return 42 + + @override + @staticmethod + def static_method_bad_order(): + return 42 + + + self.assertIsSubclass(Derived, Base) + instance = Derived() + self.assertEqual(instance.normal_method(), 42) + self.assertIs(True, instance.normal_method.__override__) + self.assertEqual(Derived.static_method_good_order(), 42) + self.assertIs(True, Derived.static_method_good_order.__override__) + self.assertEqual(Derived.static_method_bad_order(), 42) + self.assertIs(False, hasattr(Derived.static_method_bad_order, "__override__")) + + class CastTests(BaseTestCase): def test_basics(self): diff --git a/Lib/typing.py b/Lib/typing.py index bdf51bb5f41595..8d40e923bb1d08 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -138,6 +138,7 @@ def _idfunc(_, x): 'NoReturn', 'NotRequired', 'overload', + 'override', 'ParamSpecArgs', 'ParamSpecKwargs', 'Required', @@ -2657,6 +2658,7 @@ class Other(Leaf): # Error reported by type checker # Internal type variable used for Type[]. CT_co = TypeVar('CT_co', covariant=True, bound=type) + # A useful type variable with constraints. This represents string types. # (This one *is* for export!) AnyStr = TypeVar('AnyStr', bytes, str) @@ -2748,6 +2750,8 @@ def new_user(user_class: Type[U]) -> U: At this point the type checker knows that joe has type BasicUser. """ +# Internal type variable for callables. Not for export. +F = TypeVar("F", bound=Callable[..., Any]) @runtime_checkable class SupportsInt(Protocol): @@ -3448,3 +3452,40 @@ def decorator(cls_or_fn): } return cls_or_fn return decorator + + + +def override(method: F, /) -> F: + """Indicate that a method is intended to override a method in a base class. + + Usage: + + class Base: + def method(self) -> None: ... + pass + + class Child(Base): + @override + def method(self) -> None: + super().method() + + When this decorator is applied to a method, the type checker will + validate that it overrides a method or attribute with the same name on a + base class. This helps prevent bugs that may occur when a base class is + changed without an equivalent change to a child class. + + There is no runtime checking of this property. The decorator sets the + ``__override__`` attribute to ``True`` on the decorated object to allow + runtime introspection. + + See PEP 698 for details. + + """ + try: + method.__override__ = True + except (AttributeError, TypeError): + # Skip the attribute silently if it is not writable. + # AttributeError happens if the object has __slots__ or a + # read-only property, TypeError if it's a builtin class. + pass + return method diff --git a/Misc/ACKS b/Misc/ACKS index 3403aee4cc78ff..2da3d0ab29b81d 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1848,6 +1848,7 @@ Tom Tromey John Tromp Diane Trout Jason Trowbridge +Steven Troxler Brent Tubbs Anthony Tuininga Erno Tukia diff --git a/Misc/NEWS.d/next/Library/2023-02-04-16-35-46.gh-issue-101561.Xo6pIZ.rst b/Misc/NEWS.d/next/Library/2023-02-04-16-35-46.gh-issue-101561.Xo6pIZ.rst new file mode 100644 index 00000000000000..2f6a4153062e5a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-04-16-35-46.gh-issue-101561.Xo6pIZ.rst @@ -0,0 +1 @@ +Add a new decorator :func:`typing.override`. See :pep:`698` for details. Patch by Steven Troxler. From c41af812c948ec3cb07b2ef7a46a238f5cab3dc2 Mon Sep 17 00:00:00 2001 From: JosephSBoyle <48555120+JosephSBoyle@users.noreply.github.com> Date: Tue, 28 Feb 2023 06:11:52 +0000 Subject: [PATCH 206/247] IDLE: Simplify DynOptionsMenu __init__code (#101371) Refactor DynOptionMenu's initializer to not copy kwargs dict and use subscripting; improve its htest. Co-authored-by: Terry Jan Reedy --- Lib/idlelib/dynoption.py | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/Lib/idlelib/dynoption.py b/Lib/idlelib/dynoption.py index 9c6ffa435a1089..d5dfc3eda13f60 100644 --- a/Lib/idlelib/dynoption.py +++ b/Lib/idlelib/dynoption.py @@ -2,24 +2,19 @@ OptionMenu widget modified to allow dynamic menu reconfiguration and setting of highlightthickness """ -import copy - from tkinter import OptionMenu, _setit, StringVar, Button class DynOptionMenu(OptionMenu): - """ - unlike OptionMenu, our kwargs can include highlightthickness + """Add SetMenu and highlightthickness to OptionMenu. + + Highlightthickness adds space around menu button. """ def __init__(self, master, variable, value, *values, **kwargs): - # TODO copy value instead of whole dict - kwargsCopy=copy.copy(kwargs) - if 'highlightthickness' in list(kwargs.keys()): - del(kwargs['highlightthickness']) + highlightthickness = kwargs.pop('highlightthickness', None) OptionMenu.__init__(self, master, variable, value, *values, **kwargs) - self.config(highlightthickness=kwargsCopy.get('highlightthickness')) - #self.menu=self['menu'] - self.variable=variable - self.command=kwargs.get('command') + self['highlightthickness'] = highlightthickness + self.variable = variable + self.command = kwargs.get('command') def SetMenu(self,valueList,value=None): """ @@ -38,14 +33,15 @@ def _dyn_option_menu(parent): # htest # from tkinter import Toplevel # + StringVar, Button top = Toplevel(parent) - top.title("Tets dynamic option menu") + top.title("Test dynamic option menu") x, y = map(int, parent.geometry().split('+')[1:]) top.geometry("200x100+%d+%d" % (x + 250, y + 175)) top.focus_set() var = StringVar(top) var.set("Old option set") #Set the default value - dyn = DynOptionMenu(top,var, "old1","old2","old3","old4") + dyn = DynOptionMenu(top, var, "old1","old2","old3","old4", + highlightthickness=5) dyn.pack() def update(): @@ -54,5 +50,6 @@ def update(): button.pack() if __name__ == '__main__': + # Only module without unittests because of intention to replace. from idlelib.idle_test.htest import run run(_dyn_option_menu) From 6b2d7c0ddb4d2afe298ee7c8f6a23c400f1465fd Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 28 Feb 2023 09:31:01 +0100 Subject: [PATCH 207/247] gh-101101: Unstable C API tier (PEP 689) (GH-101102) --- Doc/c-api/code.rst | 98 ++++++++++++++- Doc/c-api/stable.rst | 36 +++++- Doc/tools/extensions/c_annotations.py | 16 +++ Doc/whatsnew/3.12.rst | 23 ++++ Include/README.rst | 6 +- Include/cpython/ceval.h | 7 +- Include/cpython/code.h | 47 +++++-- Include/pyport.h | 9 ++ Lib/test/test_code.py | 6 +- ...2-04-21-17-25-22.gh-issue-91744.FgvaMi.rst | 3 + Modules/Setup.stdlib.in | 2 +- Modules/_testcapi/code.c | 115 ++++++++++++++++++ Modules/_testcapi/parts.h | 1 + Modules/_testcapimodule.c | 3 + Objects/codeobject.c | 9 +- PCbuild/_testcapi.vcxproj | 1 + PCbuild/_testcapi.vcxproj.filters | 3 + Python/ceval.c | 2 +- 18 files changed, 358 insertions(+), 29 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2022-04-21-17-25-22.gh-issue-91744.FgvaMi.rst create mode 100644 Modules/_testcapi/code.c diff --git a/Doc/c-api/code.rst b/Doc/c-api/code.rst index ae75d68901d7ba..062ef3a1fea93c 100644 --- a/Doc/c-api/code.rst +++ b/Doc/c-api/code.rst @@ -33,28 +33,47 @@ 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* PyUnstable_Code_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) 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 - will bind you to a precise Python version since the definition of the bytecode - changes often. The many arguments of this function are inter-dependent in complex + use :c:func:`PyCode_NewEmpty` instead. + + Since the definition of the bytecode changes often, calling + :c:func:`PyCode_New` directly can bind you to a precise Python version. + + The many arguments of this function are inter-dependent in complex ways, meaning that subtle changes to values are likely to result in incorrect execution or VM crashes. Use this function only with extreme care. .. versionchanged:: 3.11 Added ``exceptiontable`` parameter. -.. 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) + .. index:: single: PyCode_New + + .. versionchanged:: 3.12 + + Renamed from ``PyCode_New`` as part of :ref:`unstable-c-api`. + The old name is deprecated, but will remain available until the + signature changes again. + +.. c:function:: PyCodeObject* PyUnstable_Code_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) 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. - .. versionadded:: 3.8 + .. index:: single: PyCode_NewWithPosOnlyArgs + + .. versionadded:: 3.8 as ``PyCode_NewWithPosOnlyArgs`` .. versionchanged:: 3.11 Added ``exceptiontable`` parameter. + .. versionchanged:: 3.12 + + Renamed to ``PyUnstable_Code_NewWithPosOnlyArgs``. + The old name is deprecated, but will remain available until the + signature changes again. + .. c:function:: PyCodeObject* PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) Return a new empty code object with the specified filename, @@ -165,3 +184,70 @@ bound into a function. :c:func:`PyErr_WriteUnraisable`. Otherwise it should return ``0``. .. versionadded:: 3.12 + + +Extra information +----------------- + +To support low-level extensions to frame evaluation, such as external +just-in-time compilers, it is possible to attach arbitrary extra data to +code objects. + +These functions are part of the unstable C API tier: +this functionality is a CPython implementation detail, and the API +may change without deprecation warnings. + +.. c:function:: Py_ssize_t PyUnstable_Eval_RequestCodeExtraIndex(freefunc free) + + Return a new an opaque index value used to adding data to code objects. + + You generally call this function once (per interpreter) and use the result + with ``PyCode_GetExtra`` and ``PyCode_SetExtra`` to manipulate + data on individual code objects. + + If *free* is not ``NULL``: when a code object is deallocated, + *free* will be called on non-``NULL`` data stored under the new index. + Use :c:func:`Py_DecRef` when storing :c:type:`PyObject`. + + .. index:: single: _PyEval_RequestCodeExtraIndex + + .. versionadded:: 3.6 as ``_PyEval_RequestCodeExtraIndex`` + + .. versionchanged:: 3.12 + + Renamed to ``PyUnstable_Eval_RequestCodeExtraIndex``. + The old private name is deprecated, but will be available until the API + changes. + +.. c:function:: int PyUnstable_Code_GetExtra(PyObject *code, Py_ssize_t index, void **extra) + + Set *extra* to the extra data stored under the given index. + Return 0 on success. Set an exception and return -1 on failure. + + If no data was set under the index, set *extra* to ``NULL`` and return + 0 without setting an exception. + + .. index:: single: _PyCode_GetExtra + + .. versionadded:: 3.6 as ``_PyCode_GetExtra`` + + .. versionchanged:: 3.12 + + Renamed to ``PyUnstable_Code_GetExtra``. + The old private name is deprecated, but will be available until the API + changes. + +.. c:function:: int PyUnstable_Code_SetExtra(PyObject *code, Py_ssize_t index, void *extra) + + Set the extra data stored under the given index to *extra*. + Return 0 on success. Set an exception and return -1 on failure. + + .. index:: single: _PyCode_SetExtra + + .. versionadded:: 3.6 as ``_PyCode_SetExtra`` + + .. versionchanged:: 3.12 + + Renamed to ``PyUnstable_Code_SetExtra``. + The old private name is deprecated, but will be available until the API + changes. diff --git a/Doc/c-api/stable.rst b/Doc/c-api/stable.rst index 4ae20e93e36785..3721fc0697f5cd 100644 --- a/Doc/c-api/stable.rst +++ b/Doc/c-api/stable.rst @@ -6,9 +6,9 @@ C API Stability *************** -Python's C API is covered by the Backwards Compatibility Policy, :pep:`387`. -While the C API will change with every minor release (e.g. from 3.9 to 3.10), -most changes will be source-compatible, typically by only adding new API. +Unless documented otherwise, Python's C API is covered by the Backwards +Compatibility Policy, :pep:`387`. +Most changes to it are source-compatible (typically by only adding new API). Changing existing API or removing API is only done after a deprecation period or to fix serious issues. @@ -18,8 +18,38 @@ way; see :ref:`stable-abi-platform` below). So, code compiled for Python 3.10.0 will work on 3.10.8 and vice versa, but will need to be compiled separately for 3.9.x and 3.10.x. +There are two tiers of C API with different stability exepectations: + +- *Unstable API*, may change in minor versions without a deprecation period. + It is marked by the ``PyUnstable`` prefix in names. +- *Limited API*, is compatible across several minor releases. + When :c:macro:`Py_LIMITED_API` is defined, only this subset is exposed + from ``Python.h``. + +These are discussed in more detail below. + Names prefixed by an underscore, such as ``_Py_InternalState``, are private API that can change without notice even in patch releases. +If you need to use this API, consider reaching out to +`CPython developers `_ +to discuss adding public API for your use case. + +.. _unstable-c-api: + +Unstable C API +============== + +.. index:: single: PyUnstable + +Any API named with the ``PyUnstable`` prefix exposes CPython implementation +details, and may change in every minor release (e.g. from 3.9 to 3.10) without +any deprecation warnings. +However, it will not change in a bugfix release (e.g. from 3.10.0 to 3.10.1). + +It is generally intended for specialized, low-level tools like debuggers. + +Projects that use this API are expected to follow +CPython development and spend extra effort adjusting to changes. Stable Application Binary Interface diff --git a/Doc/tools/extensions/c_annotations.py b/Doc/tools/extensions/c_annotations.py index 9defb24a0331ef..e5dc82cf0dc2d9 100644 --- a/Doc/tools/extensions/c_annotations.py +++ b/Doc/tools/extensions/c_annotations.py @@ -143,6 +143,22 @@ def add_annotations(self, app, doctree): ' (Only some members are part of the stable ABI.)') node.insert(0, emph_node) + # Unstable API annotation. + if name.startswith('PyUnstable'): + warn_node = nodes.admonition( + classes=['unstable-c-api', 'warning']) + message = 'This is ' + emph_node = nodes.emphasis(message, message) + ref_node = addnodes.pending_xref( + 'Unstable API', refdomain="std", + reftarget='unstable-c-api', + reftype='ref', refexplicit="False") + ref_node += nodes.Text('Unstable API') + emph_node += ref_node + emph_node += nodes.Text('. It may change without warning in minor releases.') + warn_node += emph_node + node.insert(0, warn_node) + # Return value annotation if objtype != 'function': continue diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 1a25ec6b70613b..c0c021c679147f 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -810,6 +810,29 @@ C API Changes New Features ------------ + +* :pep:`697`: Introduced the :ref:`Unstable C API tier `, + intended for low-level tools like debuggers and JIT compilers. + This API may change in each minor release of CPython without deprecation + warnings. + Its contents are marked by the ``PyUnstable_`` prefix in names. + + Code object constructors: + + - ``PyUnstable_Code_New()`` (renamed from ``PyCode_New``) + - ``PyUnstable_Code_NewWithPosOnlyArgs()`` (renamed from ``PyCode_NewWithPosOnlyArgs``) + + Extra storage for code objects (:pep:`523`): + + - ``PyUnstable_Eval_RequestCodeExtraIndex()`` (renamed from ``_PyEval_RequestCodeExtraIndex``) + - ``PyUnstable_Code_GetExtra()`` (renamed from ``_PyCode_GetExtra``) + - ``PyUnstable_Code_SetExtra()`` (renamed from ``_PyCode_SetExtra``) + + The original names will continue to be available until the respective + API changes. + + (Contributed by Petr Viktorin in :gh:`101101`.) + * Added the new limited C API function :c:func:`PyType_FromMetaclass`, which generalizes the existing :c:func:`PyType_FromModuleAndSpec` using an additional metaclass argument. diff --git a/Include/README.rst b/Include/README.rst index f52e690eac9a91..531f09692f783f 100644 --- a/Include/README.rst +++ b/Include/README.rst @@ -1,11 +1,13 @@ The Python C API ================ -The C API is divided into three sections: +The C API is divided into these sections: 1. ``Include/``: Limited API 2. ``Include/cpython/``: CPython implementation details -3. ``Include/internal/``: The internal API +3. ``Include/cpython/``, names with the ``PyUnstable_`` prefix: API that can + change between minor releases +4. ``Include/internal/``, and any name with ``_`` prefix: The internal API Information on changing the C API is available `in the developer guide`_ diff --git a/Include/cpython/ceval.h b/Include/cpython/ceval.h index 74665c9fa10580..0fbbee10c2edce 100644 --- a/Include/cpython/ceval.h +++ b/Include/cpython/ceval.h @@ -22,7 +22,12 @@ PyAPI_FUNC(PyObject *) _PyEval_EvalFrameDefault(PyThreadState *tstate, struct _P PyAPI_FUNC(void) _PyEval_SetSwitchInterval(unsigned long microseconds); PyAPI_FUNC(unsigned long) _PyEval_GetSwitchInterval(void); -PyAPI_FUNC(Py_ssize_t) _PyEval_RequestCodeExtraIndex(freefunc); +PyAPI_FUNC(Py_ssize_t) PyUnstable_Eval_RequestCodeExtraIndex(freefunc); +// Old name -- remove when this API changes: +_Py_DEPRECATED_EXTERNALLY(3.12) static inline Py_ssize_t +_PyEval_RequestCodeExtraIndex(freefunc f) { + return PyUnstable_Eval_RequestCodeExtraIndex(f); +} PyAPI_FUNC(int) _PyEval_SliceIndex(PyObject *, Py_ssize_t *); PyAPI_FUNC(int) _PyEval_SliceIndexNotNone(PyObject *, Py_ssize_t *); diff --git a/Include/cpython/code.h b/Include/cpython/code.h index fba9296aedc99d..0e4bd8a58c165b 100644 --- a/Include/cpython/code.h +++ b/Include/cpython/code.h @@ -178,19 +178,40 @@ static inline int PyCode_GetFirstFree(PyCodeObject *op) { #define _PyCode_CODE(CO) _Py_RVALUE((_Py_CODEUNIT *)(CO)->co_code_adaptive) #define _PyCode_NBYTES(CO) (Py_SIZE(CO) * (Py_ssize_t)sizeof(_Py_CODEUNIT)) -/* Public interface */ -PyAPI_FUNC(PyCodeObject *) PyCode_New( +/* Unstable public interface */ +PyAPI_FUNC(PyCodeObject *) PyUnstable_Code_New( int, int, int, int, int, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, int, PyObject *, PyObject *); -PyAPI_FUNC(PyCodeObject *) PyCode_NewWithPosOnlyArgs( +PyAPI_FUNC(PyCodeObject *) PyUnstable_Code_NewWithPosOnlyArgs( int, int, int, int, int, int, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, int, PyObject *, PyObject *); /* same as struct above */ +// Old names -- remove when this API changes: +_Py_DEPRECATED_EXTERNALLY(3.12) static inline PyCodeObject * +PyCode_New( + int a, int b, int c, int d, int e, PyObject *f, PyObject *g, + PyObject *h, PyObject *i, PyObject *j, PyObject *k, + PyObject *l, PyObject *m, PyObject *n, int o, PyObject *p, + PyObject *q) +{ + return PyUnstable_Code_New( + a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q); +} +_Py_DEPRECATED_EXTERNALLY(3.12) static inline PyCodeObject * +PyCode_NewWithPosOnlyArgs( + int a, int poac, int b, int c, int d, int e, PyObject *f, PyObject *g, + PyObject *h, PyObject *i, PyObject *j, PyObject *k, + PyObject *l, PyObject *m, PyObject *n, int o, PyObject *p, + PyObject *q) +{ + return PyUnstable_Code_NewWithPosOnlyArgs( + a, poac, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q); +} /* Creates a new empty code object with the specified source location. */ PyAPI_FUNC(PyCodeObject *) @@ -269,11 +290,21 @@ PyAPI_FUNC(PyObject*) _PyCode_ConstantKey(PyObject *obj); PyAPI_FUNC(PyObject*) PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names, PyObject *lnotab); - -PyAPI_FUNC(int) _PyCode_GetExtra(PyObject *code, Py_ssize_t index, - void **extra); -PyAPI_FUNC(int) _PyCode_SetExtra(PyObject *code, Py_ssize_t index, - void *extra); +PyAPI_FUNC(int) PyUnstable_Code_GetExtra( + PyObject *code, Py_ssize_t index, void **extra); +PyAPI_FUNC(int) PyUnstable_Code_SetExtra( + PyObject *code, Py_ssize_t index, void *extra); +// Old names -- remove when this API changes: +_Py_DEPRECATED_EXTERNALLY(3.12) static inline int +_PyCode_GetExtra(PyObject *code, Py_ssize_t index, void **extra) +{ + return PyUnstable_Code_GetExtra(code, index, extra); +} +_Py_DEPRECATED_EXTERNALLY(3.12) static inline int +_PyCode_SetExtra(PyObject *code, Py_ssize_t index, void *extra) +{ + return PyUnstable_Code_SetExtra(code, index, extra); +} /* Equivalent to getattr(code, 'co_code') in Python. Returns a strong reference to a bytes object. */ diff --git a/Include/pyport.h b/Include/pyport.h index 40092c2f81ad48..eef0fe1bfd71d8 100644 --- a/Include/pyport.h +++ b/Include/pyport.h @@ -323,6 +323,15 @@ extern "C" { #define Py_DEPRECATED(VERSION_UNUSED) #endif +// _Py_DEPRECATED_EXTERNALLY(version) +// Deprecated outside CPython core. +#ifdef Py_BUILD_CORE +#define _Py_DEPRECATED_EXTERNALLY(VERSION_UNUSED) +#else +#define _Py_DEPRECATED_EXTERNALLY(version) Py_DEPRECATED(version) +#endif + + #if defined(__clang__) #define _Py_COMP_DIAG_PUSH _Pragma("clang diagnostic push") #define _Py_COMP_DIAG_IGNORE_DEPR_DECLS \ diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py index 9c2ac83e1b69e3..0cd1fb3f9728e5 100644 --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -752,15 +752,15 @@ def f(): py = ctypes.pythonapi freefunc = ctypes.CFUNCTYPE(None,ctypes.c_voidp) - RequestCodeExtraIndex = py._PyEval_RequestCodeExtraIndex + RequestCodeExtraIndex = py.PyUnstable_Eval_RequestCodeExtraIndex RequestCodeExtraIndex.argtypes = (freefunc,) RequestCodeExtraIndex.restype = ctypes.c_ssize_t - SetExtra = py._PyCode_SetExtra + SetExtra = py.PyUnstable_Code_SetExtra SetExtra.argtypes = (ctypes.py_object, ctypes.c_ssize_t, ctypes.c_voidp) SetExtra.restype = ctypes.c_int - GetExtra = py._PyCode_GetExtra + GetExtra = py.PyUnstable_Code_GetExtra GetExtra.argtypes = (ctypes.py_object, ctypes.c_ssize_t, ctypes.POINTER(ctypes.c_voidp)) GetExtra.restype = ctypes.c_int diff --git a/Misc/NEWS.d/next/C API/2022-04-21-17-25-22.gh-issue-91744.FgvaMi.rst b/Misc/NEWS.d/next/C API/2022-04-21-17-25-22.gh-issue-91744.FgvaMi.rst new file mode 100644 index 00000000000000..20db25ddd0c1f6 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2022-04-21-17-25-22.gh-issue-91744.FgvaMi.rst @@ -0,0 +1,3 @@ +Introduced the *Unstable C API tier*, marking APi that is allowed to change +in minor releases without a deprecation period. +See :pep:`689` for details. diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index 7551e5b349430e..b12290d436cbeb 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -169,7 +169,7 @@ @MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c @MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c @MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c -@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/unicode.c _testcapi/getargs.c _testcapi/pytime.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/structmember.c _testcapi/exceptions.c +@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/unicode.c _testcapi/getargs.c _testcapi/pytime.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c @MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c # Some testing modules MUST be built as shared libraries. diff --git a/Modules/_testcapi/code.c b/Modules/_testcapi/code.c new file mode 100644 index 00000000000000..588dc67971ea5a --- /dev/null +++ b/Modules/_testcapi/code.c @@ -0,0 +1,115 @@ +#include "parts.h" + +static Py_ssize_t +get_code_extra_index(PyInterpreterState* interp) { + Py_ssize_t result = -1; + + static const char *key = "_testcapi.frame_evaluation.code_index"; + + PyObject *interp_dict = PyInterpreterState_GetDict(interp); // borrowed + assert(interp_dict); // real users would handle missing dict... somehow + + PyObject *index_obj = PyDict_GetItemString(interp_dict, key); // borrowed + Py_ssize_t index = 0; + if (!index_obj) { + if (PyErr_Occurred()) { + goto finally; + } + index = PyUnstable_Eval_RequestCodeExtraIndex(NULL); + if (index < 0 || PyErr_Occurred()) { + goto finally; + } + index_obj = PyLong_FromSsize_t(index); // strong ref + if (!index_obj) { + goto finally; + } + int res = PyDict_SetItemString(interp_dict, key, index_obj); + Py_DECREF(index_obj); + if (res < 0) { + goto finally; + } + } + else { + index = PyLong_AsSsize_t(index_obj); + if (index == -1 && PyErr_Occurred()) { + goto finally; + } + } + + result = index; +finally: + return result; +} + +static PyObject * +test_code_extra(PyObject* self, PyObject *Py_UNUSED(callable)) +{ + PyObject *result = NULL; + PyObject *test_module = NULL; + PyObject *test_func = NULL; + + // Get or initialize interpreter-specific code object storage index + PyInterpreterState *interp = PyInterpreterState_Get(); + if (!interp) { + return NULL; + } + Py_ssize_t code_extra_index = get_code_extra_index(interp); + if (PyErr_Occurred()) { + goto finally; + } + + // Get a function to test with + // This can be any Python function. Use `test.test_misc.testfunction`. + test_module = PyImport_ImportModule("test.test_capi.test_misc"); + if (!test_module) { + goto finally; + } + test_func = PyObject_GetAttrString(test_module, "testfunction"); + if (!test_func) { + goto finally; + } + PyObject *test_func_code = PyFunction_GetCode(test_func); // borrowed + if (!test_func_code) { + goto finally; + } + + // Check the value is initially NULL + void *extra; + int res = PyUnstable_Code_GetExtra(test_func_code, code_extra_index, &extra); + if (res < 0) { + goto finally; + } + assert (extra == NULL); + + // Set another code extra value + res = PyUnstable_Code_SetExtra(test_func_code, code_extra_index, (void*)(uintptr_t)77); + if (res < 0) { + goto finally; + } + // Assert it was set correctly + res = PyUnstable_Code_GetExtra(test_func_code, code_extra_index, &extra); + if (res < 0) { + goto finally; + } + assert ((uintptr_t)extra == 77); + + result = Py_NewRef(Py_None); +finally: + Py_XDECREF(test_module); + Py_XDECREF(test_func); + return result; +} + +static PyMethodDef TestMethods[] = { + {"test_code_extra", test_code_extra, METH_NOARGS}, + {NULL}, +}; + +int +_PyTestCapi_Init_Code(PyObject *m) { + if (PyModule_AddFunctions(m, TestMethods) < 0) { + return -1; + } + + return 0; +} diff --git a/Modules/_testcapi/parts.h b/Modules/_testcapi/parts.h index 1689f186b833f6..c8f31dc8e39fae 100644 --- a/Modules/_testcapi/parts.h +++ b/Modules/_testcapi/parts.h @@ -37,6 +37,7 @@ int _PyTestCapi_Init_Long(PyObject *module); int _PyTestCapi_Init_Float(PyObject *module); int _PyTestCapi_Init_Structmember(PyObject *module); int _PyTestCapi_Init_Exceptions(PyObject *module); +int _PyTestCapi_Init_Code(PyObject *module); #ifdef LIMITED_API_AVAILABLE int _PyTestCapi_Init_VectorcallLimited(PyObject *module); diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index fc716a3564d39a..10e507d6b481de 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -4083,6 +4083,9 @@ PyInit__testcapi(void) if (_PyTestCapi_Init_Exceptions(m) < 0) { return NULL; } + if (_PyTestCapi_Init_Code(m) < 0) { + return NULL; + } #ifndef LIMITED_API_AVAILABLE PyModule_AddObjectRef(m, "LIMITED_API_AVAILABLE", Py_False); diff --git a/Objects/codeobject.c b/Objects/codeobject.c index a03b14edea8d4c..175bd57568f8f6 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -567,7 +567,8 @@ _PyCode_New(struct _PyCodeConstructor *con) ******************/ PyCodeObject * -PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, +PyUnstable_Code_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, @@ -691,7 +692,7 @@ PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, } PyCodeObject * -PyCode_New(int argcount, int kwonlyargcount, +PyUnstable_Code_New(int argcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, @@ -1371,7 +1372,7 @@ typedef struct { int -_PyCode_GetExtra(PyObject *code, Py_ssize_t index, void **extra) +PyUnstable_Code_GetExtra(PyObject *code, Py_ssize_t index, void **extra) { if (!PyCode_Check(code)) { PyErr_BadInternalCall(); @@ -1392,7 +1393,7 @@ _PyCode_GetExtra(PyObject *code, Py_ssize_t index, void **extra) int -_PyCode_SetExtra(PyObject *code, Py_ssize_t index, void *extra) +PyUnstable_Code_SetExtra(PyObject *code, Py_ssize_t index, void *extra) { PyInterpreterState *interp = _PyInterpreterState_GET(); diff --git a/PCbuild/_testcapi.vcxproj b/PCbuild/_testcapi.vcxproj index 742eb3ed2d9056..4cc184bfc1ac82 100644 --- a/PCbuild/_testcapi.vcxproj +++ b/PCbuild/_testcapi.vcxproj @@ -108,6 +108,7 @@ + diff --git a/PCbuild/_testcapi.vcxproj.filters b/PCbuild/_testcapi.vcxproj.filters index ab5afc150c32f5..fbdaf04ce37cb1 100644 --- a/PCbuild/_testcapi.vcxproj.filters +++ b/PCbuild/_testcapi.vcxproj.filters @@ -54,6 +54,9 @@ Source Files + + Source Files + diff --git a/Python/ceval.c b/Python/ceval.c index b382d2109b93b7..5540c93d5e3dd7 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2988,7 +2988,7 @@ format_awaitable_error(PyThreadState *tstate, PyTypeObject *type, int oparg) Py_ssize_t -_PyEval_RequestCodeExtraIndex(freefunc free) +PyUnstable_Eval_RequestCodeExtraIndex(freefunc free) { PyInterpreterState *interp = _PyInterpreterState_GET(); Py_ssize_t new_index; From 9f799ab0200f07d762eb8fe822c606e1b4b4866e Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Tue, 28 Feb 2023 11:29:32 +0000 Subject: [PATCH 208/247] gh-87092: Make jump target label equal to the offset of the target in the instructions sequence (#102093) --- Lib/test/support/bytecode_helper.py | 83 +++++++++----------- Lib/test/test_compiler_codegen.py | 8 +- Lib/test/test_peepholer.py | 13 ++-- Python/compile.c | 115 ++++++++++++++++------------ 4 files changed, 111 insertions(+), 108 deletions(-) diff --git a/Lib/test/support/bytecode_helper.py b/Lib/test/support/bytecode_helper.py index 65ae7a227baafe..190fe8723b1fb5 100644 --- a/Lib/test/support/bytecode_helper.py +++ b/Lib/test/support/bytecode_helper.py @@ -50,18 +50,13 @@ class CompilationStepTestCase(unittest.TestCase): HAS_TARGET = set(dis.hasjrel + dis.hasjabs + dis.hasexc) HAS_ARG_OR_TARGET = HAS_ARG.union(HAS_TARGET) - def setUp(self): - self.last_label = 0 - - def Label(self): - self.last_label += 1 - return self.last_label + class Label: + pass def assertInstructionsMatch(self, actual_, expected_): # get two lists where each entry is a label or - # an instruction tuple. Compare them, while mapping - # each actual label to a corresponding expected label - # based on their locations. + # an instruction tuple. Normalize the labels to the + # instruction count of the target, and compare the lists. self.assertIsInstance(actual_, list) self.assertIsInstance(expected_, list) @@ -82,39 +77,35 @@ def assertInstructionsMatch(self, actual_, expected_): act = act[:len(exp)] self.assertEqual(exp, act) + def resolveAndRemoveLabels(self, insts): + idx = 0 + res = [] + for item in insts: + assert isinstance(item, (self.Label, tuple)) + if isinstance(item, self.Label): + item.value = idx + else: + idx += 1 + res.append(item) + + return res + def normalize_insts(self, insts): """ Map labels to instruction index. - Remove labels which are not used as jump targets. Map opcodes to opnames. """ - labels_map = {} - targets = set() - idx = 1 - for item in insts: - assert isinstance(item, (int, tuple)) - if isinstance(item, tuple): - opcode, oparg, *_ = item - if dis.opmap.get(opcode, opcode) in self.HAS_TARGET: - targets.add(oparg) - idx += 1 - elif isinstance(item, int): - assert item not in labels_map, "label reused" - labels_map[item] = idx - + insts = self.resolveAndRemoveLabels(insts) res = [] for item in insts: - if isinstance(item, int) and item in targets: - if not res or labels_map[item] != res[-1]: - res.append(labels_map[item]) - elif isinstance(item, tuple): - opcode, oparg, *loc = item - opcode = dis.opmap.get(opcode, opcode) - if opcode in self.HAS_TARGET: - arg = labels_map[oparg] - else: - arg = oparg if opcode in self.HAS_TARGET else None - opcode = dis.opname[opcode] - res.append((opcode, arg, *loc)) + assert isinstance(item, tuple) + opcode, oparg, *loc = item + opcode = dis.opmap.get(opcode, opcode) + if isinstance(oparg, self.Label): + arg = oparg.value + else: + arg = oparg if opcode in self.HAS_ARG else None + opcode = dis.opname[opcode] + res.append((opcode, arg, *loc)) return res @@ -129,20 +120,18 @@ class CfgOptimizationTestCase(CompilationStepTestCase): def complete_insts_info(self, insts): # fill in omitted fields in location, and oparg 0 for ops with no arg. - instructions = [] + res = [] for item in insts: - if isinstance(item, int): - instructions.append(item) - else: - assert isinstance(item, tuple) - inst = list(reversed(item)) - opcode = dis.opmap[inst.pop()] - oparg = inst.pop() if opcode in self.HAS_ARG_OR_TARGET else 0 - loc = inst + [-1] * (4 - len(inst)) - instructions.append((opcode, oparg, *loc)) - return instructions + assert isinstance(item, tuple) + inst = list(reversed(item)) + opcode = dis.opmap[inst.pop()] + oparg = inst.pop() if opcode in self.HAS_ARG_OR_TARGET else 0 + loc = inst + [-1] * (4 - len(inst)) + res.append((opcode, oparg, *loc)) + return res def get_optimized(self, insts, consts): + insts = self.normalize_insts(insts) insts = self.complete_insts_info(insts) insts = optimize_cfg(insts, consts) return insts, consts diff --git a/Lib/test/test_compiler_codegen.py b/Lib/test/test_compiler_codegen.py index f2e14c1e628c01..022753e0c99483 100644 --- a/Lib/test/test_compiler_codegen.py +++ b/Lib/test/test_compiler_codegen.py @@ -37,11 +37,11 @@ def test_for_loop(self): ('GET_ITER', None, 1), loop_lbl := self.Label(), ('FOR_ITER', exit_lbl := self.Label(), 1), - ('STORE_NAME', None, 1), + ('STORE_NAME', 1, 1), ('PUSH_NULL', None, 2), - ('LOAD_NAME', None, 2), - ('LOAD_NAME', None, 2), - ('CALL', None, 2), + ('LOAD_NAME', 2, 2), + ('LOAD_NAME', 1, 2), + ('CALL', 1, 2), ('POP_TOP', None), ('JUMP', loop_lbl), exit_lbl, diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 707ff821b31a8a..aea234e38705a8 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -984,6 +984,7 @@ def cfg_optimization_test(self, insts, expected_insts, if expected_consts is None: expected_consts = consts opt_insts, opt_consts = self.get_optimized(insts, consts) + expected_insts = self.normalize_insts(expected_insts) self.assertInstructionsMatch(opt_insts, expected_insts) self.assertEqual(opt_consts, expected_consts) @@ -996,11 +997,11 @@ def test_conditional_jump_forward_non_const_condition(self): ('LOAD_CONST', 3, 14), ] expected = [ - ('LOAD_NAME', '1', 11), + ('LOAD_NAME', 1, 11), ('POP_JUMP_IF_TRUE', lbl := self.Label(), 12), - ('LOAD_CONST', '2', 13), + ('LOAD_CONST', 2, 13), lbl, - ('LOAD_CONST', '3', 14) + ('LOAD_CONST', 3, 14) ] self.cfg_optimization_test(insts, expected, consts=list(range(5))) @@ -1018,7 +1019,7 @@ def test_conditional_jump_forward_const_condition(self): expected = [ ('NOP', None, 11), ('NOP', None, 12), - ('LOAD_CONST', '3', 14) + ('LOAD_CONST', 3, 14) ] self.cfg_optimization_test(insts, expected, consts=list(range(5))) @@ -1031,9 +1032,9 @@ def test_conditional_jump_backward_non_const_condition(self): ] expected = [ lbl := self.Label(), - ('LOAD_NAME', '1', 11), + ('LOAD_NAME', 1, 11), ('POP_JUMP_IF_TRUE', lbl, 12), - ('LOAD_CONST', '2', 13) + ('LOAD_CONST', 2, 13) ] self.cfg_optimization_test(insts, expected, consts=list(range(5))) diff --git a/Python/compile.c b/Python/compile.c index 3f620beb0d0205..2f1130e62ee161 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -9704,56 +9704,77 @@ instructions_to_cfg(PyObject *instructions, cfg_builder *g) { assert(PyList_Check(instructions)); - Py_ssize_t instr_size = PyList_GET_SIZE(instructions); - for (Py_ssize_t i = 0; i < instr_size; i++) { + Py_ssize_t num_insts = PyList_GET_SIZE(instructions); + bool *is_target = PyMem_Calloc(num_insts, sizeof(bool)); + if (is_target == NULL) { + return ERROR; + } + for (Py_ssize_t i = 0; i < num_insts; i++) { PyObject *item = PyList_GET_ITEM(instructions, i); - if (PyLong_Check(item)) { - int lbl_id = PyLong_AsLong(item); + if (!PyTuple_Check(item) || PyTuple_GET_SIZE(item) != 6) { + PyErr_SetString(PyExc_ValueError, "expected a 6-tuple"); + goto error; + } + int opcode = PyLong_AsLong(PyTuple_GET_ITEM(item, 0)); + if (PyErr_Occurred()) { + goto error; + } + if (HAS_TARGET(opcode)) { + int oparg = PyLong_AsLong(PyTuple_GET_ITEM(item, 1)); if (PyErr_Occurred()) { - return ERROR; + goto error; } - if (lbl_id <= 0 || lbl_id > instr_size) { - /* expect label in a reasonable range */ + if (oparg < 0 || oparg >= num_insts) { PyErr_SetString(PyExc_ValueError, "label out of range"); - return ERROR; + goto error; } - jump_target_label lbl = {lbl_id}; + is_target[oparg] = true; + } + } + + for (int i = 0; i < num_insts; i++) { + if (is_target[i]) { + jump_target_label lbl = {i}; RETURN_IF_ERROR(cfg_builder_use_label(g, lbl)); } - else { - if (!PyTuple_Check(item) || PyTuple_GET_SIZE(item) != 6) { - PyErr_SetString(PyExc_ValueError, "expected a 6-tuple"); - return ERROR; - } - int opcode = PyLong_AsLong(PyTuple_GET_ITEM(item, 0)); - if (PyErr_Occurred()) { - return ERROR; - } - int oparg = PyLong_AsLong(PyTuple_GET_ITEM(item, 1)); - if (PyErr_Occurred()) { - return ERROR; - } - location loc; - loc.lineno = PyLong_AsLong(PyTuple_GET_ITEM(item, 2)); - if (PyErr_Occurred()) { - return ERROR; - } - loc.end_lineno = PyLong_AsLong(PyTuple_GET_ITEM(item, 3)); - if (PyErr_Occurred()) { - return ERROR; - } - loc.col_offset = PyLong_AsLong(PyTuple_GET_ITEM(item, 4)); - if (PyErr_Occurred()) { - return ERROR; - } - loc.end_col_offset = PyLong_AsLong(PyTuple_GET_ITEM(item, 5)); - if (PyErr_Occurred()) { - return ERROR; - } - RETURN_IF_ERROR(cfg_builder_addop(g, opcode, oparg, loc)); + PyObject *item = PyList_GET_ITEM(instructions, i); + if (!PyTuple_Check(item) || PyTuple_GET_SIZE(item) != 6) { + PyErr_SetString(PyExc_ValueError, "expected a 6-tuple"); + goto error; + } + int opcode = PyLong_AsLong(PyTuple_GET_ITEM(item, 0)); + if (PyErr_Occurred()) { + goto error; + } + int oparg = PyLong_AsLong(PyTuple_GET_ITEM(item, 1)); + if (PyErr_Occurred()) { + goto error; + } + location loc; + loc.lineno = PyLong_AsLong(PyTuple_GET_ITEM(item, 2)); + if (PyErr_Occurred()) { + goto error; } + loc.end_lineno = PyLong_AsLong(PyTuple_GET_ITEM(item, 3)); + if (PyErr_Occurred()) { + goto error; + } + loc.col_offset = PyLong_AsLong(PyTuple_GET_ITEM(item, 4)); + if (PyErr_Occurred()) { + goto error; + } + loc.end_col_offset = PyLong_AsLong(PyTuple_GET_ITEM(item, 5)); + if (PyErr_Occurred()) { + goto error; + } + RETURN_IF_ERROR(cfg_builder_addop(g, opcode, oparg, loc)); } + + PyMem_Free(is_target); return SUCCESS; +error: + PyMem_Free(is_target); + return ERROR; } static PyObject * @@ -9763,20 +9784,12 @@ cfg_to_instructions(cfg_builder *g) if (instructions == NULL) { return NULL; } - int lbl = 1; + int lbl = 0; for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) { - b->b_label = lbl++; + b->b_label = lbl; + lbl += b->b_iused; } for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) { - PyObject *lbl = PyLong_FromLong(b->b_label); - if (lbl == NULL) { - goto error; - } - if (PyList_Append(instructions, lbl) != 0) { - Py_DECREF(lbl); - goto error; - } - Py_DECREF(lbl); for (int i = 0; i < b->b_iused; i++) { struct instr *instr = &b->b_instr[i]; location loc = instr->i_loc; From 85b1fc1fc5a2e49d521382eaf5e7793175d00371 Mon Sep 17 00:00:00 2001 From: Furkan Onder Date: Tue, 28 Feb 2023 11:43:00 +0000 Subject: [PATCH 209/247] GH-90744: Fix erroneous doc links in the sys module (#101319) Co-authored-by: Hugo van Kemenade --- Doc/library/sys.rst | 98 ++++++++++++++++++++++----------------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 605e2c9a6710c1..23c5bbed0c6f9c 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -568,55 +568,55 @@ always available. .. tabularcolumns:: |l|l|L| - +---------------------+----------------+--------------------------------------------------+ - | attribute | float.h macro | explanation | - +=====================+================+==================================================+ - | :const:`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`. | - +---------------------+----------------+--------------------------------------------------+ - | :const:`dig` | DBL_DIG | maximum number of decimal digits that can be | - | | | faithfully represented in a float; see below | - +---------------------+----------------+--------------------------------------------------+ - | :const:`mant_dig` | DBL_MANT_DIG | float precision: the number of base-``radix`` | - | | | digits in the significand of a float | - +---------------------+----------------+--------------------------------------------------+ - | :const:`max` | DBL_MAX | maximum representable positive finite float | - +---------------------+----------------+--------------------------------------------------+ - | :const:`max_exp` | DBL_MAX_EXP | maximum integer *e* such that ``radix**(e-1)`` is| - | | | a representable finite float | - +---------------------+----------------+--------------------------------------------------+ - | :const:`max_10_exp` | DBL_MAX_10_EXP | maximum integer *e* such that ``10**e`` is in the| - | | | range of representable finite floats | - +---------------------+----------------+--------------------------------------------------+ - | :const:`min` | DBL_MIN | minimum representable positive *normalized* float| - | | | | - | | | Use :func:`math.ulp(0.0) ` to get the | - | | | smallest positive *denormalized* representable | - | | | float. | - +---------------------+----------------+--------------------------------------------------+ - | :const:`min_exp` | DBL_MIN_EXP | minimum integer *e* such that ``radix**(e-1)`` is| - | | | a normalized float | - +---------------------+----------------+--------------------------------------------------+ - | :const:`min_10_exp` | DBL_MIN_10_EXP | minimum integer *e* such that ``10**e`` is a | - | | | normalized float | - +---------------------+----------------+--------------------------------------------------+ - | :const:`radix` | FLT_RADIX | radix of exponent representation | - +---------------------+----------------+--------------------------------------------------+ - | :const:`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. | - +---------------------+----------------+--------------------------------------------------+ + +---------------------+---------------------+--------------------------------------------------+ + | 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. | + +---------------------+---------------------+--------------------------------------------------+ The attribute :attr:`sys.float_info.dig` needs further explanation. If ``s`` is any string representing a decimal number with at most From 4c87537efb5fd28b4e4ee9631076ed5953720156 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Tue, 28 Feb 2023 11:50:52 +0000 Subject: [PATCH 210/247] gh-102192: Replace PyErr_Fetch/Restore etc by more efficient alternatives (in Python/) (#102193) --- Python/bytecodes.c | 2 +- Python/ceval.c | 49 +++++++++++++++------------------------------ Python/ceval_gil.c | 7 +++---- Python/compile.c | 5 ++--- Python/frame.c | 9 +++------ Python/initconfig.c | 2 +- Python/modsupport.c | 14 +++++-------- Python/sysmodule.c | 26 ++++++++++-------------- 8 files changed, 42 insertions(+), 72 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 7e9b36f697210a..63dbecad3b45fc 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -17,7 +17,7 @@ #include "pycore_object.h" // _PyObject_GC_TRACK() #include "pycore_moduleobject.h" // PyModuleObject #include "pycore_opcode.h" // EXTRA_CASES -#include "pycore_pyerrors.h" // _PyErr_Fetch() +#include "pycore_pyerrors.h" // _PyErr_GetRaisedException() #include "pycore_pymem.h" // _PyMem_IsPtrFreed() #include "pycore_pystate.h" // _PyInterpreterState_GET() #include "pycore_range.h" // _PyRangeIterObject diff --git a/Python/ceval.c b/Python/ceval.c index 5540c93d5e3dd7..b422d0ed34ede3 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -13,7 +13,7 @@ #include "pycore_object.h" // _PyObject_GC_TRACK() #include "pycore_moduleobject.h" // PyModuleObject #include "pycore_opcode.h" // EXTRA_CASES -#include "pycore_pyerrors.h" // _PyErr_Fetch() +#include "pycore_pyerrors.h" // _PyErr_Fetch(), _PyErr_GetRaisedException() #include "pycore_pymem.h" // _PyMem_IsPtrFreed() #include "pycore_pystate.h" // _PyInterpreterState_GET() #include "pycore_range.h" // _PyRangeIterObject @@ -105,8 +105,7 @@ static void dump_stack(_PyInterpreterFrame *frame, PyObject **stack_pointer) { PyObject **stack_base = _PyFrame_Stackbase(frame); - PyObject *type, *value, *traceback; - PyErr_Fetch(&type, &value, &traceback); + PyObject *exc = PyErr_GetRaisedException(); printf(" stack=["); for (PyObject **ptr = stack_base; ptr < stack_pointer; ptr++) { if (ptr != stack_base) { @@ -120,7 +119,7 @@ dump_stack(_PyInterpreterFrame *frame, PyObject **stack_pointer) } printf("]\n"); fflush(stdout); - PyErr_Restore(type, value, traceback); + PyErr_SetRaisedException(exc); } static void @@ -157,8 +156,7 @@ lltrace_resume_frame(_PyInterpreterFrame *frame) return; } PyFunctionObject *f = (PyFunctionObject *)fobj; - PyObject *type, *value, *traceback; - PyErr_Fetch(&type, &value, &traceback); + PyObject *exc = PyErr_GetRaisedException(); PyObject *name = f->func_qualname; if (name == NULL) { name = f->func_name; @@ -178,7 +176,7 @@ lltrace_resume_frame(_PyInterpreterFrame *frame) } printf("\n"); fflush(stdout); - PyErr_Restore(type, value, traceback); + PyErr_SetRaisedException(exc); } #endif static int call_trace(Py_tracefunc, PyObject *, @@ -1032,7 +1030,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int PyObject *v = POP(); Py_XDECREF(v); } - PyObject *exc, *val, *tb; if (lasti) { int frame_lasti = _PyInterpreterFrame_LASTI(frame); PyObject *lasti = PyLong_FromLong(frame_lasti); @@ -1041,19 +1038,12 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } PUSH(lasti); } - _PyErr_Fetch(tstate, &exc, &val, &tb); + /* Make the raw exception data available to the handler, so a program can emulate the Python main loop. */ - _PyErr_NormalizeException(tstate, &exc, &val, &tb); - if (tb != NULL) - PyException_SetTraceback(val, tb); - else - PyException_SetTraceback(val, Py_None); - Py_XDECREF(tb); - Py_XDECREF(exc); - PUSH(val); + PUSH(_PyErr_GetRaisedException(tstate)); JUMPTO(handler); /* Resume normal execution */ DISPATCH(); @@ -2075,19 +2065,15 @@ call_trace_protected(Py_tracefunc func, PyObject *obj, PyThreadState *tstate, _PyInterpreterFrame *frame, int what, PyObject *arg) { - PyObject *type, *value, *traceback; - int err; - _PyErr_Fetch(tstate, &type, &value, &traceback); - err = call_trace(func, obj, tstate, frame, what, arg); + PyObject *exc = _PyErr_GetRaisedException(tstate); + int err = call_trace(func, obj, tstate, frame, what, arg); if (err == 0) { - _PyErr_Restore(tstate, type, value, traceback); + _PyErr_SetRaisedException(tstate, exc); return 0; } else { - Py_XDECREF(type); - Py_XDECREF(value); - Py_XDECREF(traceback); + Py_XDECREF(exc); return -1; } } @@ -2935,18 +2921,15 @@ format_exc_check_arg(PyThreadState *tstate, PyObject *exc, if (exc == PyExc_NameError) { // Include the name in the NameError exceptions to offer suggestions later. - PyObject *type, *value, *traceback; - PyErr_Fetch(&type, &value, &traceback); - PyErr_NormalizeException(&type, &value, &traceback); - if (PyErr_GivenExceptionMatches(value, PyExc_NameError)) { - PyNameErrorObject* exc = (PyNameErrorObject*) value; - if (exc->name == NULL) { + PyObject *exc = PyErr_GetRaisedException(); + if (PyErr_GivenExceptionMatches(exc, PyExc_NameError)) { + if (((PyNameErrorObject*)exc)->name == NULL) { // We do not care if this fails because we are going to restore the // NameError anyway. - (void)PyObject_SetAttr(value, &_Py_ID(name), obj); + (void)PyObject_SetAttr(exc, &_Py_ID(name), obj); } } - PyErr_Restore(type, value, traceback); + PyErr_SetRaisedException(exc); } } diff --git a/Python/ceval_gil.c b/Python/ceval_gil.c index 1bf223348d28fa..749d8144bf7a23 100644 --- a/Python/ceval_gil.c +++ b/Python/ceval_gil.c @@ -2,7 +2,7 @@ #include "Python.h" #include "pycore_atomic.h" // _Py_atomic_int #include "pycore_ceval.h" // _PyEval_SignalReceived() -#include "pycore_pyerrors.h" // _PyErr_Fetch() +#include "pycore_pyerrors.h" // _PyErr_GetRaisedException() #include "pycore_pylifecycle.h" // _PyErr_Print() #include "pycore_initconfig.h" // _PyStatus_OK() #include "pycore_interp.h" // _Py_RunGC() @@ -870,10 +870,9 @@ _Py_FinishPendingCalls(PyThreadState *tstate) } if (make_pending_calls(tstate->interp) < 0) { - PyObject *exc, *val, *tb; - _PyErr_Fetch(tstate, &exc, &val, &tb); + PyObject *exc = _PyErr_GetRaisedException(tstate); PyErr_BadInternalCall(); - _PyErr_ChainExceptions(exc, val, tb); + _PyErr_ChainExceptions1(exc); _PyErr_Print(tstate); } } diff --git a/Python/compile.c b/Python/compile.c index 2f1130e62ee161..b14c637210ff55 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -1633,8 +1633,7 @@ static void compiler_exit_scope(struct compiler *c) { // Don't call PySequence_DelItem() with an exception raised - PyObject *exc_type, *exc_val, *exc_tb; - PyErr_Fetch(&exc_type, &exc_val, &exc_tb); + PyObject *exc = PyErr_GetRaisedException(); c->c_nestlevel--; compiler_unit_free(c->u); @@ -1655,7 +1654,7 @@ compiler_exit_scope(struct compiler *c) c->u = NULL; } - PyErr_Restore(exc_type, exc_val, exc_tb); + PyErr_SetRaisedException(exc); } /* Search if variable annotations are present statically in a block. */ diff --git a/Python/frame.c b/Python/frame.c index b562709ce10fee..c2c0be30113912 100644 --- a/Python/frame.c +++ b/Python/frame.c @@ -29,17 +29,14 @@ PyFrameObject * _PyFrame_MakeAndSetFrameObject(_PyInterpreterFrame *frame) { assert(frame->frame_obj == NULL); - PyObject *error_type, *error_value, *error_traceback; - PyErr_Fetch(&error_type, &error_value, &error_traceback); + PyObject *exc = PyErr_GetRaisedException(); PyFrameObject *f = _PyFrame_New_NoTrack(frame->f_code); if (f == NULL) { - Py_XDECREF(error_type); - Py_XDECREF(error_value); - Py_XDECREF(error_traceback); + Py_XDECREF(exc); return NULL; } - PyErr_Restore(error_type, error_value, error_traceback); + PyErr_SetRaisedException(exc); if (frame->frame_obj) { // GH-97002: How did we get into this horrible situation? Most likely, // allocating f triggered a GC collection, which ran some code that diff --git a/Python/initconfig.c b/Python/initconfig.c index deec805a6b1ca4..db7f11e17d6662 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -5,7 +5,7 @@ #include "pycore_interp.h" // _PyInterpreterState.runtime #include "pycore_long.h" // _PY_LONG_MAX_STR_DIGITS_THRESHOLD #include "pycore_pathconfig.h" // _Py_path_config -#include "pycore_pyerrors.h" // _PyErr_Fetch() +#include "pycore_pyerrors.h" // _PyErr_GetRaisedException() #include "pycore_pylifecycle.h" // _Py_PreInitializeFromConfig() #include "pycore_pymem.h" // _PyMem_SetDefaultAllocator() #include "pycore_pystate.h" // _PyThreadState_GET() diff --git a/Python/modsupport.c b/Python/modsupport.c index b9a10dc157e7e5..75698455c88166 100644 --- a/Python/modsupport.c +++ b/Python/modsupport.c @@ -93,16 +93,12 @@ static PyObject *do_mkvalue(const char**, va_list *, int); static void do_ignore(const char **p_format, va_list *p_va, char endchar, Py_ssize_t n, int flags) { - PyObject *v; - Py_ssize_t i; assert(PyErr_Occurred()); - v = PyTuple_New(n); - for (i = 0; i < n; i++) { - PyObject *exception, *value, *tb, *w; - - PyErr_Fetch(&exception, &value, &tb); - w = do_mkvalue(p_format, p_va, flags); - PyErr_Restore(exception, value, tb); + PyObject *v = PyTuple_New(n); + for (Py_ssize_t i = 0; i < n; i++) { + PyObject *exc = PyErr_GetRaisedException(); + PyObject *w = do_mkvalue(p_format, p_va, flags); + PyErr_SetRaisedException(exc); if (w != NULL) { if (v != NULL) { PyTuple_SET_ITEM(v, i, w); diff --git a/Python/sysmodule.c b/Python/sysmodule.c index b69b803560924c..207abb964bcac9 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -23,7 +23,7 @@ Data members: #include "pycore_namespace.h" // _PyNamespace_New() #include "pycore_object.h" // _PyObject_IS_GC() #include "pycore_pathconfig.h" // _PyPathConfig_ComputeSysPath0() -#include "pycore_pyerrors.h" // _PyErr_Fetch() +#include "pycore_pyerrors.h" // _PyErr_GetRaisedException() #include "pycore_pylifecycle.h" // _PyErr_WriteUnraisableDefaultHook() #include "pycore_pymath.h" // _PY_SHORT_FLOAT_REPR #include "pycore_pymem.h" // _PyMem_SetDefaultAllocator() @@ -89,12 +89,11 @@ PySys_GetObject(const char *name) { PyThreadState *tstate = _PyThreadState_GET(); - PyObject *exc_type, *exc_value, *exc_tb; - _PyErr_Fetch(tstate, &exc_type, &exc_value, &exc_tb); + PyObject *exc = _PyErr_GetRaisedException(tstate); PyObject *value = _PySys_GetObject(tstate->interp, name); /* XXX Suppress a new exception if it was raised and restore * the old one. */ - _PyErr_Restore(tstate, exc_type, exc_value, exc_tb); + _PyErr_SetRaisedException(tstate, exc); return value; } @@ -203,8 +202,8 @@ sys_audit_tstate(PyThreadState *ts, const char *event, int dtrace = PyDTrace_AUDIT_ENABLED(); - PyObject *exc_type, *exc_value, *exc_tb; - _PyErr_Fetch(ts, &exc_type, &exc_value, &exc_tb); + + PyObject *exc = _PyErr_GetRaisedException(ts); /* Initialize event args now */ if (argFormat && argFormat[0]) { @@ -287,13 +286,11 @@ sys_audit_tstate(PyThreadState *ts, const char *event, Py_XDECREF(eventArgs); if (!res) { - _PyErr_Restore(ts, exc_type, exc_value, exc_tb); + _PyErr_SetRaisedException(ts, exc); } else { assert(_PyErr_Occurred(ts)); - Py_XDECREF(exc_type); - Py_XDECREF(exc_value); - Py_XDECREF(exc_tb); + Py_XDECREF(exc); } return res; @@ -3661,12 +3658,11 @@ static void sys_write(PyObject *key, FILE *fp, const char *format, va_list va) { PyObject *file; - PyObject *error_type, *error_value, *error_traceback; char buffer[1001]; int written; PyThreadState *tstate = _PyThreadState_GET(); - _PyErr_Fetch(tstate, &error_type, &error_value, &error_traceback); + PyObject *exc = _PyErr_GetRaisedException(tstate); file = _PySys_GetAttr(tstate, key); written = PyOS_vsnprintf(buffer, sizeof(buffer), format, va); if (sys_pyfile_write(buffer, file) != 0) { @@ -3678,7 +3674,7 @@ sys_write(PyObject *key, FILE *fp, const char *format, va_list va) if (sys_pyfile_write(truncated, file) != 0) fputs(truncated, fp); } - _PyErr_Restore(tstate, error_type, error_value, error_traceback); + _PyErr_SetRaisedException(tstate, exc); } void @@ -3708,7 +3704,7 @@ sys_format(PyObject *key, FILE *fp, const char *format, va_list va) const char *utf8; PyThreadState *tstate = _PyThreadState_GET(); - PyObject *error = _PyErr_GetRaisedException(tstate); + PyObject *exc = _PyErr_GetRaisedException(tstate); file = _PySys_GetAttr(tstate, key); message = PyUnicode_FromFormatV(format, va); if (message != NULL) { @@ -3720,7 +3716,7 @@ sys_format(PyObject *key, FILE *fp, const char *format, va_list va) } Py_DECREF(message); } - _PyErr_SetRaisedException(tstate, error); + _PyErr_SetRaisedException(tstate, exc); } void From e1a90ec75cd0f5cab21b467a73404081eaa1077f Mon Sep 17 00:00:00 2001 From: Ee Durbin Date: Tue, 28 Feb 2023 08:23:39 -0500 Subject: [PATCH 211/247] Migrate to new PSF mailgun account (#102284) Our legacy mailgun account is associated with a parent rackspace account that I am trying to decomission. The necessary secret has been added to the GitHub Actions Secrets already, so this is ready to go on approval. --- .github/workflows/new-bugs-announce-notifier.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/new-bugs-announce-notifier.yml b/.github/workflows/new-bugs-announce-notifier.yml index b2b63472d83421..b2a76ef7d36153 100644 --- a/.github/workflows/new-bugs-announce-notifier.yml +++ b/.github/workflows/new-bugs-announce-notifier.yml @@ -19,13 +19,13 @@ jobs: - name: Send notification uses: actions/github-script@v6 env: - MAILGUN_API_KEY: ${{ secrets.PSF_MAILGUN_KEY }} + MAILGUN_API_KEY: ${{ secrets.MAILGUN_PYTHON_ORG_MAILGUN_KEY }} with: script: | const Mailgun = require("mailgun.js"); const formData = require('form-data'); const mailgun = new Mailgun(formData); - const DOMAIN = "mg.python.org"; + const DOMAIN = "mailgun.python.org"; const mg = mailgun.client({username: 'api', key: process.env.MAILGUN_API_KEY}); github.rest.issues.get({ issue_number: context.issue.number, @@ -44,7 +44,7 @@ jobs: }; const data = { - from: "CPython Issues ", + from: "CPython Issues ", to: "new-bugs-announce@python.org", subject: `[Issue ${issue.data.number}] ${issue.data.title}`, template: "new-github-issue", From b5ff38243355c06d665ba8245d461a0d82504581 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 28 Feb 2023 08:49:35 -0800 Subject: [PATCH 212/247] GH-102305: Expand some macros in generated_cases.c.h (#102309) * Emit straight stack_pointer[-i] instead of PEEK(i), POKE(i, ...) * Expand JUMPBY() and NEXTOPARG(), and fix a perf bug --- Python/generated_cases.c.h | 845 ++++++++++++------------ Tools/cases_generator/generate_cases.py | 48 +- 2 files changed, 441 insertions(+), 452 deletions(-) diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 271ba26f489521..f59f7c17451c17 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -22,7 +22,7 @@ if (value == NULL) goto unbound_local_error; Py_INCREF(value); STACK_GROW(1); - POKE(1, value); + stack_pointer[-1] = value; DISPATCH(); } @@ -32,7 +32,7 @@ if (value == NULL) goto unbound_local_error; Py_INCREF(value); STACK_GROW(1); - POKE(1, value); + stack_pointer[-1] = value; DISPATCH(); } @@ -42,7 +42,7 @@ assert(value != NULL); Py_INCREF(value); STACK_GROW(1); - POKE(1, value); + stack_pointer[-1] = value; DISPATCH(); } @@ -52,12 +52,12 @@ value = GETITEM(consts, oparg); Py_INCREF(value); STACK_GROW(1); - POKE(1, value); + stack_pointer[-1] = value; DISPATCH(); } TARGET(STORE_FAST) { - PyObject *value = PEEK(1); + PyObject *value = stack_pointer[-1]; SETLOCAL(oparg, value); STACK_SHRINK(1); DISPATCH(); @@ -73,8 +73,7 @@ Py_INCREF(value); _tmp_2 = value; } - NEXTOPARG(); - JUMPBY(1); + oparg = (next_instr++)->op.arg; { PyObject *value; value = GETLOCAL(oparg); @@ -83,8 +82,8 @@ _tmp_1 = value; } STACK_GROW(2); - POKE(1, _tmp_1); - POKE(2, _tmp_2); + stack_pointer[-1] = _tmp_1; + stack_pointer[-2] = _tmp_2; DISPATCH(); } @@ -98,8 +97,7 @@ Py_INCREF(value); _tmp_2 = value; } - NEXTOPARG(); - JUMPBY(1); + oparg = (next_instr++)->op.arg; { PyObject *value; value = GETITEM(consts, oparg); @@ -107,19 +105,18 @@ _tmp_1 = value; } STACK_GROW(2); - POKE(1, _tmp_1); - POKE(2, _tmp_2); + stack_pointer[-1] = _tmp_1; + stack_pointer[-2] = _tmp_2; DISPATCH(); } TARGET(STORE_FAST__LOAD_FAST) { - PyObject *_tmp_1 = PEEK(1); + PyObject *_tmp_1 = stack_pointer[-1]; { PyObject *value = _tmp_1; SETLOCAL(oparg, value); } - NEXTOPARG(); - JUMPBY(1); + oparg = (next_instr++)->op.arg; { PyObject *value; value = GETLOCAL(oparg); @@ -127,19 +124,18 @@ Py_INCREF(value); _tmp_1 = value; } - POKE(1, _tmp_1); + stack_pointer[-1] = _tmp_1; DISPATCH(); } TARGET(STORE_FAST__STORE_FAST) { - PyObject *_tmp_1 = PEEK(1); - PyObject *_tmp_2 = PEEK(2); + PyObject *_tmp_1 = stack_pointer[-1]; + PyObject *_tmp_2 = stack_pointer[-2]; { PyObject *value = _tmp_1; SETLOCAL(oparg, value); } - NEXTOPARG(); - JUMPBY(1); + oparg = (next_instr++)->op.arg; { PyObject *value = _tmp_2; SETLOCAL(oparg, value); @@ -157,8 +153,7 @@ Py_INCREF(value); _tmp_2 = value; } - NEXTOPARG(); - JUMPBY(1); + oparg = (next_instr++)->op.arg; { PyObject *value; value = GETLOCAL(oparg); @@ -167,13 +162,13 @@ _tmp_1 = value; } STACK_GROW(2); - POKE(1, _tmp_1); - POKE(2, _tmp_2); + stack_pointer[-1] = _tmp_1; + stack_pointer[-2] = _tmp_2; DISPATCH(); } TARGET(POP_TOP) { - PyObject *value = PEEK(1); + PyObject *value = stack_pointer[-1]; Py_DECREF(value); STACK_SHRINK(1); DISPATCH(); @@ -183,13 +178,13 @@ PyObject *res; res = NULL; STACK_GROW(1); - POKE(1, res); + stack_pointer[-1] = res; DISPATCH(); } TARGET(END_FOR) { - PyObject *_tmp_1 = PEEK(1); - PyObject *_tmp_2 = PEEK(2); + PyObject *_tmp_1 = stack_pointer[-1]; + PyObject *_tmp_2 = stack_pointer[-2]; { PyObject *value = _tmp_1; Py_DECREF(value); @@ -203,17 +198,17 @@ } TARGET(UNARY_NEGATIVE) { - PyObject *value = PEEK(1); + PyObject *value = stack_pointer[-1]; PyObject *res; res = PyNumber_Negative(value); Py_DECREF(value); if (res == NULL) goto pop_1_error; - POKE(1, res); + stack_pointer[-1] = res; DISPATCH(); } TARGET(UNARY_NOT) { - PyObject *value = PEEK(1); + PyObject *value = stack_pointer[-1]; PyObject *res; int err = PyObject_IsTrue(value); Py_DECREF(value); @@ -225,23 +220,23 @@ res = Py_False; } Py_INCREF(res); - POKE(1, res); + stack_pointer[-1] = res; DISPATCH(); } TARGET(UNARY_INVERT) { - PyObject *value = PEEK(1); + PyObject *value = stack_pointer[-1]; PyObject *res; res = PyNumber_Invert(value); Py_DECREF(value); if (res == NULL) goto pop_1_error; - POKE(1, res); + stack_pointer[-1] = res; DISPATCH(); } TARGET(BINARY_OP_MULTIPLY_INT) { - PyObject *right = PEEK(1); - PyObject *left = PEEK(2); + PyObject *right = stack_pointer[-1]; + PyObject *left = stack_pointer[-2]; PyObject *prod; assert(cframe.use_tracing == 0); DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP); @@ -252,14 +247,14 @@ _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); if (prod == NULL) goto pop_2_error; STACK_SHRINK(1); - POKE(1, prod); - JUMPBY(1); + stack_pointer[-1] = prod; + next_instr += 1; DISPATCH(); } TARGET(BINARY_OP_MULTIPLY_FLOAT) { - PyObject *right = PEEK(1); - PyObject *left = PEEK(2); + PyObject *right = stack_pointer[-1]; + PyObject *left = stack_pointer[-2]; PyObject *prod; assert(cframe.use_tracing == 0); DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP); @@ -272,14 +267,14 @@ _Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc); if (prod == NULL) goto pop_2_error; STACK_SHRINK(1); - POKE(1, prod); - JUMPBY(1); + stack_pointer[-1] = prod; + next_instr += 1; DISPATCH(); } TARGET(BINARY_OP_SUBTRACT_INT) { - PyObject *right = PEEK(1); - PyObject *left = PEEK(2); + PyObject *right = stack_pointer[-1]; + PyObject *left = stack_pointer[-2]; PyObject *sub; assert(cframe.use_tracing == 0); DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP); @@ -290,14 +285,14 @@ _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); if (sub == NULL) goto pop_2_error; STACK_SHRINK(1); - POKE(1, sub); - JUMPBY(1); + stack_pointer[-1] = sub; + next_instr += 1; DISPATCH(); } TARGET(BINARY_OP_SUBTRACT_FLOAT) { - PyObject *right = PEEK(1); - PyObject *left = PEEK(2); + PyObject *right = stack_pointer[-1]; + PyObject *left = stack_pointer[-2]; PyObject *sub; assert(cframe.use_tracing == 0); DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP); @@ -309,14 +304,14 @@ _Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc); if (sub == NULL) goto pop_2_error; STACK_SHRINK(1); - POKE(1, sub); - JUMPBY(1); + stack_pointer[-1] = sub; + next_instr += 1; DISPATCH(); } TARGET(BINARY_OP_ADD_UNICODE) { - PyObject *right = PEEK(1); - PyObject *left = PEEK(2); + PyObject *right = stack_pointer[-1]; + PyObject *left = stack_pointer[-2]; PyObject *res; assert(cframe.use_tracing == 0); DEOPT_IF(!PyUnicode_CheckExact(left), BINARY_OP); @@ -327,14 +322,14 @@ _Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc); if (res == NULL) goto pop_2_error; STACK_SHRINK(1); - POKE(1, res); - JUMPBY(1); + stack_pointer[-1] = res; + next_instr += 1; DISPATCH(); } TARGET(BINARY_OP_INPLACE_ADD_UNICODE) { - PyObject *right = PEEK(1); - PyObject *left = PEEK(2); + PyObject *right = stack_pointer[-1]; + PyObject *left = stack_pointer[-2]; assert(cframe.use_tracing == 0); DEOPT_IF(!PyUnicode_CheckExact(left), BINARY_OP); DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP); @@ -367,8 +362,8 @@ } TARGET(BINARY_OP_ADD_FLOAT) { - PyObject *right = PEEK(1); - PyObject *left = PEEK(2); + PyObject *right = stack_pointer[-1]; + PyObject *left = stack_pointer[-2]; PyObject *sum; assert(cframe.use_tracing == 0); DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP); @@ -381,14 +376,14 @@ _Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc); if (sum == NULL) goto pop_2_error; STACK_SHRINK(1); - POKE(1, sum); - JUMPBY(1); + stack_pointer[-1] = sum; + next_instr += 1; DISPATCH(); } TARGET(BINARY_OP_ADD_INT) { - PyObject *right = PEEK(1); - PyObject *left = PEEK(2); + PyObject *right = stack_pointer[-1]; + PyObject *left = stack_pointer[-2]; PyObject *sum; assert(cframe.use_tracing == 0); DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP); @@ -399,16 +394,16 @@ _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); if (sum == NULL) goto pop_2_error; STACK_SHRINK(1); - POKE(1, sum); - JUMPBY(1); + stack_pointer[-1] = sum; + next_instr += 1; DISPATCH(); } TARGET(BINARY_SUBSCR) { PREDICTED(BINARY_SUBSCR); static_assert(INLINE_CACHE_ENTRIES_BINARY_SUBSCR == 4, "incorrect cache size"); - PyObject *sub = PEEK(1); - PyObject *container = PEEK(2); + PyObject *sub = stack_pointer[-1]; + PyObject *container = stack_pointer[-2]; PyObject *res; #if ENABLE_SPECIALIZATION _PyBinarySubscrCache *cache = (_PyBinarySubscrCache *)next_instr; @@ -426,15 +421,15 @@ Py_DECREF(sub); if (res == NULL) goto pop_2_error; STACK_SHRINK(1); - POKE(1, res); - JUMPBY(4); + stack_pointer[-1] = res; + next_instr += 4; DISPATCH(); } TARGET(BINARY_SLICE) { - PyObject *stop = PEEK(1); - PyObject *start = PEEK(2); - PyObject *container = PEEK(3); + PyObject *stop = stack_pointer[-1]; + PyObject *start = stack_pointer[-2]; + PyObject *container = stack_pointer[-3]; PyObject *res; PyObject *slice = _PyBuildSlice_ConsumeRefs(start, stop); // Can't use ERROR_IF() here, because we haven't @@ -449,15 +444,15 @@ Py_DECREF(container); if (res == NULL) goto pop_3_error; STACK_SHRINK(2); - POKE(1, res); + stack_pointer[-1] = res; DISPATCH(); } TARGET(STORE_SLICE) { - PyObject *stop = PEEK(1); - PyObject *start = PEEK(2); - PyObject *container = PEEK(3); - PyObject *v = PEEK(4); + PyObject *stop = stack_pointer[-1]; + PyObject *start = stack_pointer[-2]; + PyObject *container = stack_pointer[-3]; + PyObject *v = stack_pointer[-4]; PyObject *slice = _PyBuildSlice_ConsumeRefs(start, stop); int err; if (slice == NULL) { @@ -475,8 +470,8 @@ } TARGET(BINARY_SUBSCR_LIST_INT) { - PyObject *sub = PEEK(1); - PyObject *list = PEEK(2); + PyObject *sub = stack_pointer[-1]; + PyObject *list = stack_pointer[-2]; PyObject *res; assert(cframe.use_tracing == 0); DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR); @@ -494,14 +489,14 @@ _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free); Py_DECREF(list); STACK_SHRINK(1); - POKE(1, res); - JUMPBY(4); + stack_pointer[-1] = res; + next_instr += 4; DISPATCH(); } TARGET(BINARY_SUBSCR_TUPLE_INT) { - PyObject *sub = PEEK(1); - PyObject *tuple = PEEK(2); + PyObject *sub = stack_pointer[-1]; + PyObject *tuple = stack_pointer[-2]; PyObject *res; assert(cframe.use_tracing == 0); DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR); @@ -519,14 +514,14 @@ _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free); Py_DECREF(tuple); STACK_SHRINK(1); - POKE(1, res); - JUMPBY(4); + stack_pointer[-1] = res; + next_instr += 4; DISPATCH(); } TARGET(BINARY_SUBSCR_DICT) { - PyObject *sub = PEEK(1); - PyObject *dict = PEEK(2); + PyObject *sub = stack_pointer[-1]; + PyObject *dict = stack_pointer[-2]; PyObject *res; assert(cframe.use_tracing == 0); DEOPT_IF(!PyDict_CheckExact(dict), BINARY_SUBSCR); @@ -544,14 +539,14 @@ Py_DECREF(dict); Py_DECREF(sub); STACK_SHRINK(1); - POKE(1, res); - JUMPBY(4); + stack_pointer[-1] = res; + next_instr += 4; DISPATCH(); } TARGET(BINARY_SUBSCR_GETITEM) { - PyObject *sub = PEEK(1); - PyObject *container = PEEK(2); + PyObject *sub = stack_pointer[-1]; + PyObject *container = stack_pointer[-2]; uint32_t type_version = read_u32(&next_instr[1].cache); uint16_t func_version = read_u16(&next_instr[3].cache); PyTypeObject *tp = Py_TYPE(container); @@ -575,8 +570,8 @@ } TARGET(LIST_APPEND) { - PyObject *v = PEEK(1); - PyObject *list = PEEK(2 + (oparg-1)); + PyObject *v = stack_pointer[-1]; + PyObject *list = stack_pointer[-(2 + (oparg-1))]; if (_PyList_AppendTakeRef((PyListObject *)list, v) < 0) goto pop_1_error; STACK_SHRINK(1); PREDICT(JUMP_BACKWARD); @@ -584,8 +579,8 @@ } TARGET(SET_ADD) { - PyObject *v = PEEK(1); - PyObject *set = PEEK(2 + (oparg-1)); + PyObject *v = stack_pointer[-1]; + PyObject *set = stack_pointer[-(2 + (oparg-1))]; int err = PySet_Add(set, v); Py_DECREF(v); if (err) goto pop_1_error; @@ -597,9 +592,9 @@ TARGET(STORE_SUBSCR) { PREDICTED(STORE_SUBSCR); static_assert(INLINE_CACHE_ENTRIES_STORE_SUBSCR == 1, "incorrect cache size"); - PyObject *sub = PEEK(1); - PyObject *container = PEEK(2); - PyObject *v = PEEK(3); + PyObject *sub = stack_pointer[-1]; + PyObject *container = stack_pointer[-2]; + PyObject *v = stack_pointer[-3]; uint16_t counter = read_u16(&next_instr[0].cache); #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { @@ -621,14 +616,14 @@ Py_DECREF(sub); if (err) goto pop_3_error; STACK_SHRINK(3); - JUMPBY(1); + next_instr += 1; DISPATCH(); } TARGET(STORE_SUBSCR_LIST_INT) { - PyObject *sub = PEEK(1); - PyObject *list = PEEK(2); - PyObject *value = PEEK(3); + PyObject *sub = stack_pointer[-1]; + PyObject *list = stack_pointer[-2]; + PyObject *value = stack_pointer[-3]; assert(cframe.use_tracing == 0); DEOPT_IF(!PyLong_CheckExact(sub), STORE_SUBSCR); DEOPT_IF(!PyList_CheckExact(list), STORE_SUBSCR); @@ -647,14 +642,14 @@ _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free); Py_DECREF(list); STACK_SHRINK(3); - JUMPBY(1); + next_instr += 1; DISPATCH(); } TARGET(STORE_SUBSCR_DICT) { - PyObject *sub = PEEK(1); - PyObject *dict = PEEK(2); - PyObject *value = PEEK(3); + PyObject *sub = stack_pointer[-1]; + PyObject *dict = stack_pointer[-2]; + PyObject *value = stack_pointer[-3]; assert(cframe.use_tracing == 0); DEOPT_IF(!PyDict_CheckExact(dict), STORE_SUBSCR); STAT_INC(STORE_SUBSCR, hit); @@ -662,13 +657,13 @@ Py_DECREF(dict); if (err) goto pop_3_error; STACK_SHRINK(3); - JUMPBY(1); + next_instr += 1; DISPATCH(); } TARGET(DELETE_SUBSCR) { - PyObject *sub = PEEK(1); - PyObject *container = PEEK(2); + PyObject *sub = stack_pointer[-1]; + PyObject *container = stack_pointer[-2]; /* del container[sub] */ int err = PyObject_DelItem(container, sub); Py_DECREF(container); @@ -679,19 +674,19 @@ } TARGET(CALL_INTRINSIC_1) { - PyObject *value = PEEK(1); + PyObject *value = stack_pointer[-1]; PyObject *res; assert(oparg <= MAX_INTRINSIC_1); res = _PyIntrinsics_UnaryFunctions[oparg](tstate, value); Py_DECREF(value); if (res == NULL) goto pop_1_error; - POKE(1, res); + stack_pointer[-1] = res; DISPATCH(); } TARGET(CALL_INTRINSIC_2) { - PyObject *value1 = PEEK(1); - PyObject *value2 = PEEK(2); + PyObject *value1 = stack_pointer[-1]; + PyObject *value2 = stack_pointer[-2]; PyObject *res; assert(oparg <= MAX_INTRINSIC_2); res = _PyIntrinsics_BinaryFunctions[oparg](tstate, value2, value1); @@ -699,12 +694,12 @@ Py_DECREF(value1); if (res == NULL) goto pop_2_error; STACK_SHRINK(1); - POKE(1, res); + stack_pointer[-1] = res; DISPATCH(); } TARGET(RAISE_VARARGS) { - PyObject **args = &PEEK(oparg); + PyObject **args = (stack_pointer - oparg); PyObject *cause = NULL, *exc = NULL; switch (oparg) { case 2: @@ -725,7 +720,7 @@ } TARGET(INTERPRETER_EXIT) { - PyObject *retval = PEEK(1); + PyObject *retval = stack_pointer[-1]; assert(frame == &entry_frame); assert(_PyFrame_IsIncomplete(frame)); STACK_SHRINK(1); // Since we're not going to DISPATCH() @@ -740,7 +735,7 @@ } TARGET(RETURN_VALUE) { - PyObject *retval = PEEK(1); + PyObject *retval = stack_pointer[-1]; STACK_SHRINK(1); assert(EMPTY()); _PyFrame_SetStackPointer(frame, stack_pointer); @@ -774,7 +769,7 @@ } TARGET(GET_AITER) { - PyObject *obj = PEEK(1); + PyObject *obj = stack_pointer[-1]; PyObject *iter; unaryfunc getter = NULL; PyTypeObject *type = Py_TYPE(obj); @@ -806,12 +801,12 @@ Py_DECREF(iter); if (true) goto pop_1_error; } - POKE(1, iter); + stack_pointer[-1] = iter; DISPATCH(); } TARGET(GET_ANEXT) { - PyObject *aiter = PEEK(1); + PyObject *aiter = stack_pointer[-1]; PyObject *awaitable; unaryfunc getter = NULL; PyObject *next_iter = NULL; @@ -857,14 +852,14 @@ } STACK_GROW(1); - POKE(1, awaitable); + stack_pointer[-1] = awaitable; PREDICT(LOAD_CONST); DISPATCH(); } TARGET(GET_AWAITABLE) { PREDICTED(GET_AWAITABLE); - PyObject *iterable = PEEK(1); + PyObject *iterable = stack_pointer[-1]; PyObject *iter; iter = _PyCoro_GetAwaitableIter(iterable); @@ -890,15 +885,15 @@ if (iter == NULL) goto pop_1_error; - POKE(1, iter); + stack_pointer[-1] = iter; PREDICT(LOAD_CONST); DISPATCH(); } TARGET(SEND) { PREDICTED(SEND); - PyObject *v = PEEK(1); - PyObject *receiver = PEEK(2); + PyObject *v = stack_pointer[-1]; + PyObject *receiver = stack_pointer[-2]; PyObject *retval; #if ENABLE_SPECIALIZATION _PySendCache *cache = (_PySendCache *)next_instr; @@ -935,14 +930,14 @@ assert(retval != NULL); } Py_DECREF(v); - POKE(1, retval); - JUMPBY(1); + stack_pointer[-1] = retval; + next_instr += 1; DISPATCH(); } TARGET(SEND_GEN) { - PyObject *v = PEEK(1); - PyObject *receiver = PEEK(2); + PyObject *v = stack_pointer[-1]; + PyObject *receiver = stack_pointer[-2]; assert(cframe.use_tracing == 0); PyGenObject *gen = (PyGenObject *)receiver; DEOPT_IF(Py_TYPE(gen) != &PyGen_Type && @@ -961,7 +956,7 @@ } TARGET(YIELD_VALUE) { - PyObject *retval = PEEK(1); + PyObject *retval = stack_pointer[-1]; // NOTE: It's important that YIELD_VALUE never raises an exception! // The compiler treats any exception raised here as a failed close() // or throw() call. @@ -983,7 +978,7 @@ } TARGET(POP_EXCEPT) { - PyObject *exc_value = PEEK(1); + PyObject *exc_value = stack_pointer[-1]; _PyErr_StackItem *exc_info = tstate->exc_info; Py_XSETREF(exc_info->exc_value, exc_value); STACK_SHRINK(1); @@ -991,8 +986,8 @@ } TARGET(RERAISE) { - PyObject *exc = PEEK(1); - PyObject **values = &PEEK(1 + oparg); + PyObject *exc = stack_pointer[-1]; + PyObject **values = (stack_pointer - (1 + oparg)); assert(oparg >= 0 && oparg <= 2); if (oparg) { PyObject *lasti = values[0]; @@ -1015,8 +1010,8 @@ } TARGET(END_ASYNC_FOR) { - PyObject *exc = PEEK(1); - PyObject *awaitable = PEEK(2); + PyObject *exc = stack_pointer[-1]; + PyObject *awaitable = stack_pointer[-2]; assert(exc && PyExceptionInstance_Check(exc)); if (PyErr_GivenExceptionMatches(exc, PyExc_StopAsyncIteration)) { Py_DECREF(awaitable); @@ -1034,9 +1029,9 @@ } TARGET(CLEANUP_THROW) { - PyObject *exc_value = PEEK(1); - PyObject *last_sent_val = PEEK(2); - PyObject *sub_iter = PEEK(3); + PyObject *exc_value = stack_pointer[-1]; + PyObject *last_sent_val = stack_pointer[-2]; + PyObject *sub_iter = stack_pointer[-3]; PyObject *none; PyObject *value; assert(throwflag); @@ -1053,8 +1048,8 @@ goto exception_unwind; } STACK_SHRINK(1); - POKE(1, value); - POKE(2, none); + stack_pointer[-1] = value; + stack_pointer[-2] = none; DISPATCH(); } @@ -1062,7 +1057,7 @@ PyObject *value; value = Py_NewRef(PyExc_AssertionError); STACK_GROW(1); - POKE(1, value); + stack_pointer[-1] = value; DISPATCH(); } @@ -1090,12 +1085,12 @@ } } STACK_GROW(1); - POKE(1, bc); + stack_pointer[-1] = bc; DISPATCH(); } TARGET(STORE_NAME) { - PyObject *v = PEEK(1); + PyObject *v = stack_pointer[-1]; PyObject *name = GETITEM(names, oparg); PyObject *ns = LOCALS(); int err; @@ -1138,7 +1133,7 @@ TARGET(UNPACK_SEQUENCE) { PREDICTED(UNPACK_SEQUENCE); static_assert(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE == 1, "incorrect cache size"); - PyObject *seq = PEEK(1); + PyObject *seq = stack_pointer[-1]; #if ENABLE_SPECIALIZATION _PyUnpackSequenceCache *cache = (_PyUnpackSequenceCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { @@ -1156,12 +1151,12 @@ if (res == 0) goto pop_1_error; STACK_SHRINK(1); STACK_GROW(oparg); - JUMPBY(1); + next_instr += 1; DISPATCH(); } TARGET(UNPACK_SEQUENCE_TWO_TUPLE) { - PyObject *seq = PEEK(1); + PyObject *seq = stack_pointer[-1]; PyObject **values = stack_pointer - (1); DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE); DEOPT_IF(PyTuple_GET_SIZE(seq) != 2, UNPACK_SEQUENCE); @@ -1172,12 +1167,12 @@ Py_DECREF(seq); STACK_SHRINK(1); STACK_GROW(oparg); - JUMPBY(1); + next_instr += 1; DISPATCH(); } TARGET(UNPACK_SEQUENCE_TUPLE) { - PyObject *seq = PEEK(1); + PyObject *seq = stack_pointer[-1]; PyObject **values = stack_pointer - (1); DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE); DEOPT_IF(PyTuple_GET_SIZE(seq) != oparg, UNPACK_SEQUENCE); @@ -1189,12 +1184,12 @@ Py_DECREF(seq); STACK_SHRINK(1); STACK_GROW(oparg); - JUMPBY(1); + next_instr += 1; DISPATCH(); } TARGET(UNPACK_SEQUENCE_LIST) { - PyObject *seq = PEEK(1); + PyObject *seq = stack_pointer[-1]; PyObject **values = stack_pointer - (1); DEOPT_IF(!PyList_CheckExact(seq), UNPACK_SEQUENCE); DEOPT_IF(PyList_GET_SIZE(seq) != oparg, UNPACK_SEQUENCE); @@ -1206,12 +1201,12 @@ Py_DECREF(seq); STACK_SHRINK(1); STACK_GROW(oparg); - JUMPBY(1); + next_instr += 1; DISPATCH(); } TARGET(UNPACK_EX) { - PyObject *seq = PEEK(1); + PyObject *seq = stack_pointer[-1]; int totalargs = 1 + (oparg & 0xFF) + (oparg >> 8); PyObject **top = stack_pointer + totalargs - 1; int res = unpack_iterable(tstate, seq, oparg & 0xFF, oparg >> 8, top); @@ -1224,8 +1219,8 @@ TARGET(STORE_ATTR) { PREDICTED(STORE_ATTR); static_assert(INLINE_CACHE_ENTRIES_STORE_ATTR == 4, "incorrect cache size"); - PyObject *owner = PEEK(1); - PyObject *v = PEEK(2); + PyObject *owner = stack_pointer[-1]; + PyObject *v = stack_pointer[-2]; uint16_t counter = read_u16(&next_instr[0].cache); #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { @@ -1247,12 +1242,12 @@ Py_DECREF(owner); if (err) goto pop_2_error; STACK_SHRINK(2); - JUMPBY(4); + next_instr += 4; DISPATCH(); } TARGET(DELETE_ATTR) { - PyObject *owner = PEEK(1); + PyObject *owner = stack_pointer[-1]; PyObject *name = GETITEM(names, oparg); int err = PyObject_SetAttr(owner, name, (PyObject *)NULL); Py_DECREF(owner); @@ -1262,7 +1257,7 @@ } TARGET(STORE_GLOBAL) { - PyObject *v = PEEK(1); + PyObject *v = stack_pointer[-1]; PyObject *name = GETITEM(names, oparg); int err = PyDict_SetItem(GLOBALS(), name, v); Py_DECREF(v); @@ -1347,7 +1342,7 @@ } } STACK_GROW(1); - POKE(1, v); + stack_pointer[-1] = v; DISPATCH(); } @@ -1410,9 +1405,9 @@ null = NULL; STACK_GROW(1); STACK_GROW(((oparg & 1) ? 1 : 0)); - POKE(1, v); - if (oparg & 1) { POKE(1 + ((oparg & 1) ? 1 : 0), null); } - JUMPBY(5); + stack_pointer[-1] = v; + if (oparg & 1) { stack_pointer[-(1 + ((oparg & 1) ? 1 : 0))] = null; } + next_instr += 5; DISPATCH(); } @@ -1434,9 +1429,9 @@ null = NULL; STACK_GROW(1); STACK_GROW(((oparg & 1) ? 1 : 0)); - POKE(1, res); - if (oparg & 1) { POKE(1 + ((oparg & 1) ? 1 : 0), null); } - JUMPBY(5); + stack_pointer[-1] = res; + if (oparg & 1) { stack_pointer[-(1 + ((oparg & 1) ? 1 : 0))] = null; } + next_instr += 5; DISPATCH(); } @@ -1462,9 +1457,9 @@ null = NULL; STACK_GROW(1); STACK_GROW(((oparg & 1) ? 1 : 0)); - POKE(1, res); - if (oparg & 1) { POKE(1 + ((oparg & 1) ? 1 : 0), null); } - JUMPBY(5); + stack_pointer[-1] = res; + if (oparg & 1) { stack_pointer[-(1 + ((oparg & 1) ? 1 : 0))] = null; } + next_instr += 5; DISPATCH(); } @@ -1535,7 +1530,7 @@ Py_INCREF(value); } STACK_GROW(1); - POKE(1, value); + stack_pointer[-1] = value; DISPATCH(); } @@ -1549,12 +1544,12 @@ } Py_INCREF(value); STACK_GROW(1); - POKE(1, value); + stack_pointer[-1] = value; DISPATCH(); } TARGET(STORE_DEREF) { - PyObject *v = PEEK(1); + PyObject *v = stack_pointer[-1]; PyObject *cell = GETLOCAL(oparg); PyObject *oldobj = PyCell_GET(cell); PyCell_SET(cell, v); @@ -1578,7 +1573,7 @@ } TARGET(BUILD_STRING) { - PyObject **pieces = &PEEK(oparg); + PyObject **pieces = (stack_pointer - oparg); PyObject *str; str = _PyUnicode_JoinArray(&_Py_STR(empty), pieces, oparg); for (int i = 0; i < oparg; i++) { @@ -1587,35 +1582,35 @@ if (str == NULL) { STACK_SHRINK(oparg); goto error; } STACK_SHRINK(oparg); STACK_GROW(1); - POKE(1, str); + stack_pointer[-1] = str; DISPATCH(); } TARGET(BUILD_TUPLE) { - PyObject **values = &PEEK(oparg); + PyObject **values = (stack_pointer - oparg); PyObject *tup; tup = _PyTuple_FromArraySteal(values, oparg); if (tup == NULL) { STACK_SHRINK(oparg); goto error; } STACK_SHRINK(oparg); STACK_GROW(1); - POKE(1, tup); + stack_pointer[-1] = tup; DISPATCH(); } TARGET(BUILD_LIST) { - PyObject **values = &PEEK(oparg); + PyObject **values = (stack_pointer - oparg); PyObject *list; list = _PyList_FromArraySteal(values, oparg); if (list == NULL) { STACK_SHRINK(oparg); goto error; } STACK_SHRINK(oparg); STACK_GROW(1); - POKE(1, list); + stack_pointer[-1] = list; DISPATCH(); } TARGET(LIST_EXTEND) { - PyObject *iterable = PEEK(1); - PyObject *list = PEEK(2 + (oparg-1)); + PyObject *iterable = stack_pointer[-1]; + PyObject *list = stack_pointer[-(2 + (oparg-1))]; PyObject *none_val = _PyList_Extend((PyListObject *)list, iterable); if (none_val == NULL) { if (_PyErr_ExceptionMatches(tstate, PyExc_TypeError) && @@ -1636,8 +1631,8 @@ } TARGET(SET_UPDATE) { - PyObject *iterable = PEEK(1); - PyObject *set = PEEK(2 + (oparg-1)); + PyObject *iterable = stack_pointer[-1]; + PyObject *set = stack_pointer[-(2 + (oparg-1))]; int err = _PySet_Update(set, iterable); Py_DECREF(iterable); if (err < 0) goto pop_1_error; @@ -1646,7 +1641,7 @@ } TARGET(BUILD_SET) { - PyObject **values = &PEEK(oparg); + PyObject **values = (stack_pointer - oparg); PyObject *set; set = PySet_New(NULL); if (set == NULL) @@ -1664,12 +1659,12 @@ } STACK_SHRINK(oparg); STACK_GROW(1); - POKE(1, set); + stack_pointer[-1] = set; DISPATCH(); } TARGET(BUILD_MAP) { - PyObject **values = &PEEK(oparg*2); + PyObject **values = (stack_pointer - oparg*2); PyObject *map; map = _PyDict_FromItems( values, 2, @@ -1685,7 +1680,7 @@ if (map == NULL) { STACK_SHRINK(oparg*2); goto error; } STACK_SHRINK(oparg*2); STACK_GROW(1); - POKE(1, map); + stack_pointer[-1] = map; DISPATCH(); } @@ -1733,8 +1728,8 @@ } TARGET(BUILD_CONST_KEY_MAP) { - PyObject *keys = PEEK(1); - PyObject **values = &PEEK(1 + oparg); + PyObject *keys = stack_pointer[-1]; + PyObject **values = (stack_pointer - (1 + oparg)); PyObject *map; if (!PyTuple_CheckExact(keys) || PyTuple_GET_SIZE(keys) != (Py_ssize_t)oparg) { @@ -1751,12 +1746,12 @@ } if (map == NULL) { STACK_SHRINK(oparg); goto pop_1_error; } STACK_SHRINK(oparg); - POKE(1, map); + stack_pointer[-1] = map; DISPATCH(); } TARGET(DICT_UPDATE) { - PyObject *update = PEEK(1); + PyObject *update = stack_pointer[-1]; PyObject *dict = PEEK(oparg + 1); // update is still on the stack if (PyDict_Update(dict, update) < 0) { if (_PyErr_ExceptionMatches(tstate, PyExc_AttributeError)) { @@ -1773,7 +1768,7 @@ } TARGET(DICT_MERGE) { - PyObject *update = PEEK(1); + PyObject *update = stack_pointer[-1]; PyObject *dict = PEEK(oparg + 1); // update is still on the stack if (_PyDict_MergeEx(dict, update, 2) < 0) { @@ -1788,8 +1783,8 @@ } TARGET(MAP_ADD) { - PyObject *value = PEEK(1); - PyObject *key = PEEK(2); + PyObject *value = stack_pointer[-1]; + PyObject *key = stack_pointer[-2]; PyObject *dict = PEEK(oparg + 2); // key, value are still on the stack assert(PyDict_CheckExact(dict)); /* dict[key] = value */ @@ -1803,7 +1798,7 @@ TARGET(LOAD_ATTR) { PREDICTED(LOAD_ATTR); static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); - PyObject *owner = PEEK(1); + PyObject *owner = stack_pointer[-1]; PyObject *res2 = NULL; PyObject *res; #if ENABLE_SPECIALIZATION @@ -1853,14 +1848,14 @@ if (res == NULL) goto pop_1_error; } STACK_GROW(((oparg & 1) ? 1 : 0)); - POKE(1, res); - if (oparg & 1) { POKE(1 + ((oparg & 1) ? 1 : 0), res2); } - JUMPBY(9); + stack_pointer[-1] = res; + if (oparg & 1) { stack_pointer[-(1 + ((oparg & 1) ? 1 : 0))] = res2; } + next_instr += 9; DISPATCH(); } TARGET(LOAD_ATTR_INSTANCE_VALUE) { - PyObject *owner = PEEK(1); + PyObject *owner = stack_pointer[-1]; PyObject *res2 = NULL; PyObject *res; uint32_t type_version = read_u32(&next_instr[1].cache); @@ -1880,14 +1875,14 @@ res2 = NULL; Py_DECREF(owner); STACK_GROW(((oparg & 1) ? 1 : 0)); - POKE(1, res); - if (oparg & 1) { POKE(1 + ((oparg & 1) ? 1 : 0), res2); } - JUMPBY(9); + stack_pointer[-1] = res; + if (oparg & 1) { stack_pointer[-(1 + ((oparg & 1) ? 1 : 0))] = res2; } + next_instr += 9; DISPATCH(); } TARGET(LOAD_ATTR_MODULE) { - PyObject *owner = PEEK(1); + PyObject *owner = stack_pointer[-1]; PyObject *res2 = NULL; PyObject *res; uint32_t type_version = read_u32(&next_instr[1].cache); @@ -1907,14 +1902,14 @@ res2 = NULL; Py_DECREF(owner); STACK_GROW(((oparg & 1) ? 1 : 0)); - POKE(1, res); - if (oparg & 1) { POKE(1 + ((oparg & 1) ? 1 : 0), res2); } - JUMPBY(9); + stack_pointer[-1] = res; + if (oparg & 1) { stack_pointer[-(1 + ((oparg & 1) ? 1 : 0))] = res2; } + next_instr += 9; DISPATCH(); } TARGET(LOAD_ATTR_WITH_HINT) { - PyObject *owner = PEEK(1); + PyObject *owner = stack_pointer[-1]; PyObject *res2 = NULL; PyObject *res; uint32_t type_version = read_u32(&next_instr[1].cache); @@ -1948,14 +1943,14 @@ res2 = NULL; Py_DECREF(owner); STACK_GROW(((oparg & 1) ? 1 : 0)); - POKE(1, res); - if (oparg & 1) { POKE(1 + ((oparg & 1) ? 1 : 0), res2); } - JUMPBY(9); + stack_pointer[-1] = res; + if (oparg & 1) { stack_pointer[-(1 + ((oparg & 1) ? 1 : 0))] = res2; } + next_instr += 9; DISPATCH(); } TARGET(LOAD_ATTR_SLOT) { - PyObject *owner = PEEK(1); + PyObject *owner = stack_pointer[-1]; PyObject *res2 = NULL; PyObject *res; uint32_t type_version = read_u32(&next_instr[1].cache); @@ -1972,14 +1967,14 @@ res2 = NULL; Py_DECREF(owner); STACK_GROW(((oparg & 1) ? 1 : 0)); - POKE(1, res); - if (oparg & 1) { POKE(1 + ((oparg & 1) ? 1 : 0), res2); } - JUMPBY(9); + stack_pointer[-1] = res; + if (oparg & 1) { stack_pointer[-(1 + ((oparg & 1) ? 1 : 0))] = res2; } + next_instr += 9; DISPATCH(); } TARGET(LOAD_ATTR_CLASS) { - PyObject *cls = PEEK(1); + PyObject *cls = stack_pointer[-1]; PyObject *res2 = NULL; PyObject *res; uint32_t type_version = read_u32(&next_instr[1].cache); @@ -1998,14 +1993,14 @@ Py_INCREF(res); Py_DECREF(cls); STACK_GROW(((oparg & 1) ? 1 : 0)); - POKE(1, res); - if (oparg & 1) { POKE(1 + ((oparg & 1) ? 1 : 0), res2); } - JUMPBY(9); + stack_pointer[-1] = res; + if (oparg & 1) { stack_pointer[-(1 + ((oparg & 1) ? 1 : 0))] = res2; } + next_instr += 9; DISPATCH(); } TARGET(LOAD_ATTR_PROPERTY) { - PyObject *owner = PEEK(1); + PyObject *owner = stack_pointer[-1]; uint32_t type_version = read_u32(&next_instr[1].cache); uint32_t func_version = read_u32(&next_instr[3].cache); PyObject *fget = read_obj(&next_instr[5].cache); @@ -2035,7 +2030,7 @@ } TARGET(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN) { - PyObject *owner = PEEK(1); + PyObject *owner = stack_pointer[-1]; uint32_t type_version = read_u32(&next_instr[1].cache); uint32_t func_version = read_u32(&next_instr[3].cache); PyObject *getattribute = read_obj(&next_instr[5].cache); @@ -2067,8 +2062,8 @@ } TARGET(STORE_ATTR_INSTANCE_VALUE) { - PyObject *owner = PEEK(1); - PyObject *value = PEEK(2); + PyObject *owner = stack_pointer[-1]; + PyObject *value = stack_pointer[-2]; uint32_t type_version = read_u32(&next_instr[1].cache); uint16_t index = read_u16(&next_instr[3].cache); assert(cframe.use_tracing == 0); @@ -2090,13 +2085,13 @@ } Py_DECREF(owner); STACK_SHRINK(2); - JUMPBY(4); + next_instr += 4; DISPATCH(); } TARGET(STORE_ATTR_WITH_HINT) { - PyObject *owner = PEEK(1); - PyObject *value = PEEK(2); + PyObject *owner = stack_pointer[-1]; + PyObject *value = stack_pointer[-2]; uint32_t type_version = read_u32(&next_instr[1].cache); uint16_t hint = read_u16(&next_instr[3].cache); assert(cframe.use_tracing == 0); @@ -2139,13 +2134,13 @@ dict->ma_version_tag = new_version; Py_DECREF(owner); STACK_SHRINK(2); - JUMPBY(4); + next_instr += 4; DISPATCH(); } TARGET(STORE_ATTR_SLOT) { - PyObject *owner = PEEK(1); - PyObject *value = PEEK(2); + PyObject *owner = stack_pointer[-1]; + PyObject *value = stack_pointer[-2]; uint32_t type_version = read_u32(&next_instr[1].cache); uint16_t index = read_u16(&next_instr[3].cache); assert(cframe.use_tracing == 0); @@ -2159,13 +2154,13 @@ Py_XDECREF(old_value); Py_DECREF(owner); STACK_SHRINK(2); - JUMPBY(4); + next_instr += 4; DISPATCH(); } TARGET(COMPARE_OP) { - PyObject *right = PEEK(1); - PyObject *left = PEEK(2); + PyObject *right = stack_pointer[-1]; + PyObject *left = stack_pointer[-2]; PyObject *res; STAT_INC(COMPARE_OP, deferred); assert((oparg >> 4) <= Py_GE); @@ -2174,15 +2169,15 @@ Py_DECREF(right); if (res == NULL) goto pop_2_error; STACK_SHRINK(1); - POKE(1, res); - JUMPBY(1); + stack_pointer[-1] = res; + next_instr += 1; DISPATCH(); } TARGET(COMPARE_AND_BRANCH) { PREDICTED(COMPARE_AND_BRANCH); - PyObject *right = PEEK(1); - PyObject *left = PEEK(2); + PyObject *right = stack_pointer[-1]; + PyObject *left = stack_pointer[-2]; #if ENABLE_SPECIALIZATION _PyCompareOpCache *cache = (_PyCompareOpCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { @@ -2210,13 +2205,13 @@ JUMPBY(offset); } STACK_SHRINK(2); - JUMPBY(2); + next_instr += 2; DISPATCH(); } TARGET(COMPARE_AND_BRANCH_FLOAT) { - PyObject *right = PEEK(1); - PyObject *left = PEEK(2); + PyObject *right = stack_pointer[-1]; + PyObject *left = stack_pointer[-2]; assert(cframe.use_tracing == 0); DEOPT_IF(!PyFloat_CheckExact(left), COMPARE_AND_BRANCH); DEOPT_IF(!PyFloat_CheckExact(right), COMPARE_AND_BRANCH); @@ -2232,13 +2227,13 @@ JUMPBY(offset); } STACK_SHRINK(2); - JUMPBY(2); + next_instr += 2; DISPATCH(); } TARGET(COMPARE_AND_BRANCH_INT) { - PyObject *right = PEEK(1); - PyObject *left = PEEK(2); + PyObject *right = stack_pointer[-1]; + PyObject *left = stack_pointer[-2]; assert(cframe.use_tracing == 0); DEOPT_IF(!PyLong_CheckExact(left), COMPARE_AND_BRANCH); DEOPT_IF(!PyLong_CheckExact(right), COMPARE_AND_BRANCH); @@ -2257,13 +2252,13 @@ JUMPBY(offset); } STACK_SHRINK(2); - JUMPBY(2); + next_instr += 2; DISPATCH(); } TARGET(COMPARE_AND_BRANCH_STR) { - PyObject *right = PEEK(1); - PyObject *left = PEEK(2); + PyObject *right = stack_pointer[-1]; + PyObject *left = stack_pointer[-2]; assert(cframe.use_tracing == 0); DEOPT_IF(!PyUnicode_CheckExact(left), COMPARE_AND_BRANCH); DEOPT_IF(!PyUnicode_CheckExact(right), COMPARE_AND_BRANCH); @@ -2280,26 +2275,26 @@ JUMPBY(offset); } STACK_SHRINK(2); - JUMPBY(2); + next_instr += 2; DISPATCH(); } TARGET(IS_OP) { - PyObject *right = PEEK(1); - PyObject *left = PEEK(2); + PyObject *right = stack_pointer[-1]; + PyObject *left = stack_pointer[-2]; PyObject *b; int res = Py_Is(left, right) ^ oparg; Py_DECREF(left); Py_DECREF(right); b = Py_NewRef(res ? Py_True : Py_False); STACK_SHRINK(1); - POKE(1, b); + stack_pointer[-1] = b; DISPATCH(); } TARGET(CONTAINS_OP) { - PyObject *right = PEEK(1); - PyObject *left = PEEK(2); + PyObject *right = stack_pointer[-1]; + PyObject *left = stack_pointer[-2]; PyObject *b; int res = PySequence_Contains(right, left); Py_DECREF(left); @@ -2307,13 +2302,13 @@ if (res < 0) goto pop_2_error; b = Py_NewRef((res^oparg) ? Py_True : Py_False); STACK_SHRINK(1); - POKE(1, b); + stack_pointer[-1] = b; DISPATCH(); } TARGET(CHECK_EG_MATCH) { - PyObject *match_type = PEEK(1); - PyObject *exc_value = PEEK(2); + PyObject *match_type = stack_pointer[-1]; + PyObject *exc_value = stack_pointer[-2]; PyObject *rest; PyObject *match; if (check_except_star_type_valid(tstate, match_type) < 0) { @@ -2336,14 +2331,14 @@ if (!Py_IsNone(match)) { PyErr_SetExcInfo(NULL, Py_NewRef(match), NULL); } - POKE(1, match); - POKE(2, rest); + stack_pointer[-1] = match; + stack_pointer[-2] = rest; DISPATCH(); } TARGET(CHECK_EXC_MATCH) { - PyObject *right = PEEK(1); - PyObject *left = PEEK(2); + PyObject *right = stack_pointer[-1]; + PyObject *left = stack_pointer[-2]; PyObject *b; assert(PyExceptionInstance_Check(left)); if (check_except_type_valid(tstate, right) < 0) { @@ -2354,13 +2349,13 @@ int res = PyErr_GivenExceptionMatches(left, right); Py_DECREF(right); b = Py_NewRef(res ? Py_True : Py_False); - POKE(1, b); + stack_pointer[-1] = b; DISPATCH(); } TARGET(IMPORT_NAME) { - PyObject *fromlist = PEEK(1); - PyObject *level = PEEK(2); + PyObject *fromlist = stack_pointer[-1]; + PyObject *level = stack_pointer[-2]; PyObject *res; PyObject *name = GETITEM(names, oparg); res = import_name(tstate, frame, name, fromlist, level); @@ -2368,18 +2363,18 @@ Py_DECREF(fromlist); if (res == NULL) goto pop_2_error; STACK_SHRINK(1); - POKE(1, res); + stack_pointer[-1] = res; DISPATCH(); } TARGET(IMPORT_FROM) { - PyObject *from = PEEK(1); + PyObject *from = stack_pointer[-1]; PyObject *res; PyObject *name = GETITEM(names, oparg); res = import_from(tstate, from, name); if (res == NULL) goto error; STACK_GROW(1); - POKE(1, res); + stack_pointer[-1] = res; DISPATCH(); } @@ -2398,7 +2393,7 @@ TARGET(POP_JUMP_IF_FALSE) { PREDICTED(POP_JUMP_IF_FALSE); - PyObject *cond = PEEK(1); + PyObject *cond = stack_pointer[-1]; if (Py_IsTrue(cond)) { _Py_DECREF_NO_DEALLOC(cond); } @@ -2421,7 +2416,7 @@ } TARGET(POP_JUMP_IF_TRUE) { - PyObject *cond = PEEK(1); + PyObject *cond = stack_pointer[-1]; if (Py_IsFalse(cond)) { _Py_DECREF_NO_DEALLOC(cond); } @@ -2444,7 +2439,7 @@ } TARGET(POP_JUMP_IF_NOT_NONE) { - PyObject *value = PEEK(1); + PyObject *value = stack_pointer[-1]; if (!Py_IsNone(value)) { Py_DECREF(value); JUMPBY(oparg); @@ -2457,7 +2452,7 @@ } TARGET(POP_JUMP_IF_NONE) { - PyObject *value = PEEK(1); + PyObject *value = stack_pointer[-1]; if (Py_IsNone(value)) { _Py_DECREF_NO_DEALLOC(value); JUMPBY(oparg); @@ -2470,7 +2465,7 @@ } TARGET(JUMP_IF_FALSE_OR_POP) { - PyObject *cond = PEEK(1); + PyObject *cond = stack_pointer[-1]; bool jump = false; int err; if (Py_IsTrue(cond)) { @@ -2499,7 +2494,7 @@ } TARGET(JUMP_IF_TRUE_OR_POP) { - PyObject *cond = PEEK(1); + PyObject *cond = stack_pointer[-1]; bool jump = false; int err; if (Py_IsFalse(cond)) { @@ -2538,7 +2533,7 @@ } TARGET(GET_LEN) { - PyObject *obj = PEEK(1); + PyObject *obj = stack_pointer[-1]; PyObject *len_o; // PUSH(len(TOS)) Py_ssize_t len_i = PyObject_Length(obj); @@ -2546,14 +2541,14 @@ len_o = PyLong_FromSsize_t(len_i); if (len_o == NULL) goto error; STACK_GROW(1); - POKE(1, len_o); + stack_pointer[-1] = len_o; DISPATCH(); } TARGET(MATCH_CLASS) { - PyObject *names = PEEK(1); - PyObject *type = PEEK(2); - PyObject *subject = PEEK(3); + PyObject *names = stack_pointer[-1]; + PyObject *type = stack_pointer[-2]; + PyObject *subject = stack_pointer[-3]; PyObject *attrs; // Pop TOS and TOS1. Set TOS to a tuple of attributes on success, or // None on failure. @@ -2570,57 +2565,57 @@ attrs = Py_NewRef(Py_None); // Failure! } STACK_SHRINK(2); - POKE(1, attrs); + stack_pointer[-1] = attrs; DISPATCH(); } TARGET(MATCH_MAPPING) { - PyObject *subject = PEEK(1); + PyObject *subject = stack_pointer[-1]; PyObject *res; int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_MAPPING; res = Py_NewRef(match ? Py_True : Py_False); STACK_GROW(1); - POKE(1, res); + stack_pointer[-1] = res; PREDICT(POP_JUMP_IF_FALSE); DISPATCH(); } TARGET(MATCH_SEQUENCE) { - PyObject *subject = PEEK(1); + PyObject *subject = stack_pointer[-1]; PyObject *res; int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_SEQUENCE; res = Py_NewRef(match ? Py_True : Py_False); STACK_GROW(1); - POKE(1, res); + stack_pointer[-1] = res; PREDICT(POP_JUMP_IF_FALSE); DISPATCH(); } TARGET(MATCH_KEYS) { - PyObject *keys = PEEK(1); - PyObject *subject = PEEK(2); + PyObject *keys = stack_pointer[-1]; + PyObject *subject = stack_pointer[-2]; PyObject *values_or_none; // On successful match, PUSH(values). Otherwise, PUSH(None). values_or_none = match_keys(tstate, subject, keys); if (values_or_none == NULL) goto error; STACK_GROW(1); - POKE(1, values_or_none); + stack_pointer[-1] = values_or_none; DISPATCH(); } TARGET(GET_ITER) { - PyObject *iterable = PEEK(1); + PyObject *iterable = stack_pointer[-1]; PyObject *iter; /* before: [obj]; after [getiter(obj)] */ iter = PyObject_GetIter(iterable); Py_DECREF(iterable); if (iter == NULL) goto pop_1_error; - POKE(1, iter); + stack_pointer[-1] = iter; DISPATCH(); } TARGET(GET_YIELD_FROM_ITER) { - PyObject *iterable = PEEK(1); + PyObject *iterable = stack_pointer[-1]; PyObject *iter; /* before: [obj]; after [getiter(obj)] */ if (PyCoro_CheckExact(iterable)) { @@ -2646,7 +2641,7 @@ } Py_DECREF(iterable); } - POKE(1, iter); + stack_pointer[-1] = iter; PREDICT(LOAD_CONST); DISPATCH(); } @@ -2654,7 +2649,7 @@ TARGET(FOR_ITER) { PREDICTED(FOR_ITER); static_assert(INLINE_CACHE_ENTRIES_FOR_ITER == 1, "incorrect cache size"); - PyObject *iter = PEEK(1); + PyObject *iter = stack_pointer[-1]; PyObject *next; #if ENABLE_SPECIALIZATION _PyForIterCache *cache = (_PyForIterCache *)next_instr; @@ -2689,13 +2684,13 @@ } // Common case: no jump, leave it to the code generator STACK_GROW(1); - POKE(1, next); - JUMPBY(1); + stack_pointer[-1] = next; + next_instr += 1; DISPATCH(); } TARGET(FOR_ITER_LIST) { - PyObject *iter = PEEK(1); + PyObject *iter = stack_pointer[-1]; PyObject *next; assert(cframe.use_tracing == 0); DEOPT_IF(Py_TYPE(iter) != &PyListIter_Type, FOR_ITER); @@ -2718,13 +2713,13 @@ end_for_iter_list: // Common case: no jump, leave it to the code generator STACK_GROW(1); - POKE(1, next); - JUMPBY(1); + stack_pointer[-1] = next; + next_instr += 1; DISPATCH(); } TARGET(FOR_ITER_TUPLE) { - PyObject *iter = PEEK(1); + PyObject *iter = stack_pointer[-1]; PyObject *next; assert(cframe.use_tracing == 0); _PyTupleIterObject *it = (_PyTupleIterObject *)iter; @@ -2747,13 +2742,13 @@ end_for_iter_tuple: // Common case: no jump, leave it to the code generator STACK_GROW(1); - POKE(1, next); - JUMPBY(1); + stack_pointer[-1] = next; + next_instr += 1; DISPATCH(); } TARGET(FOR_ITER_RANGE) { - PyObject *iter = PEEK(1); + PyObject *iter = stack_pointer[-1]; PyObject *next; assert(cframe.use_tracing == 0); _PyRangeIterObject *r = (_PyRangeIterObject *)iter; @@ -2774,13 +2769,13 @@ goto error; } STACK_GROW(1); - POKE(1, next); - JUMPBY(1); + stack_pointer[-1] = next; + next_instr += 1; DISPATCH(); } TARGET(FOR_ITER_GEN) { - PyObject *iter = PEEK(1); + PyObject *iter = stack_pointer[-1]; assert(cframe.use_tracing == 0); PyGenObject *gen = (PyGenObject *)iter; DEOPT_IF(Py_TYPE(gen) != &PyGen_Type, FOR_ITER); @@ -2798,7 +2793,7 @@ } TARGET(BEFORE_ASYNC_WITH) { - PyObject *mgr = PEEK(1); + PyObject *mgr = stack_pointer[-1]; PyObject *exit; PyObject *res; PyObject *enter = _PyObject_LookupSpecial(mgr, &_Py_ID(__aenter__)); @@ -2831,14 +2826,14 @@ if (true) goto pop_1_error; } STACK_GROW(1); - POKE(1, res); - POKE(2, exit); + stack_pointer[-1] = res; + stack_pointer[-2] = exit; PREDICT(GET_AWAITABLE); DISPATCH(); } TARGET(BEFORE_WITH) { - PyObject *mgr = PEEK(1); + PyObject *mgr = stack_pointer[-1]; PyObject *exit; PyObject *res; /* pop the context manager, push its __exit__ and the @@ -2874,15 +2869,15 @@ if (true) goto pop_1_error; } STACK_GROW(1); - POKE(1, res); - POKE(2, exit); + stack_pointer[-1] = res; + stack_pointer[-2] = exit; DISPATCH(); } TARGET(WITH_EXCEPT_START) { - PyObject *val = PEEK(1); - PyObject *lasti = PEEK(3); - PyObject *exit_func = PEEK(4); + PyObject *val = stack_pointer[-1]; + PyObject *lasti = stack_pointer[-3]; + PyObject *exit_func = stack_pointer[-4]; PyObject *res; /* At the top of the stack are 4 values: - val: TOP = exc_info() @@ -2905,12 +2900,12 @@ 3 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); if (res == NULL) goto error; STACK_GROW(1); - POKE(1, res); + stack_pointer[-1] = res; DISPATCH(); } TARGET(PUSH_EXC_INFO) { - PyObject *new_exc = PEEK(1); + PyObject *new_exc = stack_pointer[-1]; PyObject *prev_exc; _PyErr_StackItem *exc_info = tstate->exc_info; if (exc_info->exc_value != NULL) { @@ -2922,13 +2917,13 @@ assert(PyExceptionInstance_Check(new_exc)); exc_info->exc_value = Py_NewRef(new_exc); STACK_GROW(1); - POKE(1, new_exc); - POKE(2, prev_exc); + stack_pointer[-1] = new_exc; + stack_pointer[-2] = prev_exc; DISPATCH(); } TARGET(LOAD_ATTR_METHOD_WITH_VALUES) { - PyObject *self = PEEK(1); + PyObject *self = stack_pointer[-1]; PyObject *res2 = NULL; PyObject *res; uint32_t type_version = read_u32(&next_instr[1].cache); @@ -2952,14 +2947,14 @@ res = self; assert(oparg & 1); STACK_GROW(((oparg & 1) ? 1 : 0)); - POKE(1, res); - if (oparg & 1) { POKE(1 + ((oparg & 1) ? 1 : 0), res2); } - JUMPBY(9); + stack_pointer[-1] = res; + if (oparg & 1) { stack_pointer[-(1 + ((oparg & 1) ? 1 : 0))] = res2; } + next_instr += 9; DISPATCH(); } TARGET(LOAD_ATTR_METHOD_NO_DICT) { - PyObject *self = PEEK(1); + PyObject *self = stack_pointer[-1]; PyObject *res2 = NULL; PyObject *res; uint32_t type_version = read_u32(&next_instr[1].cache); @@ -2975,14 +2970,14 @@ res = self; assert(oparg & 1); STACK_GROW(((oparg & 1) ? 1 : 0)); - POKE(1, res); - if (oparg & 1) { POKE(1 + ((oparg & 1) ? 1 : 0), res2); } - JUMPBY(9); + stack_pointer[-1] = res; + if (oparg & 1) { stack_pointer[-(1 + ((oparg & 1) ? 1 : 0))] = res2; } + next_instr += 9; DISPATCH(); } TARGET(LOAD_ATTR_METHOD_LAZY_DICT) { - PyObject *self = PEEK(1); + PyObject *self = stack_pointer[-1]; PyObject *res2 = NULL; PyObject *res; uint32_t type_version = read_u32(&next_instr[1].cache); @@ -3002,9 +2997,9 @@ res = self; assert(oparg & 1); STACK_GROW(((oparg & 1) ? 1 : 0)); - POKE(1, res); - if (oparg & 1) { POKE(1 + ((oparg & 1) ? 1 : 0), res2); } - JUMPBY(9); + stack_pointer[-1] = res; + if (oparg & 1) { stack_pointer[-(1 + ((oparg & 1) ? 1 : 0))] = res2; } + next_instr += 9; DISPATCH(); } @@ -3018,9 +3013,9 @@ TARGET(CALL) { PREDICTED(CALL); static_assert(INLINE_CACHE_ENTRIES_CALL == 4, "incorrect cache size"); - PyObject **args = &PEEK(oparg); - PyObject *callable = PEEK(1 + oparg); - PyObject *method = PEEK(2 + oparg); + PyObject **args = (stack_pointer - oparg); + PyObject *callable = stack_pointer[-(1 + oparg)]; + PyObject *method = stack_pointer[-(2 + oparg)]; PyObject *res; int is_meth = method != NULL; int total_args = oparg; @@ -3095,15 +3090,15 @@ if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } STACK_SHRINK(oparg); STACK_SHRINK(1); - POKE(1, res); - JUMPBY(4); + stack_pointer[-1] = res; + next_instr += 4; CHECK_EVAL_BREAKER(); DISPATCH(); } TARGET(CALL_BOUND_METHOD_EXACT_ARGS) { - PyObject *callable = PEEK(1 + oparg); - PyObject *method = PEEK(2 + oparg); + PyObject *callable = stack_pointer[-(1 + oparg)]; + PyObject *method = stack_pointer[-(2 + oparg)]; DEOPT_IF(method != NULL, CALL); DEOPT_IF(Py_TYPE(callable) != &PyMethod_Type, CALL); STAT_INC(CALL, hit); @@ -3117,9 +3112,9 @@ TARGET(CALL_PY_EXACT_ARGS) { PREDICTED(CALL_PY_EXACT_ARGS); - PyObject **args = &PEEK(oparg); - PyObject *callable = PEEK(1 + oparg); - PyObject *method = PEEK(2 + oparg); + PyObject **args = (stack_pointer - oparg); + PyObject *callable = stack_pointer[-(1 + oparg)]; + PyObject *method = stack_pointer[-(2 + oparg)]; uint32_t func_version = read_u32(&next_instr[1].cache); assert(kwnames == NULL); DEOPT_IF(tstate->interp->eval_frame, CALL); @@ -3148,9 +3143,9 @@ } TARGET(CALL_PY_WITH_DEFAULTS) { - PyObject **args = &PEEK(oparg); - PyObject *callable = PEEK(1 + oparg); - PyObject *method = PEEK(2 + oparg); + PyObject **args = (stack_pointer - oparg); + PyObject *callable = stack_pointer[-(1 + oparg)]; + PyObject *method = stack_pointer[-(2 + oparg)]; uint32_t func_version = read_u32(&next_instr[1].cache); uint16_t min_args = read_u16(&next_instr[3].cache); assert(kwnames == NULL); @@ -3185,9 +3180,9 @@ } TARGET(CALL_NO_KW_TYPE_1) { - PyObject **args = &PEEK(oparg); - PyObject *callable = PEEK(1 + oparg); - PyObject *null = PEEK(2 + oparg); + PyObject **args = (stack_pointer - oparg); + PyObject *callable = stack_pointer[-(1 + oparg)]; + PyObject *null = stack_pointer[-(2 + oparg)]; PyObject *res; assert(kwnames == NULL); assert(cframe.use_tracing == 0); @@ -3201,15 +3196,15 @@ Py_DECREF(&PyType_Type); // I.e., callable STACK_SHRINK(oparg); STACK_SHRINK(1); - POKE(1, res); - JUMPBY(4); + stack_pointer[-1] = res; + next_instr += 4; DISPATCH(); } TARGET(CALL_NO_KW_STR_1) { - PyObject **args = &PEEK(oparg); - PyObject *callable = PEEK(1 + oparg); - PyObject *null = PEEK(2 + oparg); + PyObject **args = (stack_pointer - oparg); + PyObject *callable = stack_pointer[-(1 + oparg)]; + PyObject *null = stack_pointer[-(2 + oparg)]; PyObject *res; assert(kwnames == NULL); assert(cframe.use_tracing == 0); @@ -3224,16 +3219,16 @@ if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } STACK_SHRINK(oparg); STACK_SHRINK(1); - POKE(1, res); - JUMPBY(4); + stack_pointer[-1] = res; + next_instr += 4; CHECK_EVAL_BREAKER(); DISPATCH(); } TARGET(CALL_NO_KW_TUPLE_1) { - PyObject **args = &PEEK(oparg); - PyObject *callable = PEEK(1 + oparg); - PyObject *null = PEEK(2 + oparg); + PyObject **args = (stack_pointer - oparg); + PyObject *callable = stack_pointer[-(1 + oparg)]; + PyObject *null = stack_pointer[-(2 + oparg)]; PyObject *res; assert(kwnames == NULL); assert(oparg == 1); @@ -3247,16 +3242,16 @@ if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } STACK_SHRINK(oparg); STACK_SHRINK(1); - POKE(1, res); - JUMPBY(4); + stack_pointer[-1] = res; + next_instr += 4; CHECK_EVAL_BREAKER(); DISPATCH(); } TARGET(CALL_BUILTIN_CLASS) { - PyObject **args = &PEEK(oparg); - PyObject *callable = PEEK(1 + oparg); - PyObject *method = PEEK(2 + oparg); + PyObject **args = (stack_pointer - oparg); + PyObject *callable = stack_pointer[-(1 + oparg)]; + PyObject *method = stack_pointer[-(2 + oparg)]; PyObject *res; int is_meth = method != NULL; int total_args = oparg; @@ -3281,16 +3276,16 @@ if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } STACK_SHRINK(oparg); STACK_SHRINK(1); - POKE(1, res); - JUMPBY(4); + stack_pointer[-1] = res; + next_instr += 4; CHECK_EVAL_BREAKER(); DISPATCH(); } TARGET(CALL_NO_KW_BUILTIN_O) { - PyObject **args = &PEEK(oparg); - PyObject *callable = PEEK(1 + oparg); - PyObject *method = PEEK(2 + oparg); + PyObject **args = (stack_pointer - oparg); + PyObject *callable = stack_pointer[-(1 + oparg)]; + PyObject *method = stack_pointer[-(2 + oparg)]; PyObject *res; assert(cframe.use_tracing == 0); /* Builtin METH_O functions */ @@ -3322,16 +3317,16 @@ if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } STACK_SHRINK(oparg); STACK_SHRINK(1); - POKE(1, res); - JUMPBY(4); + stack_pointer[-1] = res; + next_instr += 4; CHECK_EVAL_BREAKER(); DISPATCH(); } TARGET(CALL_NO_KW_BUILTIN_FAST) { - PyObject **args = &PEEK(oparg); - PyObject *callable = PEEK(1 + oparg); - PyObject *method = PEEK(2 + oparg); + PyObject **args = (stack_pointer - oparg); + PyObject *callable = stack_pointer[-(1 + oparg)]; + PyObject *method = stack_pointer[-(2 + oparg)]; PyObject *res; assert(cframe.use_tracing == 0); /* Builtin METH_FASTCALL functions, without keywords */ @@ -3367,16 +3362,16 @@ */ STACK_SHRINK(oparg); STACK_SHRINK(1); - POKE(1, res); - JUMPBY(4); + stack_pointer[-1] = res; + next_instr += 4; CHECK_EVAL_BREAKER(); DISPATCH(); } TARGET(CALL_BUILTIN_FAST_WITH_KEYWORDS) { - PyObject **args = &PEEK(oparg); - PyObject *callable = PEEK(1 + oparg); - PyObject *method = PEEK(2 + oparg); + PyObject **args = (stack_pointer - oparg); + PyObject *callable = stack_pointer[-(1 + oparg)]; + PyObject *method = stack_pointer[-(2 + oparg)]; PyObject *res; assert(cframe.use_tracing == 0); /* Builtin METH_FASTCALL | METH_KEYWORDS functions */ @@ -3412,16 +3407,16 @@ if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } STACK_SHRINK(oparg); STACK_SHRINK(1); - POKE(1, res); - JUMPBY(4); + stack_pointer[-1] = res; + next_instr += 4; CHECK_EVAL_BREAKER(); DISPATCH(); } TARGET(CALL_NO_KW_LEN) { - PyObject **args = &PEEK(oparg); - PyObject *callable = PEEK(1 + oparg); - PyObject *method = PEEK(2 + oparg); + PyObject **args = (stack_pointer - oparg); + PyObject *callable = stack_pointer[-(1 + oparg)]; + PyObject *method = stack_pointer[-(2 + oparg)]; PyObject *res; assert(cframe.use_tracing == 0); assert(kwnames == NULL); @@ -3450,15 +3445,15 @@ if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } STACK_SHRINK(oparg); STACK_SHRINK(1); - POKE(1, res); - JUMPBY(4); + stack_pointer[-1] = res; + next_instr += 4; DISPATCH(); } TARGET(CALL_NO_KW_ISINSTANCE) { - PyObject **args = &PEEK(oparg); - PyObject *callable = PEEK(1 + oparg); - PyObject *method = PEEK(2 + oparg); + PyObject **args = (stack_pointer - oparg); + PyObject *callable = stack_pointer[-(1 + oparg)]; + PyObject *method = stack_pointer[-(2 + oparg)]; PyObject *res; assert(cframe.use_tracing == 0); assert(kwnames == NULL); @@ -3489,15 +3484,15 @@ if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } STACK_SHRINK(oparg); STACK_SHRINK(1); - POKE(1, res); - JUMPBY(4); + stack_pointer[-1] = res; + next_instr += 4; DISPATCH(); } TARGET(CALL_NO_KW_LIST_APPEND) { - PyObject **args = &PEEK(oparg); - PyObject *self = PEEK(1 + oparg); - PyObject *method = PEEK(2 + oparg); + PyObject **args = (stack_pointer - oparg); + PyObject *self = stack_pointer[-(1 + oparg)]; + PyObject *method = stack_pointer[-(2 + oparg)]; assert(cframe.use_tracing == 0); assert(kwnames == NULL); assert(oparg == 1); @@ -3519,8 +3514,8 @@ } TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_O) { - PyObject **args = &PEEK(oparg); - PyObject *method = PEEK(2 + oparg); + PyObject **args = (stack_pointer - oparg); + PyObject *method = stack_pointer[-(2 + oparg)]; PyObject *res; assert(kwnames == NULL); int is_meth = method != NULL; @@ -3554,15 +3549,15 @@ if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } STACK_SHRINK(oparg); STACK_SHRINK(1); - POKE(1, res); - JUMPBY(4); + stack_pointer[-1] = res; + next_instr += 4; CHECK_EVAL_BREAKER(); DISPATCH(); } TARGET(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS) { - PyObject **args = &PEEK(oparg); - PyObject *method = PEEK(2 + oparg); + PyObject **args = (stack_pointer - oparg); + PyObject *method = stack_pointer[-(2 + oparg)]; PyObject *res; int is_meth = method != NULL; int total_args = oparg; @@ -3594,15 +3589,15 @@ if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } STACK_SHRINK(oparg); STACK_SHRINK(1); - POKE(1, res); - JUMPBY(4); + stack_pointer[-1] = res; + next_instr += 4; CHECK_EVAL_BREAKER(); DISPATCH(); } TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS) { - PyObject **args = &PEEK(oparg); - PyObject *method = PEEK(2 + oparg); + PyObject **args = (stack_pointer - oparg); + PyObject *method = stack_pointer[-(2 + oparg)]; PyObject *res; assert(kwnames == NULL); assert(oparg == 0 || oparg == 1); @@ -3634,15 +3629,15 @@ if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } STACK_SHRINK(oparg); STACK_SHRINK(1); - POKE(1, res); - JUMPBY(4); + stack_pointer[-1] = res; + next_instr += 4; CHECK_EVAL_BREAKER(); DISPATCH(); } TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_FAST) { - PyObject **args = &PEEK(oparg); - PyObject *method = PEEK(2 + oparg); + PyObject **args = (stack_pointer - oparg); + PyObject *method = stack_pointer[-(2 + oparg)]; PyObject *res; assert(kwnames == NULL); int is_meth = method != NULL; @@ -3673,17 +3668,17 @@ if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } STACK_SHRINK(oparg); STACK_SHRINK(1); - POKE(1, res); - JUMPBY(4); + stack_pointer[-1] = res; + next_instr += 4; CHECK_EVAL_BREAKER(); DISPATCH(); } TARGET(CALL_FUNCTION_EX) { PREDICTED(CALL_FUNCTION_EX); - PyObject *kwargs = (oparg & 1) ? PEEK(((oparg & 1) ? 1 : 0)) : NULL; - PyObject *callargs = PEEK(1 + ((oparg & 1) ? 1 : 0)); - PyObject *func = PEEK(2 + ((oparg & 1) ? 1 : 0)); + PyObject *kwargs = (oparg & 1) ? stack_pointer[-(((oparg & 1) ? 1 : 0))] : NULL; + PyObject *callargs = stack_pointer[-(1 + ((oparg & 1) ? 1 : 0))]; + PyObject *func = stack_pointer[-(2 + ((oparg & 1) ? 1 : 0))]; PyObject *result; if (oparg & 1) { // DICT_MERGE is called before this opcode if there are kwargs. @@ -3711,17 +3706,17 @@ if (result == NULL) { STACK_SHRINK(((oparg & 1) ? 1 : 0)); goto pop_3_error; } STACK_SHRINK(((oparg & 1) ? 1 : 0)); STACK_SHRINK(2); - POKE(1, result); + stack_pointer[-1] = result; CHECK_EVAL_BREAKER(); DISPATCH(); } TARGET(MAKE_FUNCTION) { - PyObject *codeobj = PEEK(1); - PyObject *closure = (oparg & 0x08) ? PEEK(1 + ((oparg & 0x08) ? 1 : 0)) : NULL; - PyObject *annotations = (oparg & 0x04) ? PEEK(1 + ((oparg & 0x08) ? 1 : 0) + ((oparg & 0x04) ? 1 : 0)) : NULL; - PyObject *kwdefaults = (oparg & 0x02) ? PEEK(1 + ((oparg & 0x08) ? 1 : 0) + ((oparg & 0x04) ? 1 : 0) + ((oparg & 0x02) ? 1 : 0)) : NULL; - PyObject *defaults = (oparg & 0x01) ? PEEK(1 + ((oparg & 0x08) ? 1 : 0) + ((oparg & 0x04) ? 1 : 0) + ((oparg & 0x02) ? 1 : 0) + ((oparg & 0x01) ? 1 : 0)) : NULL; + PyObject *codeobj = stack_pointer[-1]; + PyObject *closure = (oparg & 0x08) ? stack_pointer[-(1 + ((oparg & 0x08) ? 1 : 0))] : NULL; + PyObject *annotations = (oparg & 0x04) ? stack_pointer[-(1 + ((oparg & 0x08) ? 1 : 0) + ((oparg & 0x04) ? 1 : 0))] : NULL; + PyObject *kwdefaults = (oparg & 0x02) ? stack_pointer[-(1 + ((oparg & 0x08) ? 1 : 0) + ((oparg & 0x04) ? 1 : 0) + ((oparg & 0x02) ? 1 : 0))] : NULL; + PyObject *defaults = (oparg & 0x01) ? stack_pointer[-(1 + ((oparg & 0x08) ? 1 : 0) + ((oparg & 0x04) ? 1 : 0) + ((oparg & 0x02) ? 1 : 0) + ((oparg & 0x01) ? 1 : 0))] : NULL; PyObject *func; PyFunctionObject *func_obj = (PyFunctionObject *) @@ -3752,7 +3747,7 @@ func_obj->func_version = ((PyCodeObject *)codeobj)->co_version; func = (PyObject *)func_obj; STACK_SHRINK(((oparg & 0x01) ? 1 : 0) + ((oparg & 0x02) ? 1 : 0) + ((oparg & 0x04) ? 1 : 0) + ((oparg & 0x08) ? 1 : 0)); - POKE(1, func); + stack_pointer[-1] = func; DISPATCH(); } @@ -3780,9 +3775,9 @@ } TARGET(BUILD_SLICE) { - PyObject *step = (oparg == 3) ? PEEK(((oparg == 3) ? 1 : 0)) : NULL; - PyObject *stop = PEEK(1 + ((oparg == 3) ? 1 : 0)); - PyObject *start = PEEK(2 + ((oparg == 3) ? 1 : 0)); + PyObject *step = (oparg == 3) ? stack_pointer[-(((oparg == 3) ? 1 : 0))] : NULL; + PyObject *stop = stack_pointer[-(1 + ((oparg == 3) ? 1 : 0))]; + PyObject *start = stack_pointer[-(2 + ((oparg == 3) ? 1 : 0))]; PyObject *slice; slice = PySlice_New(start, stop, step); Py_DECREF(start); @@ -3791,13 +3786,13 @@ if (slice == NULL) { STACK_SHRINK(((oparg == 3) ? 1 : 0)); goto pop_2_error; } STACK_SHRINK(((oparg == 3) ? 1 : 0)); STACK_SHRINK(1); - POKE(1, slice); + stack_pointer[-1] = slice; DISPATCH(); } TARGET(FORMAT_VALUE) { - PyObject *fmt_spec = ((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? PEEK((((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? 1 : 0)) : NULL; - PyObject *value = PEEK(1 + (((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? 1 : 0)); + PyObject *fmt_spec = ((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? stack_pointer[-((((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? 1 : 0))] : NULL; + PyObject *value = stack_pointer[-(1 + (((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? 1 : 0))]; PyObject *result; /* Handles f-string value formatting. */ PyObject *(*conv_fn)(PyObject *); @@ -3845,25 +3840,25 @@ if (result == NULL) { STACK_SHRINK((((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? 1 : 0)); goto pop_1_error; } } STACK_SHRINK((((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? 1 : 0)); - POKE(1, result); + stack_pointer[-1] = result; DISPATCH(); } TARGET(COPY) { - PyObject *bottom = PEEK(1 + (oparg-1)); + PyObject *bottom = stack_pointer[-(1 + (oparg-1))]; PyObject *top; assert(oparg > 0); top = Py_NewRef(bottom); STACK_GROW(1); - POKE(1, top); + stack_pointer[-1] = top; DISPATCH(); } TARGET(BINARY_OP) { PREDICTED(BINARY_OP); static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size"); - PyObject *rhs = PEEK(1); - PyObject *lhs = PEEK(2); + PyObject *rhs = stack_pointer[-1]; + PyObject *lhs = stack_pointer[-2]; PyObject *res; #if ENABLE_SPECIALIZATION _PyBinaryOpCache *cache = (_PyBinaryOpCache *)next_instr; @@ -3884,17 +3879,17 @@ Py_DECREF(rhs); if (res == NULL) goto pop_2_error; STACK_SHRINK(1); - POKE(1, res); - JUMPBY(1); + stack_pointer[-1] = res; + next_instr += 1; DISPATCH(); } TARGET(SWAP) { - PyObject *top = PEEK(1); - PyObject *bottom = PEEK(2 + (oparg-2)); + PyObject *top = stack_pointer[-1]; + PyObject *bottom = stack_pointer[-(2 + (oparg-2))]; assert(oparg >= 2); - POKE(1, bottom); - POKE(2 + (oparg-2), top); + stack_pointer[-1] = bottom; + stack_pointer[-(2 + (oparg-2))] = top; DISPATCH(); } diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index c7f52b55a5eb99..b760172974c8a5 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -171,19 +171,17 @@ def declare(self, dst: StackEffect, src: StackEffect | None): def assign(self, dst: StackEffect, src: StackEffect): if src.name == UNUSED: return + if src.size: + # Don't write sized arrays -- it's up to the user code. + return cast = self.cast(dst, src) - if m := re.match(r"^PEEK\((.*)\)$", dst.name): - stmt = f"POKE({m.group(1)}, {cast}{src.name});" + if re.match(r"^REG\(oparg(\d+)\)$", dst.name): + self.emit(f"Py_XSETREF({dst.name}, {cast}{src.name});") + else: + stmt = f"{dst.name} = {cast}{src.name};" if src.cond: stmt = f"if ({src.cond}) {{ {stmt} }}" self.emit(stmt) - elif m := re.match(r"^&PEEK\(.*\)$", dst.name): - # The user code is responsible for writing to the output array. - pass - elif m := re.match(r"^REG\(oparg(\d+)\)$", dst.name): - self.emit(f"Py_XSETREF({dst.name}, {cast}{src.name});") - else: - self.emit(f"{dst.name} = {cast}{src.name};") def cast(self, dst: StackEffect, src: StackEffect) -> str: return f"({dst.type or 'PyObject *'})" if src.type != dst.type else "" @@ -292,11 +290,11 @@ def write(self, out: Formatter) -> None: list_effect_size([ieff for ieff in ieffects[: i + 1]]) ) if ieffect.size: - src = StackEffect(f"&PEEK({isize})", "PyObject **") + src = StackEffect(f"(stack_pointer - {maybe_parenthesize(isize)})", "PyObject **") elif ieffect.cond: - src = StackEffect(f"({ieffect.cond}) ? PEEK({isize}) : NULL", "") + src = StackEffect(f"({ieffect.cond}) ? stack_pointer[-{maybe_parenthesize(isize)}] : NULL", "") else: - src = StackEffect(f"PEEK({isize})", "") + src = StackEffect(f"stack_pointer[-{maybe_parenthesize(isize)}]", "") out.declare(ieffect, src) else: # Write input register variable declarations and initializations @@ -324,7 +322,7 @@ def write(self, out: Formatter) -> None: else: out.declare(oeffect, None) - # out.emit(f"JUMPBY(OPSIZE({self.inst.name}) - 1);") + # out.emit(f"next_instr += OPSIZE({self.inst.name}) - 1;") self.write_body(out, 0) @@ -349,9 +347,9 @@ def write(self, out: Formatter) -> None: list_effect_size([oeff for oeff in oeffects[: i + 1]]) ) if oeffect.size: - dst = StackEffect(f"&PEEK({osize})", "PyObject **") + dst = StackEffect(f"stack_pointer - {maybe_parenthesize(osize)}", "PyObject **") else: - dst = StackEffect(f"PEEK({osize})", "") + dst = StackEffect(f"stack_pointer[-{maybe_parenthesize(osize)}]", "") out.assign(dst, oeffect) else: # Write output register assignments @@ -361,7 +359,7 @@ def write(self, out: Formatter) -> None: # Write cache effect if self.cache_offset: - out.emit(f"JUMPBY({self.cache_offset});") + out.emit(f"next_instr += {self.cache_offset};") def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None: """Write the instruction body.""" @@ -1060,17 +1058,13 @@ def write_super(self, sup: SuperInstruction) -> None: with self.wrap_super_or_macro(sup): first = True for comp in sup.parts: - if first: - pass - # self.out.emit("JUMPBY(OPSIZE(opcode) - 1);") - else: - self.out.emit("NEXTOPARG();") - self.out.emit("JUMPBY(1);") - # self.out.emit("JUMPBY(OPSIZE(opcode));") + if not first: + self.out.emit("oparg = (next_instr++)->op.arg;") + # self.out.emit("next_instr += OPSIZE(opcode) - 1;") first = False comp.write_body(self.out, 0) if comp.instr.cache_offset: - self.out.emit(f"JUMPBY({comp.instr.cache_offset});") + self.out.emit(f"next_instr += {comp.instr.cache_offset};") def write_macro(self, mac: MacroInstruction) -> None: """Write code for a macro instruction.""" @@ -1087,7 +1081,7 @@ def write_macro(self, mac: MacroInstruction) -> None: cache_adjust += comp.instr.cache_offset if cache_adjust: - self.out.emit(f"JUMPBY({cache_adjust});") + self.out.emit(f"next_instr += {cache_adjust};") if ( last_instr @@ -1113,7 +1107,7 @@ def wrap_super_or_macro(self, up: SuperOrMacroInstruction): for i, var in reversed(list(enumerate(up.stack))): src = None if i < up.initial_sp: - src = StackEffect(f"PEEK({up.initial_sp - i})", "") + src = StackEffect(f"stack_pointer[-{up.initial_sp - i}]", "") self.out.declare(var, src) yield @@ -1122,7 +1116,7 @@ def wrap_super_or_macro(self, up: SuperOrMacroInstruction): self.out.stack_adjust(up.final_sp - up.initial_sp, [], []) for i, var in enumerate(reversed(up.stack[: up.final_sp]), 1): - dst = StackEffect(f"PEEK({i})", "") + dst = StackEffect(f"stack_pointer[-{i}]", "") self.out.assign(dst, var) self.out.emit(f"DISPATCH();") From f300a1fa4c121f7807cbda4fc8bb26240c69ea74 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 28 Feb 2023 13:14:40 -0700 Subject: [PATCH 213/247] gh-100227: Move the dtoa State to PyInterpreterState (gh-102331) https://github.com/python/cpython/issues/100227 --- Include/internal/pycore_dtoa.h | 11 ++++++----- Include/internal/pycore_interp.h | 2 ++ Include/internal/pycore_runtime.h | 2 -- Include/internal/pycore_runtime_init.h | 6 +++--- Python/dtoa.c | 15 +++++++++------ Python/pystate.c | 20 +++++++++++++++++++- 6 files changed, 39 insertions(+), 17 deletions(-) diff --git a/Include/internal/pycore_dtoa.h b/Include/internal/pycore_dtoa.h index 67189cf0ade665..fb524770efed7c 100644 --- a/Include/internal/pycore_dtoa.h +++ b/Include/internal/pycore_dtoa.h @@ -24,10 +24,11 @@ Bigint { #ifdef Py_USING_MEMORY_DEBUGGER -struct _dtoa_runtime_state { +struct _dtoa_state { int _not_used; }; -#define _dtoa_runtime_state_INIT {0} +#define _dtoa_interp_state_INIT(INTERP) \ + {0} #else // !Py_USING_MEMORY_DEBUGGER @@ -40,7 +41,7 @@ struct _dtoa_runtime_state { #define Bigint_PREALLOC_SIZE \ ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double)) -struct _dtoa_runtime_state { +struct _dtoa_state { /* p5s is a linked list of powers of 5 of the form 5**(2**i), i >= 2 */ // XXX This should be freed during runtime fini. struct Bigint *p5s; @@ -48,9 +49,9 @@ struct _dtoa_runtime_state { double preallocated[Bigint_PREALLOC_SIZE]; double *preallocated_next; }; -#define _dtoa_runtime_state_INIT(runtime) \ +#define _dtoa_state_INIT(INTERP) \ { \ - .preallocated_next = runtime.dtoa.preallocated, \ + .preallocated_next = (INTERP)->dtoa.preallocated, \ } #endif // !Py_USING_MEMORY_DEBUGGER diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index 60de31b336f613..7ef9c40153e421 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -16,6 +16,7 @@ extern "C" { #include "pycore_code.h" // struct callable_cache #include "pycore_context.h" // struct _Py_context_state #include "pycore_dict_state.h" // struct _Py_dict_state +#include "pycore_dtoa.h" // struct _dtoa_state #include "pycore_exceptions.h" // struct _Py_exc_state #include "pycore_floatobject.h" // struct _Py_float_state #include "pycore_function.h" // FUNC_MAX_WATCHERS @@ -139,6 +140,7 @@ struct _is { struct _Py_unicode_state unicode; struct _Py_float_state float_state; struct _Py_long_state long_state; + struct _dtoa_state dtoa; /* Using a cache is very effective since typically only a single slice is created and then deleted again. */ PySliceObject *slice_cache; diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h index 9ef270791576e3..2350eaab5976ca 100644 --- a/Include/internal/pycore_runtime.h +++ b/Include/internal/pycore_runtime.h @@ -11,7 +11,6 @@ extern "C" { #include "pycore_atomic.h" /* _Py_atomic_address */ #include "pycore_ceval_state.h" // struct _ceval_runtime_state #include "pycore_dict_state.h" // struct _Py_dict_runtime_state -#include "pycore_dtoa.h" // struct _dtoa_runtime_state #include "pycore_floatobject.h" // struct _Py_float_runtime_state #include "pycore_faulthandler.h" // struct _faulthandler_runtime_state #include "pycore_function.h" // struct _func_runtime_state @@ -141,7 +140,6 @@ typedef struct pyruntimestate { struct _ceval_runtime_state ceval; struct _gilstate_runtime_state gilstate; struct _getargs_runtime_state getargs; - struct _dtoa_runtime_state dtoa; struct _fileutils_state fileutils; struct _faulthandler_runtime_state faulthandler; struct _tracemalloc_runtime_state tracemalloc; diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h index a8d5953ff98b0b..b54adf04761d4e 100644 --- a/Include/internal/pycore_runtime_init.h +++ b/Include/internal/pycore_runtime_init.h @@ -53,7 +53,6 @@ extern "C" { .gilstate = { \ .check_enabled = 1, \ }, \ - .dtoa = _dtoa_runtime_state_INIT(runtime), \ .fileutils = { \ .force_ascii = -1, \ }, \ @@ -94,10 +93,10 @@ extern "C" { }, \ }, \ }, \ - ._main_interpreter = _PyInterpreterState_INIT, \ + ._main_interpreter = _PyInterpreterState_INIT(runtime._main_interpreter), \ } -#define _PyInterpreterState_INIT \ +#define _PyInterpreterState_INIT(INTERP) \ { \ .id_refcount = -1, \ .imports = IMPORTS_INIT, \ @@ -113,6 +112,7 @@ extern "C" { { .threshold = 10, }, \ }, \ }, \ + .dtoa = _dtoa_state_INIT(&(INTERP)), \ .static_objects = { \ .singletons = { \ ._not_used = 1, \ diff --git a/Python/dtoa.c b/Python/dtoa.c index cff5f1b0658eae..6ea60ac9946e0f 100644 --- a/Python/dtoa.c +++ b/Python/dtoa.c @@ -119,7 +119,7 @@ #include "Python.h" #include "pycore_dtoa.h" // _PY_SHORT_FLOAT_REPR -#include "pycore_runtime.h" // _PyRuntime +#include "pycore_pystate.h" // _PyInterpreterState_GET() #include // exit() /* if _PY_SHORT_FLOAT_REPR == 0, then don't even try to compile @@ -339,9 +339,9 @@ typedef struct Bigint Bigint; Bfree to PyMem_Free. Investigate whether this has any significant performance on impact. */ -#define freelist _PyRuntime.dtoa.freelist -#define private_mem _PyRuntime.dtoa.preallocated -#define pmem_next _PyRuntime.dtoa.preallocated_next +#define freelist interp->dtoa.freelist +#define private_mem interp->dtoa.preallocated +#define pmem_next interp->dtoa.preallocated_next /* Allocate space for a Bigint with up to 1<next; @@ -385,6 +386,7 @@ Bfree(Bigint *v) if (v->k > Bigint_Kmax) FREE((void*)v); else { + PyInterpreterState *interp = _PyInterpreterState_GET(); v->next = freelist[v->k]; freelist[v->k] = v; } @@ -692,7 +694,8 @@ pow5mult(Bigint *b, int k) if (!(k >>= 2)) return b; - p5 = _PyRuntime.dtoa.p5s; + PyInterpreterState *interp = _PyInterpreterState_GET(); + p5 = interp->dtoa.p5s; if (!p5) { /* first time */ p5 = i2b(625); @@ -700,7 +703,7 @@ pow5mult(Bigint *b, int k) Bfree(b); return NULL; } - _PyRuntime.dtoa.p5s = p5; + interp->dtoa.p5s = p5; p5->next = 0; } for(;;) { diff --git a/Python/pystate.c b/Python/pystate.c index 3c655bf3895850..28606e4f32f71c 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -3,7 +3,8 @@ #include "Python.h" #include "pycore_ceval.h" -#include "pycore_code.h" // stats +#include "pycore_code.h" // stats +#include "pycore_dtoa.h" // _dtoa_state_INIT() #include "pycore_frame.h" #include "pycore_initconfig.h" #include "pycore_object.h" // _PyType_InitCache() @@ -618,6 +619,18 @@ free_interpreter(PyInterpreterState *interp) e.g. by PyMem_RawCalloc() or memset(), or otherwise pre-initialized. The runtime state is not manipulated. Instead it is assumed that the interpreter is getting added to the runtime. + + Note that the main interpreter was statically initialized as part + of the runtime and most state is already set properly. That leaves + a small number of fields to initialize dynamically, as well as some + that are initialized lazily. + + For subinterpreters we memcpy() the main interpreter in + PyInterpreterState_New(), leaving it in the same mostly-initialized + state. The only difference is that the interpreter has some + self-referential state that is statically initializexd to the + main interpreter. We fix those fields here, in addition + to the other dynamically initialized fields. */ static void @@ -645,6 +658,11 @@ init_interpreter(PyInterpreterState *interp, PyConfig_InitPythonConfig(&interp->config); _PyType_InitCache(interp); + if (interp != &runtime->_main_interpreter) { + /* Fix the self-referential, statically initialized fields. */ + interp->dtoa = (struct _dtoa_state)_dtoa_state_INIT(interp); + } + interp->_initialized = 1; } From 880437d4ec65ef35d505eeaff9dad5c6654dbc1a Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 28 Feb 2023 14:16:39 -0700 Subject: [PATCH 214/247] gh-100227: Move _str_replace_inf to PyInterpreterState (gh-102333) https://github.com/python/cpython/issues/100227 --- Include/internal/pycore_global_objects.h | 7 ++++--- Parser/asdl_c.py | 4 +--- Python/Python-ast.c | 4 +--- Python/ast_unparse.c | 19 ++++++++++++++----- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/Include/internal/pycore_global_objects.h b/Include/internal/pycore_global_objects.h index d0461fa7e82e8b..30c7c4e3bbd067 100644 --- a/Include/internal/pycore_global_objects.h +++ b/Include/internal/pycore_global_objects.h @@ -27,8 +27,6 @@ extern "C" { _PyRuntime.cached_objects.NAME struct _Py_cached_objects { - PyObject *str_replace_inf; - PyObject *interned_strings; }; @@ -67,11 +65,14 @@ struct _Py_static_objects { (interp)->cached_objects.NAME struct _Py_interp_cached_objects { - int _not_set; + /* AST */ + PyObject *str_replace_inf; + /* object.__reduce__ */ PyObject *objreduce; PyObject *type_slots_pname; pytype_slotdef *type_slots_ptrs[MAX_EQUIV]; + }; #define _Py_INTERP_STATIC_OBJECT(interp, NAME) \ diff --git a/Parser/asdl_c.py b/Parser/asdl_c.py index db0e597b7a5aa4..b44e303ac2594b 100755 --- a/Parser/asdl_c.py +++ b/Parser/asdl_c.py @@ -1484,9 +1484,7 @@ def generate_ast_fini(module_state, f): for s in module_state: f.write(" Py_CLEAR(state->" + s + ');\n') f.write(textwrap.dedent(""" - if (_PyInterpreterState_Get() == _PyInterpreterState_Main()) { - Py_CLEAR(_Py_CACHED_OBJECT(str_replace_inf)); - } + Py_CLEAR(_Py_INTERP_CACHED_OBJECT(interp, str_replace_inf)); #if !defined(NDEBUG) state->initialized = -1; diff --git a/Python/Python-ast.c b/Python/Python-ast.c index d113c47b95392e..6c878474afb192 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -263,9 +263,7 @@ void _PyAST_Fini(PyInterpreterState *interp) Py_CLEAR(state->vararg); Py_CLEAR(state->withitem_type); - if (_PyInterpreterState_Get() == _PyInterpreterState_Main()) { - Py_CLEAR(_Py_CACHED_OBJECT(str_replace_inf)); - } + Py_CLEAR(_Py_INTERP_CACHED_OBJECT(interp, str_replace_inf)); #if !defined(NDEBUG) state->initialized = -1; diff --git a/Python/ast_unparse.c b/Python/ast_unparse.c index 79b2e2f15ba243..8aff045101cc72 100644 --- a/Python/ast_unparse.c +++ b/Python/ast_unparse.c @@ -1,5 +1,6 @@ #include "Python.h" #include "pycore_ast.h" // expr_ty +#include "pycore_pystate.h" // _PyInterpreterState_GET() #include "pycore_runtime.h" // _Py_ID() #include // DBL_MAX_10_EXP #include @@ -13,7 +14,10 @@ _Py_DECLARE_STR(open_br, "{"); _Py_DECLARE_STR(dbl_open_br, "{{"); _Py_DECLARE_STR(close_br, "}"); _Py_DECLARE_STR(dbl_close_br, "}}"); -#define _str_replace_inf _Py_CACHED_OBJECT(str_replace_inf) + +/* We would statically initialize this if doing so were simple enough. */ +#define _str_replace_inf(interp) \ + _Py_INTERP_CACHED_OBJECT(interp, str_replace_inf) /* Forward declarations for recursion via helper functions. */ static PyObject * @@ -78,10 +82,11 @@ append_repr(_PyUnicodeWriter *writer, PyObject *obj) if ((PyFloat_CheckExact(obj) && Py_IS_INFINITY(PyFloat_AS_DOUBLE(obj))) || PyComplex_CheckExact(obj)) { + PyInterpreterState *interp = _PyInterpreterState_GET(); PyObject *new_repr = PyUnicode_Replace( repr, &_Py_ID(inf), - _str_replace_inf, + _str_replace_inf(interp), -1 ); Py_DECREF(repr); @@ -916,9 +921,13 @@ append_ast_expr(_PyUnicodeWriter *writer, expr_ty e, int level) static int maybe_init_static_strings(void) { - if (!_str_replace_inf && - !(_str_replace_inf = PyUnicode_FromFormat("1e%d", 1 + DBL_MAX_10_EXP))) { - return -1; + PyInterpreterState *interp = _PyInterpreterState_GET(); + if (_str_replace_inf(interp) == NULL) { + PyObject *tmp = PyUnicode_FromFormat("1e%d", 1 + DBL_MAX_10_EXP); + if (tmp == NULL) { + return -1; + } + _str_replace_inf(interp) = tmp; } return 0; } From 360ef843d8fc03aad38ed84f9f45026ab08ca4f4 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Tue, 28 Feb 2023 16:34:06 -0500 Subject: [PATCH 215/247] gh-99108: Add missing md5/sha1 defines to Modules/Setup (#102308) --- Modules/Setup | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/Setup b/Modules/Setup index ff432e2929ec2d..1c6f2f7ea5182d 100644 --- a/Modules/Setup +++ b/Modules/Setup @@ -163,8 +163,8 @@ PYTHONPATH=$(COREPYTHONPATH) # hashing builtins #_blake2 _blake2/blake2module.c _blake2/blake2b_impl.c _blake2/blake2s_impl.c -#_md5 md5module.c -I$(srcdir)/Modules/_hacl/include _hacl/Hacl_Hash_MD5.c -#_sha1 sha1module.c -I$(srcdir)/Modules/_hacl/include _hacl/Hacl_Hash_SHA1.c +#_md5 md5module.c -I$(srcdir)/Modules/_hacl/include _hacl/Hacl_Hash_MD5.c -D_BSD_SOURCE -D_DEFAULT_SOURCE +#_sha1 sha1module.c -I$(srcdir)/Modules/_hacl/include _hacl/Hacl_Hash_SHA1.c -D_BSD_SOURCE -D_DEFAULT_SOURCE #_sha2 sha2module.c -I$(srcdir)/Modules/_hacl/include Modules/_hacl/libHacl_Streaming_SHA2.a #_sha3 _sha3/sha3module.c From 938e36f824c5f834d6b77d47942ad81edd5491d0 Mon Sep 17 00:00:00 2001 From: Max Bachmann Date: Wed, 1 Mar 2023 01:31:21 +0100 Subject: [PATCH 216/247] gh-102336: Remove code specifically for handling Windows 7 (GH-102337) --- ...-02-28-21-17-03.gh-issue-102336.-wL3Tm.rst | 1 + Modules/_winapi.c | 31 +---- Modules/getpath.c | 5 +- Modules/mmapmodule.c | 16 +-- Modules/posixmodule.c | 112 +++++------------- Modules/socketmodule.c | 42 ++----- Python/fileutils.c | 8 +- 7 files changed, 56 insertions(+), 159 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-28-21-17-03.gh-issue-102336.-wL3Tm.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-28-21-17-03.gh-issue-102336.-wL3Tm.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-28-21-17-03.gh-issue-102336.-wL3Tm.rst new file mode 100644 index 00000000000000..0c3e4bd4b86094 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-28-21-17-03.gh-issue-102336.-wL3Tm.rst @@ -0,0 +1 @@ +Cleanup Windows 7 specific special handling. Patch by Max Bachmann. diff --git a/Modules/_winapi.c b/Modules/_winapi.c index f4d982b15d402a..8c4c725b9df256 100644 --- a/Modules/_winapi.c +++ b/Modules/_winapi.c @@ -63,23 +63,6 @@ #define T_HANDLE T_POINTER -/* Grab CancelIoEx dynamically from kernel32 */ -static int has_CancelIoEx = -1; -static BOOL (CALLBACK *Py_CancelIoEx)(HANDLE, LPOVERLAPPED); - -static int -check_CancelIoEx() -{ - if (has_CancelIoEx == -1) - { - HINSTANCE hKernel32 = GetModuleHandle("KERNEL32"); - * (FARPROC *) &Py_CancelIoEx = GetProcAddress(hKernel32, - "CancelIoEx"); - has_CancelIoEx = (Py_CancelIoEx != NULL); - } - return has_CancelIoEx; -} - typedef struct { PyTypeObject *overlapped_type; } WinApiState; @@ -134,8 +117,7 @@ overlapped_dealloc(OverlappedObject *self) PyObject_GC_UnTrack(self); if (self->pending) { - if (check_CancelIoEx() && - Py_CancelIoEx(self->handle, &self->overlapped) && + if (CancelIoEx(self->handle, &self->overlapped) && GetOverlappedResult(self->handle, &self->overlapped, &bytes, TRUE)) { /* The operation is no longer pending -- nothing to do. */ @@ -306,10 +288,7 @@ _winapi_Overlapped_cancel_impl(OverlappedObject *self) if (self->pending) { Py_BEGIN_ALLOW_THREADS - if (check_CancelIoEx()) - res = Py_CancelIoEx(self->handle, &self->overlapped); - else - res = CancelIo(self->handle); + CancelIoEx(self->handle, &self->overlapped); Py_END_ALLOW_THREADS } @@ -655,8 +634,10 @@ _winapi_CreateJunction_impl(PyObject *module, LPCWSTR src_path, cleanup: ret = GetLastError(); - CloseHandle(token); - CloseHandle(junction); + if (token != NULL) + CloseHandle(token); + if (junction != NULL) + CloseHandle(junction); PyMem_RawFree(rdb); if (ret != 0) diff --git a/Modules/getpath.c b/Modules/getpath.c index 13db010649fed8..c807a3c8e9a2b9 100644 --- a/Modules/getpath.c +++ b/Modules/getpath.c @@ -227,12 +227,11 @@ getpath_isxfile(PyObject *Py_UNUSED(self), PyObject *args) path = PyUnicode_AsWideCharString(pathobj, &cchPath); if (path) { #ifdef MS_WINDOWS - const wchar_t *ext; DWORD attr = GetFileAttributesW(path); r = (attr != INVALID_FILE_ATTRIBUTES) && !(attr & FILE_ATTRIBUTE_DIRECTORY) && - SUCCEEDED(PathCchFindExtension(path, cchPath + 1, &ext)) && - (CompareStringOrdinal(ext, -1, L".exe", -1, 1 /* ignore case */) == CSTR_EQUAL) + (cchPath >= 4) && + (CompareStringOrdinal(path + cchPath - 4, -1, L".exe", -1, 1 /* ignore case */) == CSTR_EQUAL) ? Py_True : Py_False; #else struct stat st; diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c index a01e798265c5a5..6054c2853d7a78 100644 --- a/Modules/mmapmodule.c +++ b/Modules/mmapmodule.c @@ -502,7 +502,7 @@ mmap_resize_method(mmap_object *self, CloseHandle(self->map_handle); /* if the file mapping still exists, it cannot be resized. */ if (self->tagname) { - self->map_handle = OpenFileMapping(FILE_MAP_WRITE, FALSE, + self->map_handle = OpenFileMappingA(FILE_MAP_WRITE, FALSE, self->tagname); if (self->map_handle) { PyErr_SetFromWindowsErr(ERROR_USER_MAPPED_FILE); @@ -531,7 +531,7 @@ mmap_resize_method(mmap_object *self, /* create a new file mapping and map a new view */ /* FIXME: call CreateFileMappingW with wchar_t tagname */ - self->map_handle = CreateFileMapping( + self->map_handle = CreateFileMappingA( self->file_handle, NULL, PAGE_READWRITE, @@ -1514,12 +1514,12 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict) off_lo = (DWORD)(offset & 0xFFFFFFFF); /* For files, it would be sufficient to pass 0 as size. For anonymous maps, we have to pass the size explicitly. */ - m_obj->map_handle = CreateFileMapping(m_obj->file_handle, - NULL, - flProtect, - size_hi, - size_lo, - m_obj->tagname); + m_obj->map_handle = CreateFileMappingA(m_obj->file_handle, + NULL, + flProtect, + size_hi, + size_lo, + m_obj->tagname); if (m_obj->map_handle != NULL) { m_obj->data = (char *) MapViewOfFile(m_obj->map_handle, dwDesiredAccess, diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 6ea216b3bf5e79..937233a3bd0779 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -10,16 +10,6 @@ #define PY_SSIZE_T_CLEAN #include "Python.h" -// Include before pycore internal headers. FSCTL_GET_REPARSE_POINT -// is not exported by if the WIN32_LEAN_AND_MEAN macro is defined, -// whereas pycore_condvar.h defines the WIN32_LEAN_AND_MEAN macro. -#ifdef MS_WINDOWS -# include -# include -# include // UNLEN -# include "osdefs.h" // SEP -# define HAVE_SYMLINK -#endif #ifdef __VXWORKS__ # include "pycore_bitutils.h" // _Py_popcount32() @@ -34,6 +24,15 @@ #include "pycore_pystate.h" // _PyInterpreterState_GET() #include "pycore_signal.h" // Py_NSIG +#ifdef MS_WINDOWS +# include +# include +# include +# include // UNLEN +# include "osdefs.h" // SEP +# define HAVE_SYMLINK +#endif + #include "structmember.h" // PyMemberDef #ifndef MS_WINDOWS # include "posixmodule.h" @@ -1507,32 +1506,6 @@ _Py_Sigset_Converter(PyObject *obj, void *addr) } #endif /* HAVE_SIGSET_T */ -#ifdef MS_WINDOWS - -static int -win32_get_reparse_tag(HANDLE reparse_point_handle, ULONG *reparse_tag) -{ - char target_buffer[_Py_MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; - _Py_REPARSE_DATA_BUFFER *rdb = (_Py_REPARSE_DATA_BUFFER *)target_buffer; - DWORD n_bytes_returned; - - if (0 == DeviceIoControl( - reparse_point_handle, - FSCTL_GET_REPARSE_POINT, - NULL, 0, /* in buffer */ - target_buffer, sizeof(target_buffer), - &n_bytes_returned, - NULL)) /* we're not using OVERLAPPED_IO */ - return FALSE; - - if (reparse_tag) - *reparse_tag = rdb->ReparseTag; - - return TRUE; -} - -#endif /* MS_WINDOWS */ - /* Return a dictionary corresponding to the POSIX environment table */ #if defined(WITH_NEXT_FRAMEWORK) || (defined(__APPLE__) && defined(Py_ENABLE_SHARED)) /* On Darwin/MacOSX a shared library or framework has no access to @@ -8263,42 +8236,32 @@ os_setpgrp_impl(PyObject *module) #ifdef HAVE_GETPPID #ifdef MS_WINDOWS -#include +#include static PyObject* win32_getppid() { - HANDLE snapshot; + DWORD error; PyObject* result = NULL; - BOOL have_record; - PROCESSENTRY32 pe; - - DWORD mypid = GetCurrentProcessId(); /* This function never fails */ - - snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - if (snapshot == INVALID_HANDLE_VALUE) - return PyErr_SetFromWindowsErr(GetLastError()); + HANDLE process = GetCurrentProcess(); - pe.dwSize = sizeof(pe); - have_record = Process32First(snapshot, &pe); - while (have_record) { - if (mypid == pe.th32ProcessID) { - /* We could cache the ulong value in a static variable. */ - result = PyLong_FromUnsignedLong(pe.th32ParentProcessID); - break; - } - - have_record = Process32Next(snapshot, &pe); + HPSS snapshot = NULL; + error = PssCaptureSnapshot(process, PSS_CAPTURE_NONE, 0, &snapshot); + if (error != ERROR_SUCCESS) { + return PyErr_SetFromWindowsErr(error); } - /* If our loop exits and our pid was not found (result will be NULL) - * then GetLastError will return ERROR_NO_MORE_FILES. This is an - * error anyway, so let's raise it. */ - if (!result) - result = PyErr_SetFromWindowsErr(GetLastError()); - - CloseHandle(snapshot); + PSS_PROCESS_INFORMATION info; + error = PssQuerySnapshot(snapshot, PSS_QUERY_PROCESS_INFORMATION, &info, + sizeof(info)); + if (error == ERROR_SUCCESS) { + result = PyLong_FromUnsignedLong(info.ParentProcessId); + } + else { + result = PyErr_SetFromWindowsErr(error); + } + PssFreeSnapshot(process, snapshot); return result; } #endif /*MS_WINDOWS*/ @@ -15067,9 +15030,6 @@ os_getrandom_impl(PyObject *module, Py_ssize_t size, int flags) * on win32 */ -typedef DLL_DIRECTORY_COOKIE (WINAPI *PAddDllDirectory)(PCWSTR newDirectory); -typedef BOOL (WINAPI *PRemoveDllDirectory)(DLL_DIRECTORY_COOKIE cookie); - /*[clinic input] os._add_dll_directory @@ -15089,8 +15049,6 @@ static PyObject * os__add_dll_directory_impl(PyObject *module, path_t *path) /*[clinic end generated code: output=80b025daebb5d683 input=1de3e6c13a5808c8]*/ { - HMODULE hKernel32; - PAddDllDirectory AddDllDirectory; DLL_DIRECTORY_COOKIE cookie = 0; DWORD err = 0; @@ -15098,14 +15056,8 @@ os__add_dll_directory_impl(PyObject *module, path_t *path) return NULL; } - /* For Windows 7, we have to load this. As this will be a fairly - infrequent operation, just do it each time. Kernel32 is always - loaded. */ Py_BEGIN_ALLOW_THREADS - if (!(hKernel32 = GetModuleHandleW(L"kernel32")) || - !(AddDllDirectory = (PAddDllDirectory)GetProcAddress( - hKernel32, "AddDllDirectory")) || - !(cookie = (*AddDllDirectory)(path->wide))) { + if (!(cookie = AddDllDirectory(path->wide))) { err = GetLastError(); } Py_END_ALLOW_THREADS @@ -15134,8 +15086,6 @@ static PyObject * os__remove_dll_directory_impl(PyObject *module, PyObject *cookie) /*[clinic end generated code: output=594350433ae535bc input=c1d16a7e7d9dc5dc]*/ { - HMODULE hKernel32; - PRemoveDllDirectory RemoveDllDirectory; DLL_DIRECTORY_COOKIE cookieValue; DWORD err = 0; @@ -15148,14 +15098,8 @@ os__remove_dll_directory_impl(PyObject *module, PyObject *cookie) cookieValue = (DLL_DIRECTORY_COOKIE)PyCapsule_GetPointer( cookie, "DLL directory cookie"); - /* For Windows 7, we have to load this. As this will be a fairly - infrequent operation, just do it each time. Kernel32 is always - loaded. */ Py_BEGIN_ALLOW_THREADS - if (!(hKernel32 = GetModuleHandleW(L"kernel32")) || - !(RemoveDllDirectory = (PRemoveDllDirectory)GetProcAddress( - hKernel32, "RemoveDllDirectory")) || - !(*RemoveDllDirectory)(cookieValue)) { + if (!RemoveDllDirectory(cookieValue)) { err = GetLastError(); } Py_END_ALLOW_THREADS diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 00608be38f61bb..43a0cc0f963f9d 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -606,11 +606,6 @@ select_error(void) # define SUPPRESS_DEPRECATED_CALL #endif -#ifdef MS_WINDOWS -/* Does WSASocket() support the WSA_FLAG_NO_HANDLE_INHERIT flag? */ -static int support_wsa_no_inherit = -1; -#endif - /* Convenience function to raise an error according to errno and return a NULL pointer from a function. */ @@ -5342,6 +5337,13 @@ sock_initobj_impl(PySocketSockObject *self, int family, int type, int proto, set_error(); return -1; } + + if (!SetHandleInformation((HANDLE)fd, HANDLE_FLAG_INHERIT, 0)) { + closesocket(fd); + PyErr_SetFromWindowsErr(0); + return -1; + } + family = info.iAddressFamily; type = info.iSocketType; proto = info.iProtocol; @@ -5438,33 +5440,15 @@ sock_initobj_impl(PySocketSockObject *self, int family, int type, int proto, #endif Py_BEGIN_ALLOW_THREADS - if (support_wsa_no_inherit) { - fd = WSASocketW(family, type, proto, - NULL, 0, - WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT); - if (fd == INVALID_SOCKET) { - /* Windows 7 or Windows 2008 R2 without SP1 or the hotfix */ - support_wsa_no_inherit = 0; - fd = socket(family, type, proto); - } - } - else { - fd = socket(family, type, proto); - } + fd = WSASocketW(family, type, proto, + NULL, 0, + WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT); Py_END_ALLOW_THREADS if (fd == INVALID_SOCKET) { set_error(); return -1; } - - if (!support_wsa_no_inherit) { - if (!SetHandleInformation((HANDLE)fd, HANDLE_FLAG_INHERIT, 0)) { - closesocket(fd); - PyErr_SetFromWindowsErr(0); - return -1; - } - } #else /* UNIX */ Py_BEGIN_ALLOW_THREADS @@ -7340,12 +7324,6 @@ PyInit__socket(void) if (!os_init()) return NULL; -#ifdef MS_WINDOWS - if (support_wsa_no_inherit == -1) { - support_wsa_no_inherit = IsWindows7SP1OrGreater(); - } -#endif - Py_SET_TYPE(&sock_type, &PyType_Type); m = PyModule_Create(&socketmodule); if (m == NULL) diff --git a/Python/fileutils.c b/Python/fileutils.c index 897c2f9f4ea160..93bee9ee007cd4 100644 --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -1362,17 +1362,11 @@ set_inheritable(int fd, int inheritable, int raise, int *atomic_flag_works) else flags = 0; - /* This check can be removed once support for Windows 7 ends. */ -#define CONSOLE_PSEUDOHANDLE(handle) (((ULONG_PTR)(handle) & 0x3) == 0x3 && \ - GetFileType(handle) == FILE_TYPE_CHAR) - - if (!CONSOLE_PSEUDOHANDLE(handle) && - !SetHandleInformation(handle, HANDLE_FLAG_INHERIT, flags)) { + if (!SetHandleInformation(handle, HANDLE_FLAG_INHERIT, flags)) { if (raise) PyErr_SetFromWindowsErr(0); return -1; } -#undef CONSOLE_PSEUDOHANDLE return 0; #else From 7d1d66341838d7d1963c9ee7ffca2950d3a751fd Mon Sep 17 00:00:00 2001 From: Inada Naoki Date: Wed, 1 Mar 2023 09:48:15 +0900 Subject: [PATCH 217/247] Doc: Fix minor error in ePub (GH-100614) Fix issue reported https://mail.python.org/archives/list/docs@python.org/message/KE7OIAO53P4XRC4ZOWPDHA63ZQJCHEC3/ --- Doc/conf.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/conf.py b/Doc/conf.py index b3da8fa9ec4497..29fb63cbcc8614 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -268,7 +268,7 @@ ogp_site_name = 'Python documentation' ogp_image = '_static/og-image.png' ogp_custom_meta_tags = [ - '', - '', - '', + '', + '', + '', ] From f91846ba390f4437bcd525dfe6ed4355a894e159 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Wed, 1 Mar 2023 09:49:23 +0000 Subject: [PATCH 218/247] gh-102192: Replace PyErr_Fetch/Restore etc by more efficient alternatives in tkinter module (#102319) --- Modules/_tkinter.c | 58 +++++++++++++++++++--------------------------- 1 file changed, 24 insertions(+), 34 deletions(-) diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index d4a129058702a2..1608939766ffb6 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -321,8 +321,6 @@ static PyObject *Tkinter_TclError; static int quitMainLoop = 0; static int errorInCmd = 0; static PyObject *excInCmd; -static PyObject *valInCmd; -static PyObject *trbInCmd; #ifdef TKINTER_PROTECT_LOADTK static int tk_load_failed = 0; @@ -1222,7 +1220,7 @@ typedef struct Tkapp_CallEvent { PyObject *args; int flags; PyObject **res; - PyObject **exc_type, **exc_value, **exc_tb; + PyObject **exc; Tcl_Condition *done; } Tkapp_CallEvent; @@ -1339,7 +1337,7 @@ Tkapp_CallProc(Tkapp_CallEvent *e, int flags) ENTER_PYTHON objv = Tkapp_CallArgs(e->args, objStore, &objc); if (!objv) { - PyErr_Fetch(e->exc_type, e->exc_value, e->exc_tb); + *(e->exc) = PyErr_GetRaisedException(); *(e->res) = NULL; } LEAVE_PYTHON @@ -1354,7 +1352,7 @@ Tkapp_CallProc(Tkapp_CallEvent *e, int flags) *(e->res) = Tkapp_ObjectResult(e->self); } if (*(e->res) == NULL) { - PyErr_Fetch(e->exc_type, e->exc_value, e->exc_tb); + *(e->exc) = PyErr_GetRaisedException(); } LEAVE_PYTHON @@ -1401,7 +1399,7 @@ Tkapp_Call(PyObject *selfptr, PyObject *args) marshal the parameters to the interpreter thread. */ Tkapp_CallEvent *ev; Tcl_Condition cond = NULL; - PyObject *exc_type, *exc_value, *exc_tb; + PyObject *exc; if (!WaitForMainloop(self)) return NULL; ev = (Tkapp_CallEvent*)attemptckalloc(sizeof(Tkapp_CallEvent)); @@ -1413,18 +1411,18 @@ Tkapp_Call(PyObject *selfptr, PyObject *args) ev->self = self; ev->args = args; ev->res = &res; - ev->exc_type = &exc_type; - ev->exc_value = &exc_value; - ev->exc_tb = &exc_tb; + ev->exc = &exc; ev->done = &cond; Tkapp_ThreadSend(self, (Tcl_Event*)ev, &cond, &call_mutex); if (res == NULL) { - if (exc_type) - PyErr_Restore(exc_type, exc_value, exc_tb); - else - PyErr_SetObject(Tkinter_TclError, exc_value); + if (exc) { + PyErr_SetRaisedException(exc); + } + else { + PyErr_SetObject(Tkinter_TclError, exc); + } } Tcl_ConditionFinalize(&cond); } @@ -1578,8 +1576,7 @@ typedef struct VarEvent { int flags; EventFunc func; PyObject **res; - PyObject **exc_type; - PyObject **exc_val; + PyObject **exc; Tcl_Condition *cond; } VarEvent; @@ -1643,12 +1640,7 @@ var_perform(VarEvent *ev) { *(ev->res) = ev->func(ev->self, ev->args, ev->flags); if (!*(ev->res)) { - PyObject *exc, *val, *tb; - PyErr_Fetch(&exc, &val, &tb); - PyErr_NormalizeException(&exc, &val, &tb); - *(ev->exc_type) = exc; - *(ev->exc_val) = val; - Py_XDECREF(tb); + *(ev->exc) = PyErr_GetRaisedException();; } } @@ -1672,7 +1664,7 @@ var_invoke(EventFunc func, PyObject *selfptr, PyObject *args, int flags) TkappObject *self = (TkappObject*)selfptr; if (self->threaded && self->thread_id != Tcl_GetCurrentThread()) { VarEvent *ev; - PyObject *res, *exc_type, *exc_val; + PyObject *res, *exc; Tcl_Condition cond = NULL; /* The current thread is not the interpreter thread. Marshal @@ -1691,16 +1683,14 @@ var_invoke(EventFunc func, PyObject *selfptr, PyObject *args, int flags) ev->flags = flags; ev->func = func; ev->res = &res; - ev->exc_type = &exc_type; - ev->exc_val = &exc_val; + ev->exc = &exc; ev->cond = &cond; ev->ev.proc = (Tcl_EventProc*)var_proc; Tkapp_ThreadSend(self, (Tcl_Event*)ev, &cond, &var_mutex); Tcl_ConditionFinalize(&cond); if (!res) { - PyErr_SetObject(exc_type, exc_val); - Py_DECREF(exc_type); - Py_DECREF(exc_val); + PyErr_SetObject((PyObject*)Py_TYPE(exc), exc); + Py_DECREF(exc); return NULL; } return res; @@ -2188,7 +2178,7 @@ static int PythonCmd_Error(Tcl_Interp *interp) { errorInCmd = 1; - PyErr_Fetch(&excInCmd, &valInCmd, &trbInCmd); + excInCmd = PyErr_GetRaisedException(); LEAVE_PYTHON return TCL_ERROR; } @@ -2458,7 +2448,7 @@ FileHandler(ClientData clientData, int mask) res = PyObject_CallFunction(func, "Oi", file, mask); if (res == NULL) { errorInCmd = 1; - PyErr_Fetch(&excInCmd, &valInCmd, &trbInCmd); + excInCmd = PyErr_GetRaisedException(); } Py_XDECREF(res); LEAVE_PYTHON @@ -2628,7 +2618,7 @@ TimerHandler(ClientData clientData) if (res == NULL) { errorInCmd = 1; - PyErr_Fetch(&excInCmd, &valInCmd, &trbInCmd); + excInCmd = PyErr_GetRaisedException(); } else Py_DECREF(res); @@ -2725,8 +2715,8 @@ _tkinter_tkapp_mainloop_impl(TkappObject *self, int threshold) if (errorInCmd) { errorInCmd = 0; - PyErr_Restore(excInCmd, valInCmd, trbInCmd); - excInCmd = valInCmd = trbInCmd = NULL; + PyErr_SetRaisedException(excInCmd); + excInCmd = NULL; return NULL; } Py_RETURN_NONE; @@ -3187,8 +3177,8 @@ EventHook(void) #endif if (errorInCmd) { errorInCmd = 0; - PyErr_Restore(excInCmd, valInCmd, trbInCmd); - excInCmd = valInCmd = trbInCmd = NULL; + PyErr_SetRaisedException(excInCmd); + excInCmd = NULL; PyErr_Print(); } PyEval_SaveThread(); From d3d20743ee1ae7e0be17bacd278985cffa864816 Mon Sep 17 00:00:00 2001 From: Max Bachmann Date: Wed, 1 Mar 2023 13:01:39 +0100 Subject: [PATCH 219/247] gh-102336: Ensure CancelIoEx result is not ignored (GH-102347) fix ignored return value --- Modules/_winapi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_winapi.c b/Modules/_winapi.c index 8c4c725b9df256..eefc2571ee8287 100644 --- a/Modules/_winapi.c +++ b/Modules/_winapi.c @@ -288,7 +288,7 @@ _winapi_Overlapped_cancel_impl(OverlappedObject *self) if (self->pending) { Py_BEGIN_ALLOW_THREADS - CancelIoEx(self->handle, &self->overlapped); + res = CancelIoEx(self->handle, &self->overlapped); Py_END_ALLOW_THREADS } From c1748ed59dc30ab99fe69c22bdbab54f93baa57c Mon Sep 17 00:00:00 2001 From: Max Bachmann Date: Wed, 1 Mar 2023 15:50:38 +0100 Subject: [PATCH 220/247] gh-102344: Reimplement winreg QueryValue / SetValue using QueryValueEx / SetValueEx (GH-102345) The newer APIs are more widely available than the old ones, and are called in a way to preserve functionality. --- ...-03-01-01-36-39.gh-issue-102344.Dgfux4.rst | 2 + PC/winreg.c | 182 ++++++++++++------ 2 files changed, 124 insertions(+), 60 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2023-03-01-01-36-39.gh-issue-102344.Dgfux4.rst diff --git a/Misc/NEWS.d/next/Windows/2023-03-01-01-36-39.gh-issue-102344.Dgfux4.rst b/Misc/NEWS.d/next/Windows/2023-03-01-01-36-39.gh-issue-102344.Dgfux4.rst new file mode 100644 index 00000000000000..4804212be8182c --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-03-01-01-36-39.gh-issue-102344.Dgfux4.rst @@ -0,0 +1,2 @@ +Implement ``winreg.QueryValue`` using ``QueryValueEx`` and +``winreg.SetValue`` using ``SetValueEx``. Patch by Max Bachmann. diff --git a/PC/winreg.c b/PC/winreg.c index 63b37be526ab80..86efed09855b01 100644 --- a/PC/winreg.c +++ b/PC/winreg.c @@ -561,7 +561,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; @@ -585,7 +585,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; @@ -1488,53 +1488,77 @@ static PyObject * winreg_QueryValue_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key) /*[clinic end generated code: output=c655810ae50c63a9 input=41cafbbf423b21d6]*/ { - long rc; - PyObject *retStr; - wchar_t *retBuf; - DWORD bufSize = 0; - DWORD retSize = 0; - wchar_t *tmp; + LONG rc; + HKEY childKey = key; + WCHAR buf[256], *pbuf = buf; + DWORD size = sizeof(buf); + DWORD type; + Py_ssize_t length; + PyObject *result = NULL; if (PySys_Audit("winreg.QueryValue", "nuu", - (Py_ssize_t)key, sub_key, NULL) < 0) { + (Py_ssize_t)key, sub_key, NULL) < 0) + { return NULL; } - rc = RegQueryValueW(key, sub_key, NULL, &retSize); - if (rc == ERROR_MORE_DATA) - retSize = 256; - else if (rc != ERROR_SUCCESS) - return PyErr_SetFromWindowsErrWithFunction(rc, + + if (key == HKEY_PERFORMANCE_DATA) { + return PyErr_SetFromWindowsErrWithFunction(ERROR_INVALID_HANDLE, "RegQueryValue"); + } - bufSize = retSize; - retBuf = (wchar_t *) PyMem_Malloc(bufSize); - if (retBuf == NULL) - return PyErr_NoMemory(); + if (sub_key && sub_key[0]) { + Py_BEGIN_ALLOW_THREADS + rc = RegOpenKeyExW(key, sub_key, 0, KEY_QUERY_VALUE, &childKey); + Py_END_ALLOW_THREADS + if (rc != ERROR_SUCCESS) { + return PyErr_SetFromWindowsErrWithFunction(rc, "RegOpenKeyEx"); + } + } while (1) { - retSize = bufSize; - rc = RegQueryValueW(key, sub_key, retBuf, &retSize); - if (rc != ERROR_MORE_DATA) + Py_BEGIN_ALLOW_THREADS + rc = RegQueryValueExW(childKey, NULL, NULL, &type, (LPBYTE)pbuf, + &size); + Py_END_ALLOW_THREADS + if (rc != ERROR_MORE_DATA) { break; - - bufSize *= 2; - tmp = (wchar_t *) PyMem_Realloc(retBuf, bufSize); + } + void *tmp = PyMem_Realloc(pbuf != buf ? pbuf : NULL, size); if (tmp == NULL) { - PyMem_Free(retBuf); - return PyErr_NoMemory(); + PyErr_NoMemory(); + goto exit; } - retBuf = tmp; + pbuf = tmp; } - if (rc != ERROR_SUCCESS) { - PyMem_Free(retBuf); - return PyErr_SetFromWindowsErrWithFunction(rc, - "RegQueryValue"); + if (rc == ERROR_SUCCESS) { + if (type != REG_SZ) { + PyErr_SetFromWindowsErrWithFunction(ERROR_INVALID_DATA, + "RegQueryValue"); + goto exit; + } + length = wcsnlen(pbuf, size / sizeof(WCHAR)); + } + else if (rc == ERROR_FILE_NOT_FOUND) { + // Return an empty string if there's no default value. + length = 0; + } + else { + PyErr_SetFromWindowsErrWithFunction(rc, "RegQueryValueEx"); + goto exit; } - retStr = PyUnicode_FromWideChar(retBuf, wcslen(retBuf)); - PyMem_Free(retBuf); - return retStr; + result = PyUnicode_FromWideChar(pbuf, length); + +exit: + if (pbuf != buf) { + PyMem_Free(pbuf); + } + if (childKey != key) { + RegCloseKey(childKey); + } + return result; } @@ -1687,38 +1711,69 @@ winreg_SetValue_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key, DWORD type, PyObject *value_obj) /*[clinic end generated code: output=d4773dc9c372311a input=bf088494ae2d24fd]*/ { - Py_ssize_t value_length; - long rc; + LONG rc; + HKEY childKey = key; + LPWSTR value; + Py_ssize_t size; + Py_ssize_t length; + PyObject *result = NULL; if (type != REG_SZ) { PyErr_SetString(PyExc_TypeError, "type must be winreg.REG_SZ"); return NULL; } - wchar_t *value = PyUnicode_AsWideCharString(value_obj, &value_length); + value = PyUnicode_AsWideCharString(value_obj, &length); if (value == NULL) { return NULL; } - if ((Py_ssize_t)(DWORD)value_length != value_length) { + + size = (length + 1) * sizeof(WCHAR); + if ((Py_ssize_t)(DWORD)size != size) { PyErr_SetString(PyExc_OverflowError, "value is too long"); - PyMem_Free(value); - return NULL; + goto exit; } if (PySys_Audit("winreg.SetValue", "nunu#", (Py_ssize_t)key, sub_key, (Py_ssize_t)type, - value, value_length) < 0) { - PyMem_Free(value); - return NULL; + value, length) < 0) + { + goto exit; + } + + if (key == HKEY_PERFORMANCE_DATA) { + PyErr_SetFromWindowsErrWithFunction(ERROR_INVALID_HANDLE, + "RegSetValue"); + goto exit; + } + + if (sub_key && sub_key[0]) { + Py_BEGIN_ALLOW_THREADS + rc = RegCreateKeyExW(key, sub_key, 0, NULL, 0, KEY_SET_VALUE, NULL, + &childKey, NULL); + Py_END_ALLOW_THREADS + if (rc != ERROR_SUCCESS) { + PyErr_SetFromWindowsErrWithFunction(rc, "RegCreateKeyEx"); + goto exit; + } } Py_BEGIN_ALLOW_THREADS - rc = RegSetValueW(key, sub_key, REG_SZ, value, (DWORD)(value_length + 1)); + rc = RegSetValueExW(childKey, NULL, 0, REG_SZ, (LPBYTE)value, (DWORD)size); Py_END_ALLOW_THREADS + if (rc == ERROR_SUCCESS) { + result = Py_NewRef(Py_None); + } + else { + PyErr_SetFromWindowsErrWithFunction(rc, "RegSetValueEx"); + } + +exit: PyMem_Free(value); - if (rc != ERROR_SUCCESS) - return PyErr_SetFromWindowsErrWithFunction(rc, "RegSetValue"); - Py_RETURN_NONE; + if (childKey != key) { + RegCloseKey(childKey); + } + return result; } /*[clinic input] @@ -1771,32 +1826,39 @@ winreg_SetValueEx_impl(PyObject *module, HKEY key, DWORD type, PyObject *value) /*[clinic end generated code: output=811b769a66ae11b7 input=900a9e3990bfb196]*/ { - BYTE *data; - DWORD len; - LONG rc; + BYTE *data = NULL; + DWORD size; + PyObject *result = NULL; - if (!Py2Reg(value, type, &data, &len)) + if (!Py2Reg(value, type, &data, &size)) { - if (!PyErr_Occurred()) + if (!PyErr_Occurred()) { PyErr_SetString(PyExc_ValueError, "Could not convert the data to the specified type."); + } return NULL; } if (PySys_Audit("winreg.SetValue", "nunO", (Py_ssize_t)key, value_name, (Py_ssize_t)type, - value) < 0) { - PyMem_Free(data); - return NULL; + value) < 0) + { + goto exit; } + Py_BEGIN_ALLOW_THREADS - rc = RegSetValueExW(key, value_name, 0, type, data, len); + rc = RegSetValueExW(key, value_name, 0, type, data, size); Py_END_ALLOW_THREADS + if (rc == ERROR_SUCCESS) { + result = Py_NewRef(Py_None); + } + else { + PyErr_SetFromWindowsErrWithFunction(rc, "RegSetValueEx"); + } + +exit: PyMem_Free(data); - if (rc != ERROR_SUCCESS) - return PyErr_SetFromWindowsErrWithFunction(rc, - "RegSetValueEx"); - Py_RETURN_NONE; + return result; } /*[clinic input] From 2f62a5da949cd368a9498e6a03e700f4629fa97f Mon Sep 17 00:00:00 2001 From: Hyunkyun Moon Date: Wed, 1 Mar 2023 23:56:19 +0900 Subject: [PATCH 221/247] gh-95672 skip fcntl when pipesize is smaller than pagesize (gh-102163) --- Doc/library/test.rst | 7 +++++++ Lib/test/support/__init__.py | 14 ++++++++++++++ Lib/test/test_fcntl.py | 5 +++-- Lib/test/test_subprocess.py | 3 ++- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/Doc/library/test.rst b/Doc/library/test.rst index 8199a27d7d9c4e..3c759648e4e626 100644 --- a/Doc/library/test.rst +++ b/Doc/library/test.rst @@ -536,6 +536,13 @@ The :mod:`test.support` module defines the following functions: :func:`doctest.testmod`. +.. function:: get_pagesize() + + Get size of a page in bytes. + + .. versionadded:: 3.12 + + .. function:: setswitchinterval(interval) Set the :func:`sys.setswitchinterval` to the given *interval*. Defines diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 4a22ccdd4db403..c309fd7910e0e6 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -51,6 +51,8 @@ # sys "is_jython", "is_android", "is_emscripten", "is_wasi", "check_impl_detail", "unix_shell", "setswitchinterval", + # os + "get_pagesize", # network "open_urlresource", # processes @@ -1893,6 +1895,18 @@ def setswitchinterval(interval): return sys.setswitchinterval(interval) +def get_pagesize(): + """Get size of a page in bytes.""" + try: + page_size = os.sysconf('SC_PAGESIZE') + except (ValueError, AttributeError): + try: + page_size = os.sysconf('SC_PAGE_SIZE') + except (ValueError, AttributeError): + page_size = 4096 + return page_size + + @contextlib.contextmanager def disable_faulthandler(): import faulthandler diff --git a/Lib/test/test_fcntl.py b/Lib/test/test_fcntl.py index 26de67d924272a..5da75615b41d79 100644 --- a/Lib/test/test_fcntl.py +++ b/Lib/test/test_fcntl.py @@ -6,7 +6,7 @@ import struct import sys import unittest -from test.support import verbose, cpython_only +from test.support import verbose, cpython_only, get_pagesize from test.support.import_helper import import_module from test.support.os_helper import TESTFN, unlink @@ -201,7 +201,8 @@ def test_fcntl_f_pipesize(self): # Get the default pipesize with F_GETPIPE_SZ pipesize_default = fcntl.fcntl(test_pipe_w, fcntl.F_GETPIPE_SZ) pipesize = pipesize_default // 2 # A new value to detect change. - if pipesize < 512: # the POSIX minimum + pagesize_default = get_pagesize() + if pipesize < pagesize_default: # the POSIX minimum raise unittest.SkipTest( 'default pipesize too small to perform test.') fcntl.fcntl(test_pipe_w, fcntl.F_SETPIPE_SZ, pipesize) diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 727b0e6dc578c2..3880125807f235 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -717,7 +717,8 @@ def test_pipesizes(self): os.close(test_pipe_r) os.close(test_pipe_w) pipesize = pipesize_default // 2 - if pipesize < 512: # the POSIX minimum + pagesize_default = support.get_pagesize() + if pipesize < pagesize_default: # the POSIX minimum raise unittest.SkipTest( 'default pipesize too small to perform test.') p = subprocess.Popen( From eaae563b6878aa050b4ad406b67728b6b066220e Mon Sep 17 00:00:00 2001 From: Stefan Pochmann <609905+pochmann@users.noreply.github.com> Date: Thu, 2 Mar 2023 04:16:23 +0100 Subject: [PATCH 222/247] gh-102088 Optimize iter_index itertools recipe (GH-102360) --- Doc/library/itertools.rst | 9 ++++++--- Lib/test/test_operator.py | 3 +++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index 95da7166842ee6..d85a17effb04a2 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -886,9 +886,12 @@ which incur interpreter overhead. except AttributeError: # Slow path for general iterables it = islice(iterable, start, None) - for i, element in enumerate(it, start): - if element is value or element == value: - yield i + i = start - 1 + try: + while True: + yield (i := i + operator.indexOf(it, value) + 1) + except ValueError: + pass else: # Fast path for sequences i = start - 1 diff --git a/Lib/test/test_operator.py b/Lib/test/test_operator.py index b7e38c23349878..1db738d228b1b9 100644 --- a/Lib/test/test_operator.py +++ b/Lib/test/test_operator.py @@ -208,6 +208,9 @@ def test_indexOf(self): nan = float("nan") self.assertEqual(operator.indexOf([nan, nan, 21], nan), 0) self.assertEqual(operator.indexOf([{}, 1, {}, 2], {}), 0) + it = iter('leave the iterator at exactly the position after the match') + self.assertEqual(operator.indexOf(it, 'a'), 2) + self.assertEqual(next(it), 'v') def test_invert(self): operator = self.module From 60597439ef482a840f8ffc76eb6c27f3ba773d9c Mon Sep 17 00:00:00 2001 From: Hyunkyun Moon Date: Thu, 2 Mar 2023 20:10:08 +0900 Subject: [PATCH 223/247] gh-95672: Update memory_watchdog to use test.support.get_pagesize (gh-102365) --- Lib/test/memory_watchdog.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/Lib/test/memory_watchdog.py b/Lib/test/memory_watchdog.py index 88cca8d323a636..fee062ecc9b300 100644 --- a/Lib/test/memory_watchdog.py +++ b/Lib/test/memory_watchdog.py @@ -5,20 +5,13 @@ # If the process crashes, reading from the /proc entry will fail with ESRCH. -import os import sys import time +from test.support import get_pagesize -try: - page_size = os.sysconf('SC_PAGESIZE') -except (ValueError, AttributeError): - try: - page_size = os.sysconf('SC_PAGE_SIZE') - except (ValueError, AttributeError): - page_size = 4096 - while True: + page_size = get_pagesize() sys.stdin.seek(0) statm = sys.stdin.read() data = int(statm.split()[5]) From ed55c69ebd74178115cd8b080f7f8e7588cd5fda Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Thu, 2 Mar 2023 20:32:05 +0900 Subject: [PATCH 224/247] gh-101101: Fix test_code_extra to reset value for refleak test (gh-102350) --- Modules/_testcapi/code.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Modules/_testcapi/code.c b/Modules/_testcapi/code.c index 588dc67971ea5a..84c668cd6b3b00 100644 --- a/Modules/_testcapi/code.c +++ b/Modules/_testcapi/code.c @@ -92,7 +92,11 @@ test_code_extra(PyObject* self, PyObject *Py_UNUSED(callable)) goto finally; } assert ((uintptr_t)extra == 77); - + // Revert to initial code extra value. + res = PyUnstable_Code_SetExtra(test_func_code, code_extra_index, NULL); + if (res < 0) { + goto finally; + } result = Py_NewRef(Py_None); finally: Py_XDECREF(test_module); From 73250000ac7d6a5e41917e8bcea7234444cbde78 Mon Sep 17 00:00:00 2001 From: Michael K Date: Thu, 2 Mar 2023 16:26:49 +0100 Subject: [PATCH 225/247] Fix typos in documentation and comments (GH-102374) Found some duplicate `to`s in the documentation and some code comments and fixed them. [Misc/NEWS.d/3.12.0a1.rst](https://github.com/python/cpython/blob/ed55c69ebd74178115cd8b080f7f8e7588cd5fda/Misc/NEWS.d/3.12.0a1.rst) also contains two duplicate `to`s, but I wasn't sure if it's ok to touch that file. Looks auto generated. I'm happy to amend the PR if requested. :) Automerge-Triggered-By: GH:AlexWaygood --- Doc/library/sqlite3.rst | 2 +- Doc/library/typing.rst | 2 +- Lib/zoneinfo/_zoneinfo.py | 2 +- Objects/typeobject.c | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 18d0a5e630f6a9..ff036ad56acba8 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -72,7 +72,7 @@ including `cursors`_ and `transactions`_. First, we need to create a new database and open a database connection to allow :mod:`!sqlite3` to work with it. -Call :func:`sqlite3.connect` to to create a connection to +Call :func:`sqlite3.connect` to create a connection to the database :file:`tutorial.db` in the current working directory, implicitly creating it if it does not exist: diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 3395e4bfb95c44..80a969e6335abe 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1345,7 +1345,7 @@ These are not used in annotations. They are building blocks for creating generic x: Ts # Not valid x: tuple[Ts] # Not valid - x: tuple[*Ts] # The correct way to to do it + x: tuple[*Ts] # The correct way to do it Type variable tuples can be used in the same contexts as normal type variables. For example, in class definitions, arguments, and return types:: diff --git a/Lib/zoneinfo/_zoneinfo.py b/Lib/zoneinfo/_zoneinfo.py index de68380792f17c..eede15b8271058 100644 --- a/Lib/zoneinfo/_zoneinfo.py +++ b/Lib/zoneinfo/_zoneinfo.py @@ -302,7 +302,7 @@ def _utcoff_to_dstoff(trans_idx, utcoffsets, isdsts): # difference between utcoffset() and the "standard" offset, but # the "base offset" and "DST offset" are not encoded in the file; # we can infer what they are from the isdst flag, but it is not - # sufficient to to just look at the last standard offset, because + # sufficient to just look at the last standard offset, because # occasionally countries will shift both DST offset and base offset. typecnt = len(isdsts) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index b3f1429debc58b..54f1974c6ae3a7 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -3822,11 +3822,11 @@ PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module, res->ht_qualname = Py_NewRef(ht_name); res->ht_name = ht_name; - ht_name = NULL; // Give our reference to to the type + ht_name = NULL; // Give our reference to the type type->tp_name = _ht_tpname; res->_ht_tpname = _ht_tpname; - _ht_tpname = NULL; // Give ownership to to the type + _ht_tpname = NULL; // Give ownership to the type /* Copy the sizes */ From 71db5dbcd714b2e1297c43538188dd69715feb9a Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Thu, 2 Mar 2023 18:38:22 +0000 Subject: [PATCH 226/247] gh-102371: move _Py_Mangle from compile.c to symtable.c (#102372) --- Include/internal/pycore_compile.h | 6 --- Include/internal/pycore_symtable.h | 7 +++ Objects/typeobject.c | 2 +- Python/compile.c | 70 +----------------------------- Python/symtable.c | 67 +++++++++++++++++++++++++++- 5 files changed, 76 insertions(+), 76 deletions(-) diff --git a/Include/internal/pycore_compile.h b/Include/internal/pycore_compile.h index 967fe92a5bc2b2..511f0689c93822 100644 --- a/Include/internal/pycore_compile.h +++ b/Include/internal/pycore_compile.h @@ -19,12 +19,6 @@ PyAPI_FUNC(PyCodeObject*) _PyAST_Compile( int optimize, struct _arena *arena); -int _PyFuture_FromAST( - struct _mod * mod, - PyObject *filename, - PyFutureFeatures* futures); - -extern PyObject* _Py_Mangle(PyObject *p, PyObject *name); typedef struct { int optimize; diff --git a/Include/internal/pycore_symtable.h b/Include/internal/pycore_symtable.h index 8532646ce7d95c..512c4c931f73e4 100644 --- a/Include/internal/pycore_symtable.h +++ b/Include/internal/pycore_symtable.h @@ -90,6 +90,8 @@ PyAPI_FUNC(PySTEntryObject *) PySymtable_Lookup(struct symtable *, void *); extern void _PySymtable_Free(struct symtable *); +extern PyObject* _Py_Mangle(PyObject *p, PyObject *name); + /* Flags for def-use information */ #define DEF_GLOBAL 1 /* global stmt */ @@ -128,6 +130,11 @@ extern struct symtable* _Py_SymtableStringObjectFlags( int start, PyCompilerFlags *flags); +int _PyFuture_FromAST( + struct _mod * mod, + PyObject *filename, + PyFutureFeatures* futures); + #ifdef __cplusplus } #endif diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 54f1974c6ae3a7..981930f58417d8 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -3,7 +3,7 @@ #include "Python.h" #include "pycore_call.h" #include "pycore_code.h" // CO_FAST_FREE -#include "pycore_compile.h" // _Py_Mangle() +#include "pycore_symtable.h" // _Py_Mangle() #include "pycore_dict.h" // _PyDict_KeysSize() #include "pycore_initconfig.h" // _PyStatus_OK() #include "pycore_moduleobject.h" // _PyModule_GetDef() diff --git a/Python/compile.c b/Python/compile.c index b14c637210ff55..2e60a8157533ad 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -29,12 +29,12 @@ #include "Python.h" #include "pycore_ast.h" // _PyAST_GetDocString() #include "pycore_code.h" // _PyCode_New() -#include "pycore_compile.h" // _PyFuture_FromAST() +#include "pycore_compile.h" #include "pycore_intrinsics.h" #include "pycore_long.h" // _PyLong_GetZero() #include "pycore_opcode.h" // _PyOpcode_Caches #include "pycore_pymem.h" // _PyMem_IsPtrFreed() -#include "pycore_symtable.h" // PySTEntryObject +#include "pycore_symtable.h" // PySTEntryObject, _PyFuture_FromAST() #include "opcode_metadata.h" // _PyOpcode_opcode_metadata, _PyOpcode_num_popped/pushed @@ -569,72 +569,6 @@ static PyCodeObject *assemble(struct compiler *, int addNone); #define CAPSULE_NAME "compile.c compiler unit" -PyObject * -_Py_Mangle(PyObject *privateobj, PyObject *ident) -{ - /* Name mangling: __private becomes _classname__private. - This is independent from how the name is used. */ - PyObject *result; - size_t nlen, plen, ipriv; - Py_UCS4 maxchar; - if (privateobj == NULL || !PyUnicode_Check(privateobj) || - PyUnicode_READ_CHAR(ident, 0) != '_' || - PyUnicode_READ_CHAR(ident, 1) != '_') { - return Py_NewRef(ident); - } - nlen = PyUnicode_GET_LENGTH(ident); - plen = PyUnicode_GET_LENGTH(privateobj); - /* Don't mangle __id__ or names with dots. - - The only time a name with a dot can occur is when - we are compiling an import statement that has a - package name. - - TODO(jhylton): Decide whether we want to support - mangling of the module name, e.g. __M.X. - */ - if ((PyUnicode_READ_CHAR(ident, nlen-1) == '_' && - PyUnicode_READ_CHAR(ident, nlen-2) == '_') || - PyUnicode_FindChar(ident, '.', 0, nlen, 1) != -1) { - return Py_NewRef(ident); /* Don't mangle __whatever__ */ - } - /* Strip leading underscores from class name */ - ipriv = 0; - while (PyUnicode_READ_CHAR(privateobj, ipriv) == '_') - ipriv++; - if (ipriv == plen) { - return Py_NewRef(ident); /* Don't mangle if class is just underscores */ - } - plen -= ipriv; - - if (plen + nlen >= PY_SSIZE_T_MAX - 1) { - PyErr_SetString(PyExc_OverflowError, - "private identifier too large to be mangled"); - return NULL; - } - - maxchar = PyUnicode_MAX_CHAR_VALUE(ident); - if (PyUnicode_MAX_CHAR_VALUE(privateobj) > maxchar) - maxchar = PyUnicode_MAX_CHAR_VALUE(privateobj); - - result = PyUnicode_New(1 + nlen + plen, maxchar); - if (!result) { - return NULL; - } - /* ident = "_" + priv[ipriv:] + ident # i.e. 1+plen+nlen bytes */ - PyUnicode_WRITE(PyUnicode_KIND(result), PyUnicode_DATA(result), 0, '_'); - if (PyUnicode_CopyCharacters(result, 1, privateobj, ipriv, plen) < 0) { - Py_DECREF(result); - return NULL; - } - if (PyUnicode_CopyCharacters(result, plen+1, ident, 0, nlen) < 0) { - Py_DECREF(result); - return NULL; - } - assert(_PyUnicode_CheckConsistency(result, 1)); - return result; -} - static int compiler_setup(struct compiler *c, mod_ty mod, PyObject *filename, diff --git a/Python/symtable.c b/Python/symtable.c index 89a2bc437dfa9b..df7473943f3fc1 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -1,6 +1,5 @@ #include "Python.h" #include "pycore_ast.h" // identifier, stmt_ty -#include "pycore_compile.h" // _Py_Mangle(), _PyFuture_FromAST() #include "pycore_parser.h" // _PyParser_ASTFromString() #include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_symtable.h" // PySTEntryObject @@ -2152,3 +2151,69 @@ _Py_SymtableStringObjectFlags(const char *str, PyObject *filename, _PyArena_Free(arena); return st; } + +PyObject * +_Py_Mangle(PyObject *privateobj, PyObject *ident) +{ + /* Name mangling: __private becomes _classname__private. + This is independent from how the name is used. */ + if (privateobj == NULL || !PyUnicode_Check(privateobj) || + PyUnicode_READ_CHAR(ident, 0) != '_' || + PyUnicode_READ_CHAR(ident, 1) != '_') { + return Py_NewRef(ident); + } + size_t nlen = PyUnicode_GET_LENGTH(ident); + size_t plen = PyUnicode_GET_LENGTH(privateobj); + /* Don't mangle __id__ or names with dots. + + The only time a name with a dot can occur is when + we are compiling an import statement that has a + package name. + + TODO(jhylton): Decide whether we want to support + mangling of the module name, e.g. __M.X. + */ + if ((PyUnicode_READ_CHAR(ident, nlen-1) == '_' && + PyUnicode_READ_CHAR(ident, nlen-2) == '_') || + PyUnicode_FindChar(ident, '.', 0, nlen, 1) != -1) { + return Py_NewRef(ident); /* Don't mangle __whatever__ */ + } + /* Strip leading underscores from class name */ + size_t ipriv = 0; + while (PyUnicode_READ_CHAR(privateobj, ipriv) == '_') { + ipriv++; + } + if (ipriv == plen) { + return Py_NewRef(ident); /* Don't mangle if class is just underscores */ + } + plen -= ipriv; + + if (plen + nlen >= PY_SSIZE_T_MAX - 1) { + PyErr_SetString(PyExc_OverflowError, + "private identifier too large to be mangled"); + return NULL; + } + + Py_UCS4 maxchar = PyUnicode_MAX_CHAR_VALUE(ident); + if (PyUnicode_MAX_CHAR_VALUE(privateobj) > maxchar) { + maxchar = PyUnicode_MAX_CHAR_VALUE(privateobj); + } + + PyObject *result = PyUnicode_New(1 + nlen + plen, maxchar); + if (!result) { + return NULL; + } + /* ident = "_" + priv[ipriv:] + ident # i.e. 1+plen+nlen bytes */ + PyUnicode_WRITE(PyUnicode_KIND(result), PyUnicode_DATA(result), 0, '_'); + if (PyUnicode_CopyCharacters(result, 1, privateobj, ipriv, plen) < 0) { + Py_DECREF(result); + return NULL; + } + if (PyUnicode_CopyCharacters(result, plen+1, ident, 0, nlen) < 0) { + Py_DECREF(result); + return NULL; + } + assert(_PyUnicode_CheckConsistency(result, 1)); + return result; +} + From 12011dd8bafa6867f2b4a8a9e8e54cb0fbf006e4 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Fri, 3 Mar 2023 06:59:05 +0300 Subject: [PATCH 227/247] gh-102324: Improve tests of `typing.override` (#102325) Fixes #101564 --- Lib/test/test_typing.py | 99 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index d61dc6e2fbd70b..96496ec9279e3f 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -4171,38 +4171,129 @@ class OverrideDecoratorTests(BaseTestCase): def test_override(self): class Base: def normal_method(self): ... + @classmethod + def class_method_good_order(cls): ... + @classmethod + def class_method_bad_order(cls): ... @staticmethod def static_method_good_order(): ... @staticmethod def static_method_bad_order(): ... - @staticmethod - def decorator_with_slots(): ... class Derived(Base): @override def normal_method(self): return 42 + @classmethod + @override + def class_method_good_order(cls): + return 42 + @override + @classmethod + def class_method_bad_order(cls): + return 42 + @staticmethod @override def static_method_good_order(): return 42 - @override @staticmethod def static_method_bad_order(): return 42 - self.assertIsSubclass(Derived, Base) instance = Derived() self.assertEqual(instance.normal_method(), 42) + self.assertIs(True, Derived.normal_method.__override__) self.assertIs(True, instance.normal_method.__override__) + + self.assertEqual(Derived.class_method_good_order(), 42) + self.assertIs(True, Derived.class_method_good_order.__override__) + self.assertEqual(Derived.class_method_bad_order(), 42) + self.assertIs(False, hasattr(Derived.class_method_bad_order, "__override__")) + self.assertEqual(Derived.static_method_good_order(), 42) self.assertIs(True, Derived.static_method_good_order.__override__) self.assertEqual(Derived.static_method_bad_order(), 42) self.assertIs(False, hasattr(Derived.static_method_bad_order, "__override__")) + # Base object is not changed: + self.assertIs(False, hasattr(Base.normal_method, "__override__")) + self.assertIs(False, hasattr(Base.class_method_good_order, "__override__")) + self.assertIs(False, hasattr(Base.class_method_bad_order, "__override__")) + self.assertIs(False, hasattr(Base.static_method_good_order, "__override__")) + self.assertIs(False, hasattr(Base.static_method_bad_order, "__override__")) + + def test_property(self): + class Base: + @property + def correct(self) -> int: + return 1 + @property + def wrong(self) -> int: + return 1 + + class Child(Base): + @property + @override + def correct(self) -> int: + return 2 + @override + @property + def wrong(self) -> int: + return 2 + + instance = Child() + self.assertEqual(instance.correct, 2) + self.assertTrue(Child.correct.fget.__override__) + self.assertEqual(instance.wrong, 2) + self.assertFalse(hasattr(Child.wrong, "__override__")) + self.assertFalse(hasattr(Child.wrong.fset, "__override__")) + + def test_silent_failure(self): + class CustomProp: + __slots__ = ('fget',) + def __init__(self, fget): + self.fget = fget + def __get__(self, obj, objtype=None): + return self.fget(obj) + + class WithOverride: + @override # must not fail on object with `__slots__` + @CustomProp + def some(self): + return 1 + + self.assertEqual(WithOverride.some, 1) + self.assertFalse(hasattr(WithOverride.some, "__override__")) + + def test_multiple_decorators(self): + import functools + + def with_wraps(f): # similar to `lru_cache` definition + @functools.wraps(f) + def wrapper(*args, **kwargs): + return f(*args, **kwargs) + return wrapper + + class WithOverride: + @override + @with_wraps + def on_top(self, a: int) -> int: + return a + 1 + @with_wraps + @override + def on_bottom(self, a: int) -> int: + return a + 2 + + instance = WithOverride() + self.assertEqual(instance.on_top(1), 2) + self.assertTrue(instance.on_top.__override__) + self.assertEqual(instance.on_bottom(1), 3) + self.assertTrue(instance.on_bottom.__override__) + class CastTests(BaseTestCase): From 4e7c0cbf59595714848cf9827f6e5b40c3985924 Mon Sep 17 00:00:00 2001 From: Owain Davies <116417456+OTheDev@users.noreply.github.com> Date: Fri, 3 Mar 2023 17:51:32 +0700 Subject: [PATCH 228/247] gh-101754: Document that Windows converts keys in `os.environ` to uppercase (GH-101840) --- Doc/library/os.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 85924d0e48366b..23ce98785bedfc 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -201,6 +201,11 @@ process and user. ``'surrogateescape'`` error handler. Use :data:`environb` if you would like to use a different encoding. + On Windows, the keys are converted to uppercase. This also applies when + getting, setting, or deleting an item. For example, + ``environ['monty'] = 'python'`` maps the key ``'MONTY'`` to the value + ``'python'``. + .. note:: Calling :func:`putenv` directly does not change :data:`os.environ`, so it's better From 7b9132057d8f176cb9c40e8324f5122a3132ee58 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Fri, 3 Mar 2023 20:16:50 +0300 Subject: [PATCH 229/247] gh-102383: [docs] Arguments of `PyObject_CopyData` are `PyObject *` (#102390) --- Doc/c-api/buffer.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/c-api/buffer.rst b/Doc/c-api/buffer.rst index a04062fb2a68f1..91d1edd9b2ec46 100644 --- a/Doc/c-api/buffer.rst +++ b/Doc/c-api/buffer.rst @@ -499,7 +499,7 @@ Buffer-related functions This function fails if *len* != *src->len*. -.. c:function:: int PyObject_CopyData(Py_buffer *dest, Py_buffer *src) +.. c:function:: int PyObject_CopyData(PyObject *dest, PyObject *src) Copy data from *src* to *dest* buffer. Can convert between C-style and or Fortran-style buffers. From cb944d0be869dfb1189265467ec8a986176cc104 Mon Sep 17 00:00:00 2001 From: Wagner Alberto Date: Fri, 3 Mar 2023 14:25:31 -0300 Subject: [PATCH 230/247] Add import of `unittest.mock.Mock` in documentation (#102346) --- Doc/library/unittest.mock.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Doc/library/unittest.mock.rst b/Doc/library/unittest.mock.rst index d6d8e5e9557d5c..6c4d801f69f5a9 100644 --- a/Doc/library/unittest.mock.rst +++ b/Doc/library/unittest.mock.rst @@ -72,6 +72,7 @@ available, and then make assertions about how they have been used: :attr:`side_effect` allows you to perform side effects, including raising an exception when a mock is called: + >>> from unittest.mock import Mock >>> mock = Mock(side_effect=KeyError('foo')) >>> mock() Traceback (most recent call last): From 8de59c1bb9fdcea69ff6e6357972ef1b75b71721 Mon Sep 17 00:00:00 2001 From: Jacob Bower <1978924+jbower-fb@users.noreply.github.com> Date: Fri, 3 Mar 2023 20:59:21 -0800 Subject: [PATCH 231/247] gh-102021 : Allow multiple input files for interpreter loop generator (#102022) The input files no longer use `-i`. --- Makefile.pre.in | 4 +- Python/generated_cases.c.h | 3 +- Python/opcode_metadata.h | 5 +- Tools/cases_generator/generate_cases.py | 115 ++++++++++++++++++------ Tools/cases_generator/lexer.py | 2 +- Tools/cases_generator/parser.py | 17 ++-- 6 files changed, 105 insertions(+), 41 deletions(-) diff --git a/Makefile.pre.in b/Makefile.pre.in index b12a1bc060af90..1a1853bf3d7871 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1485,9 +1485,9 @@ regen-cases: PYTHONPATH=$(srcdir)/Tools/cases_generator \ $(PYTHON_FOR_REGEN) \ $(srcdir)/Tools/cases_generator/generate_cases.py \ - -i $(srcdir)/Python/bytecodes.c \ -o $(srcdir)/Python/generated_cases.c.h.new \ - -m $(srcdir)/Python/opcode_metadata.h.new + -m $(srcdir)/Python/opcode_metadata.h.new \ + $(srcdir)/Python/bytecodes.c $(UPDATE_FILE) $(srcdir)/Python/generated_cases.c.h $(srcdir)/Python/generated_cases.c.h.new $(UPDATE_FILE) $(srcdir)/Python/opcode_metadata.h $(srcdir)/Python/opcode_metadata.h.new diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index f59f7c17451c17..82e18505b0d430 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -1,5 +1,6 @@ // This file is generated by Tools/cases_generator/generate_cases.py -// from Python/bytecodes.c +// from: +// Python/bytecodes.c // Do not edit! TARGET(NOP) { diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index f27906a3e349eb..67cb0088c3b789 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -1,5 +1,6 @@ -// This file is generated by Tools/cases_generator/generate_cases.py --metadata -// from Python/bytecodes.c +// This file is generated by Tools/cases_generator/generate_cases.py +// from: +// Python/bytecodes.c // Do not edit! #ifndef NEED_OPCODE_TABLES diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index b760172974c8a5..25cf75e4c1c490 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -37,15 +37,15 @@ description="Generate the code for the interpreter switch.", formatter_class=argparse.ArgumentDefaultsHelpFormatter, ) -arg_parser.add_argument( - "-i", "--input", type=str, help="Instruction definitions", default=DEFAULT_INPUT -) arg_parser.add_argument( "-o", "--output", type=str, help="Generated code", default=DEFAULT_OUTPUT ) arg_parser.add_argument( "-m", "--metadata", type=str, help="Generated metadata", default=DEFAULT_METADATA_OUTPUT ) +arg_parser.add_argument( + "input", nargs=argparse.REMAINDER, help="Instruction definition file(s)" +) def effect_size(effect: StackEffect) -> tuple[int, str]: @@ -485,6 +485,11 @@ class MacroInstruction(SuperOrMacroInstruction): parts: list[Component | parser.CacheEffect] +@dataclasses.dataclass +class OverriddenInstructionPlaceHolder: + name: str + + AnyInstruction = Instruction | SuperInstruction | MacroInstruction INSTR_FMT_PREFIX = "INSTR_FMT_" @@ -492,32 +497,33 @@ class MacroInstruction(SuperOrMacroInstruction): class Analyzer: """Parse input, analyze it, and write to output.""" - filename: str + input_filenames: list[str] output_filename: str metadata_filename: str - src: str errors: int = 0 - def __init__(self, filename: str, output_filename: str, metadata_filename: str): + def __init__(self, input_filenames: list[str], output_filename: str, metadata_filename: str): """Read the input file.""" - self.filename = filename + self.input_filenames = input_filenames self.output_filename = output_filename self.metadata_filename = metadata_filename - with open(filename) as f: - self.src = f.read() def error(self, msg: str, node: parser.Node) -> None: lineno = 0 + filename = "" if context := node.context: + filename = context.owner.filename # Use line number of first non-comment in the node for token in context.owner.tokens[context.begin : context.end]: lineno = token.line if token.kind != "COMMENT": break - print(f"{self.filename}:{lineno}: {msg}", file=sys.stderr) + print(f"{filename}:{lineno}: {msg}", file=sys.stderr) self.errors += 1 - everything: list[parser.InstDef | parser.Super | parser.Macro] + everything: list[ + parser.InstDef | parser.Super | parser.Macro | OverriddenInstructionPlaceHolder + ] instrs: dict[str, Instruction] # Includes ops supers: dict[str, parser.Super] super_instrs: dict[str, SuperInstruction] @@ -531,7 +537,31 @@ def parse(self) -> None: We only want the parser to see the stuff between the begin and end markers. """ - psr = parser.Parser(self.src, filename=self.filename) + + self.everything = [] + self.instrs = {} + self.supers = {} + self.macros = {} + self.families = {} + + instrs_idx: dict[str, int] = dict() + + for filename in self.input_filenames: + self.parse_file(filename, instrs_idx) + + files = " + ".join(self.input_filenames) + print( + f"Read {len(self.instrs)} instructions/ops, " + f"{len(self.supers)} supers, {len(self.macros)} macros, " + f"and {len(self.families)} families from {files}", + file=sys.stderr, + ) + + def parse_file(self, filename: str, instrs_idx: dict[str, int]) -> None: + with open(filename) as file: + src = file.read() + + psr = parser.Parser(src, filename=filename) # Skip until begin marker while tkn := psr.next(raw=True): @@ -551,16 +581,27 @@ def parse(self) -> None: # Parse from start psr.setpos(start) - self.everything = [] - self.instrs = {} - self.supers = {} - self.macros = {} - self.families = {} thing: parser.InstDef | parser.Super | parser.Macro | parser.Family | None + thing_first_token = psr.peek() while thing := psr.definition(): match thing: case parser.InstDef(name=name): + if name in self.instrs: + if not thing.override: + raise psr.make_syntax_error( + f"Duplicate definition of '{name}' @ {thing.context} " + f"previous definition @ {self.instrs[name].inst.context}", + thing_first_token, + ) + self.everything[instrs_idx[name]] = OverriddenInstructionPlaceHolder(name=name) + if name not in self.instrs and thing.override: + raise psr.make_syntax_error( + f"Definition of '{name}' @ {thing.context} is supposed to be " + "an override but no previous definition exists.", + thing_first_token, + ) self.instrs[name] = Instruction(thing) + instrs_idx[name] = len(self.everything) self.everything.append(thing) case parser.Super(name): self.supers[name] = thing @@ -573,14 +614,7 @@ def parse(self) -> None: case _: typing.assert_never(thing) if not psr.eof(): - raise psr.make_syntax_error("Extra stuff at the end") - - print( - f"Read {len(self.instrs)} instructions/ops, " - f"{len(self.supers)} supers, {len(self.macros)} macros, " - f"and {len(self.families)} families from {self.filename}", - file=sys.stderr, - ) + raise psr.make_syntax_error(f"Extra stuff at the end of {filename}") def analyze(self) -> None: """Analyze the inputs. @@ -879,6 +913,8 @@ def write_stack_effect_functions(self) -> None: popped_data: list[tuple[AnyInstruction, str]] = [] pushed_data: list[tuple[AnyInstruction, str]] = [] for thing in self.everything: + if isinstance(thing, OverriddenInstructionPlaceHolder): + continue instr, popped, pushed = self.get_stack_effect_info(thing) if instr is not None: popped_data.append((instr, popped)) @@ -907,6 +943,13 @@ def write_function( write_function("pushed", pushed_data) self.out.emit("") + def from_source_files(self) -> str: + paths = "\n// ".join( + os.path.relpath(filename, ROOT).replace(os.path.sep, posixpath.sep) + for filename in self.input_filenames + ) + return f"// from:\n// {paths}\n" + def write_metadata(self) -> None: """Write instruction metadata to output file.""" @@ -914,6 +957,8 @@ def write_metadata(self) -> None: all_formats: set[str] = set() for thing in self.everything: match thing: + case OverriddenInstructionPlaceHolder(): + continue case parser.InstDef(): format = self.instrs[thing.name].instr_fmt case parser.Super(): @@ -928,8 +973,8 @@ def write_metadata(self) -> None: with open(self.metadata_filename, "w") as f: # Write provenance header - f.write(f"// This file is generated by {THIS} --metadata\n") - f.write(f"// from {os.path.relpath(self.filename, ROOT).replace(os.path.sep, posixpath.sep)}\n") + f.write(f"// This file is generated by {THIS}\n") + f.write(self.from_source_files()) f.write(f"// Do not edit!\n") # Create formatter; the rest of the code uses this @@ -959,6 +1004,8 @@ def write_metadata(self) -> None: # Write metadata for each instruction for thing in self.everything: match thing: + case OverriddenInstructionPlaceHolder(): + continue case parser.InstDef(): if thing.kind != "op": self.write_metadata_for_inst(self.instrs[thing.name]) @@ -1008,7 +1055,7 @@ def write_instructions(self) -> None: with open(self.output_filename, "w") as f: # Write provenance header f.write(f"// This file is generated by {THIS}\n") - f.write(f"// from {os.path.relpath(self.filename, ROOT).replace(os.path.sep, posixpath.sep)}\n") + f.write(self.from_source_files()) f.write(f"// Do not edit!\n") # Create formatter; the rest of the code uses this @@ -1020,6 +1067,8 @@ def write_instructions(self) -> None: n_macros = 0 for thing in self.everything: match thing: + case OverriddenInstructionPlaceHolder(): + self.write_overridden_instr_place_holder(thing) case parser.InstDef(): if thing.kind != "op": n_instrs += 1 @@ -1039,9 +1088,17 @@ def write_instructions(self) -> None: file=sys.stderr, ) + def write_overridden_instr_place_holder(self, + place_holder: OverriddenInstructionPlaceHolder) -> None: + self.out.emit("") + self.out.emit( + f"// TARGET({place_holder.name}) overridden by later definition") + def write_instr(self, instr: Instruction) -> None: name = instr.name self.out.emit("") + if instr.inst.override: + self.out.emit("// Override") with self.out.block(f"TARGET({name})"): if instr.predicted: self.out.emit(f"PREDICTED({name});") @@ -1190,6 +1247,8 @@ def variable_used(node: parser.Node, name: str) -> bool: def main(): """Parse command line, parse input, analyze, write output.""" args = arg_parser.parse_args() # Prints message and sys.exit(2) on error + if len(args.input) == 0: + args.input.append(DEFAULT_INPUT) a = Analyzer(args.input, args.output, args.metadata) # Raises OSError if input unreadable a.parse() # Raises SyntaxError on failure a.analyze() # Prints messages and sets a.errors on failure diff --git a/Tools/cases_generator/lexer.py b/Tools/cases_generator/lexer.py index 39b6a212a67b1c..1c70d1c4089e4e 100644 --- a/Tools/cases_generator/lexer.py +++ b/Tools/cases_generator/lexer.py @@ -119,7 +119,7 @@ def choice(*opts): kwds = ( 'AUTO', 'BREAK', 'CASE', 'CHAR', 'CONST', 'CONTINUE', 'DEFAULT', 'DO', 'DOUBLE', 'ELSE', 'ENUM', 'EXTERN', - 'FLOAT', 'FOR', 'GOTO', 'IF', 'INLINE', 'INT', 'LONG', + 'FLOAT', 'FOR', 'GOTO', 'IF', 'INLINE', 'INT', 'LONG', 'OVERRIDE', 'REGISTER', 'OFFSETOF', 'RESTRICT', 'RETURN', 'SHORT', 'SIGNED', 'SIZEOF', 'STATIC', 'STRUCT', 'SWITCH', 'TYPEDEF', 'UNION', 'UNSIGNED', 'VOID', diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py index c7c8d8af6b7318..7bf45a350bc84b 100644 --- a/Tools/cases_generator/parser.py +++ b/Tools/cases_generator/parser.py @@ -33,7 +33,7 @@ class Context(NamedTuple): owner: PLexer def __repr__(self): - return f"<{self.begin}-{self.end}>" + return f"<{self.owner.filename}: {self.begin}-{self.end}>" @dataclass @@ -99,6 +99,7 @@ class OpName(Node): @dataclass class InstHeader(Node): + override: bool register: bool kind: Literal["inst", "op", "legacy"] # Legacy means no (inputs -- outputs) name: str @@ -108,6 +109,7 @@ class InstHeader(Node): @dataclass class InstDef(Node): + override: bool register: bool kind: Literal["inst", "op", "legacy"] name: str @@ -152,17 +154,18 @@ def inst_def(self) -> InstDef | None: if hdr := self.inst_header(): if block := self.block(): return InstDef( - hdr.register, hdr.kind, hdr.name, hdr.inputs, hdr.outputs, block + hdr.override, hdr.register, hdr.kind, hdr.name, hdr.inputs, hdr.outputs, block ) raise self.make_syntax_error("Expected block") return None @contextual def inst_header(self) -> InstHeader | None: - # inst(NAME) - # | [register] inst(NAME, (inputs -- outputs)) - # | [register] op(NAME, (inputs -- outputs)) + # [override] inst(NAME) + # | [override] [register] inst(NAME, (inputs -- outputs)) + # | [override] [register] op(NAME, (inputs -- outputs)) # TODO: Make INST a keyword in the lexer. + override = bool(self.expect(lx.OVERRIDE)) register = bool(self.expect(lx.REGISTER)) if (tkn := self.expect(lx.IDENTIFIER)) and (kind := tkn.text) in ("inst", "op"): if self.expect(lx.LPAREN) and (tkn := self.expect(lx.IDENTIFIER)): @@ -171,10 +174,10 @@ def inst_header(self) -> InstHeader | None: inp, outp = self.io_effect() if self.expect(lx.RPAREN): if (tkn := self.peek()) and tkn.kind == lx.LBRACE: - return InstHeader(register, kind, name, inp, outp) + return InstHeader(override, register, kind, name, inp, outp) elif self.expect(lx.RPAREN) and kind == "inst": # No legacy stack effect if kind is "op". - return InstHeader(register, "legacy", name, [], []) + return InstHeader(override, register, "legacy", name, [], []) return None def io_effect(self) -> tuple[list[InputEffect], list[OutputEffect]]: From b022250e67449e0bc49a3c982fe9e6a2d6a7b71a Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sat, 4 Mar 2023 12:20:14 +0000 Subject: [PATCH 232/247] Remove unused internal macros (#102415) Since #101826 was merged, the internal macro `_Py_InIntegralTypeRange` is unused, as are its supporting macros `_Py_IntegralTypeMax` and `_Py_IntegralTypeMin`. This PR removes them. Note that `_Py_InIntegralTypeRange` doesn't actually work as advertised - it's not a safe way to avoid undefined behaviour in an integer to double conversion. --- Include/internal/pycore_pymath.h | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/Include/internal/pycore_pymath.h b/Include/internal/pycore_pymath.h index 5f3afe4df6865c..7a4e1c1eb714f7 100644 --- a/Include/internal/pycore_pymath.h +++ b/Include/internal/pycore_pymath.h @@ -56,21 +56,6 @@ static inline void _Py_ADJUST_ERANGE2(double x, double y) } } -// Return the maximum value of integral type *type*. -#define _Py_IntegralTypeMax(type) \ - (_Py_IS_TYPE_SIGNED(type) ? (((((type)1 << (sizeof(type)*CHAR_BIT - 2)) - 1) << 1) + 1) : ~(type)0) - -// Return the minimum value of integral type *type*. -#define _Py_IntegralTypeMin(type) \ - (_Py_IS_TYPE_SIGNED(type) ? -_Py_IntegralTypeMax(type) - 1 : 0) - -// Check whether *v* is in the range of integral type *type*. This is most -// useful if *v* is floating-point, since demoting a floating-point *v* to an -// integral type that cannot represent *v*'s integral part is undefined -// behavior. -#define _Py_InIntegralTypeRange(type, v) \ - (_Py_IntegralTypeMin(type) <= v && v <= _Py_IntegralTypeMax(type)) - //--- HAVE_PY_SET_53BIT_PRECISION macro ------------------------------------ // From 705487c6557c3d8866622b4d32528bf7fc2e4204 Mon Sep 17 00:00:00 2001 From: Raj <51259329+workingpayload@users.noreply.github.com> Date: Sat, 4 Mar 2023 19:51:29 +0530 Subject: [PATCH 233/247] gh-101892: Fix `SystemError` when a callable iterator call exhausts the iterator (#101896) Co-authored-by: Oleg Iarygin --- Lib/test/test_iter.py | 25 +++++++++++++++++++ ...-02-14-09-08-48.gh-issue-101892.FMos8l.rst | 3 +++ Objects/iterobject.c | 4 +-- 3 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-02-14-09-08-48.gh-issue-101892.FMos8l.rst diff --git a/Lib/test/test_iter.py b/Lib/test/test_iter.py index 6ab9b7a7230309..30aedb0db3bb3d 100644 --- a/Lib/test/test_iter.py +++ b/Lib/test/test_iter.py @@ -348,6 +348,31 @@ def spam(state=[0]): return i self.check_iterator(iter(spam, 20), list(range(10)), pickle=False) + def test_iter_function_concealing_reentrant_exhaustion(self): + # gh-101892: Test two-argument iter() with a function that + # exhausts its associated iterator but forgets to either return + # a sentinel value or raise StopIteration. + HAS_MORE = 1 + NO_MORE = 2 + + def exhaust(iterator): + """Exhaust an iterator without raising StopIteration.""" + list(iterator) + + def spam(): + # Touching the iterator with exhaust() below will call + # spam() once again so protect against recursion. + if spam.is_recursive_call: + return NO_MORE + spam.is_recursive_call = True + exhaust(spam.iterator) + return HAS_MORE + + spam.is_recursive_call = False + spam.iterator = iter(spam, NO_MORE) + with self.assertRaises(StopIteration): + next(spam.iterator) + # Test exception propagation through function iterator def test_exception_function(self): def spam(state=[0]): diff --git a/Misc/NEWS.d/next/Library/2023-02-14-09-08-48.gh-issue-101892.FMos8l.rst b/Misc/NEWS.d/next/Library/2023-02-14-09-08-48.gh-issue-101892.FMos8l.rst new file mode 100644 index 00000000000000..d586779b3a8a36 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-14-09-08-48.gh-issue-101892.FMos8l.rst @@ -0,0 +1,3 @@ +Callable iterators no longer raise :class:`SystemError` when the +callable object exhausts the iterator but forgets to either return a +sentinel value or raise :class:`StopIteration`. diff --git a/Objects/iterobject.c b/Objects/iterobject.c index 149b701b68e91a..7cb17a6ca4ab56 100644 --- a/Objects/iterobject.c +++ b/Objects/iterobject.c @@ -219,7 +219,7 @@ calliter_iternext(calliterobject *it) } result = _PyObject_CallNoArgs(it->it_callable); - if (result != NULL) { + if (result != NULL && it->it_sentinel != NULL){ int ok; ok = PyObject_RichCompareBool(it->it_sentinel, result, Py_EQ); @@ -227,7 +227,6 @@ calliter_iternext(calliterobject *it) return result; /* Common case, fast path */ } - Py_DECREF(result); if (ok > 0) { Py_CLEAR(it->it_callable); Py_CLEAR(it->it_sentinel); @@ -238,6 +237,7 @@ calliter_iternext(calliterobject *it) Py_CLEAR(it->it_callable); Py_CLEAR(it->it_sentinel); } + Py_XDECREF(result); return NULL; } From c2bd55d26f8eb2850eb9f9026b5d7f0ed1420b65 Mon Sep 17 00:00:00 2001 From: Alexey Izbyshev Date: Sat, 4 Mar 2023 17:24:08 +0300 Subject: [PATCH 234/247] gh-102179: Fix `os.dup2` error reporting for negative fds (#102180) --- Lib/test/test_os.py | 20 +++++++++++++++++++ ...-02-23-15-06-01.gh-issue-102179.P6KQ4c.rst | 1 + Modules/posixmodule.c | 5 ----- 3 files changed, 21 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-02-23-15-06-01.gh-issue-102179.P6KQ4c.rst diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index deea207bfdadd9..792794ca109489 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2221,6 +2221,26 @@ def test_closerange(self): def test_dup2(self): self.check(os.dup2, 20) + @unittest.skipUnless(hasattr(os, 'dup2'), 'test needs os.dup2()') + @unittest.skipIf( + support.is_emscripten, + "dup2() with negative fds is broken on Emscripten (see gh-102179)" + ) + def test_dup2_negative_fd(self): + valid_fd = os.open(__file__, os.O_RDONLY) + self.addCleanup(os.close, valid_fd) + fds = [ + valid_fd, + -1, + -2**31, + ] + for fd, fd2 in itertools.product(fds, repeat=2): + if fd != fd2: + with self.subTest(fd=fd, fd2=fd2): + with self.assertRaises(OSError) as ctx: + os.dup2(fd, fd2) + self.assertEqual(ctx.exception.errno, errno.EBADF) + @unittest.skipUnless(hasattr(os, 'fchmod'), 'test needs os.fchmod()') def test_fchmod(self): self.check(os.fchmod, 0) diff --git a/Misc/NEWS.d/next/Library/2023-02-23-15-06-01.gh-issue-102179.P6KQ4c.rst b/Misc/NEWS.d/next/Library/2023-02-23-15-06-01.gh-issue-102179.P6KQ4c.rst new file mode 100644 index 00000000000000..f77493e267ac7e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-23-15-06-01.gh-issue-102179.P6KQ4c.rst @@ -0,0 +1 @@ +Fix :func:`os.dup2` error message for negative fds. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 937233a3bd0779..7beb2cee64a05c 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -9795,11 +9795,6 @@ os_dup2_impl(PyObject *module, int fd, int fd2, int inheritable) static int dup3_works = -1; #endif - if (fd < 0 || fd2 < 0) { - posix_error(); - return -1; - } - /* dup2() can fail with EINTR if the target FD is already open, because it * then has to be closed. See os_close_impl() for why we don't handle EINTR * upon close(), and therefore below. From 90801e48fdd2a57c5c5a5e202ff76c3a7dc42a50 Mon Sep 17 00:00:00 2001 From: Gouvernathor <44340603+Gouvernathor@users.noreply.github.com> Date: Sat, 4 Mar 2023 16:08:57 +0100 Subject: [PATCH 235/247] gh-102302 Micro-optimize `inspect.Parameter.__hash__` (#102303) --- Lib/inspect.py | 2 +- .../next/Library/2023-03-04-14-46-47.gh-issue-102302.-b_s6Z.rst | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2023-03-04-14-46-47.gh-issue-102302.-b_s6Z.rst diff --git a/Lib/inspect.py b/Lib/inspect.py index 8bb3a375735af6..166667c62cdccf 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -2805,7 +2805,7 @@ def __repr__(self): return '<{} "{}">'.format(self.__class__.__name__, self) def __hash__(self): - return hash((self.name, self.kind, self.annotation, self.default)) + return hash((self._name, self._kind, self._annotation, self._default)) def __eq__(self, other): if self is other: diff --git a/Misc/NEWS.d/next/Library/2023-03-04-14-46-47.gh-issue-102302.-b_s6Z.rst b/Misc/NEWS.d/next/Library/2023-03-04-14-46-47.gh-issue-102302.-b_s6Z.rst new file mode 100644 index 00000000000000..aaf4e62069ca24 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-03-04-14-46-47.gh-issue-102302.-b_s6Z.rst @@ -0,0 +1 @@ +Micro-optimise hashing of :class:`inspect.Parameter`, reducing the time it takes to hash an instance by around 40%. From 77a3196b7cc17d90a8aae5629aa71ff183b9266a Mon Sep 17 00:00:00 2001 From: Byeongmin Choi Date: Sun, 5 Mar 2023 01:01:54 +0900 Subject: [PATCH 236/247] gh-101863: Fix wrong comments in EUC-KR codec (gh-102417) --- Misc/ACKS | 1 + Modules/cjkcodecs/_codecs_kr.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Misc/ACKS b/Misc/ACKS index 2da3d0ab29b81d..c591cd3bfe4b9e 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -322,6 +322,7 @@ Adal Chiriliuc Matt Chisholm Lita Cho Kit Yan Choi +Byeongmin Choi Sayan Chowdhury Yuan-Chao Chou Anders Chrigström diff --git a/Modules/cjkcodecs/_codecs_kr.c b/Modules/cjkcodecs/_codecs_kr.c index 6d6acb5c4be4b5..72641e495af0b0 100644 --- a/Modules/cjkcodecs/_codecs_kr.c +++ b/Modules/cjkcodecs/_codecs_kr.c @@ -60,7 +60,7 @@ ENCODER(euc_kr) } else { /* Mapping is found in CP949 extension, - but we encode it in KS X 1001:1998 Annex 3, + but we encode it in KS X 1001:1998, make-up sequence for EUC-KR. */ REQUIRE_OUTBUF(8); @@ -120,7 +120,7 @@ DECODER(euc_kr) if (c == EUCKR_JAMO_FIRSTBYTE && INBYTE2 == EUCKR_JAMO_FILLER) { - /* KS X 1001:1998 Annex 3 make-up sequence */ + /* KS X 1001:1998 make-up sequence */ DBCHAR cho, jung, jong; REQUIRE_INBUF(8); From 81763341ede99ea5ae8993a57b8e3b71b46b2d72 Mon Sep 17 00:00:00 2001 From: Jaysinh Shukla Date: Sun, 5 Mar 2023 03:02:13 +0530 Subject: [PATCH 237/247] gh-63301: Set exit code when tabnanny CLI exits on error (#7699) Co-authored-by: Erlend E. Aasland --- Lib/tabnanny.py | 3 +- Lib/test/test_tabnanny.py | 30 ++++++++++++------- ...3-02-01-10-42-16.gh-issue-63301.XNxSFh.rst | 1 + 3 files changed, 21 insertions(+), 13 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-02-01-10-42-16.gh-issue-63301.XNxSFh.rst diff --git a/Lib/tabnanny.py b/Lib/tabnanny.py index a47f5a96b89722..9d2df59d36ff47 100755 --- a/Lib/tabnanny.py +++ b/Lib/tabnanny.py @@ -35,6 +35,7 @@ def errprint(*args): sys.stderr.write(sep + str(arg)) sep = " " sys.stderr.write("\n") + sys.exit(1) def main(): import getopt @@ -44,7 +45,6 @@ def main(): opts, args = getopt.getopt(sys.argv[1:], "qv") except getopt.error as msg: errprint(msg) - return for o, a in opts: if o == '-q': filename_only = filename_only + 1 @@ -52,7 +52,6 @@ def main(): verbose = verbose + 1 if not args: errprint("Usage:", sys.argv[0], "[-v] file_or_directory ...") - return for arg in args: check(arg) diff --git a/Lib/test/test_tabnanny.py b/Lib/test/test_tabnanny.py index e0a82e95c486be..afb8da719b0eed 100644 --- a/Lib/test/test_tabnanny.py +++ b/Lib/test/test_tabnanny.py @@ -110,9 +110,10 @@ def test_errprint(self): for args, expected in tests: with self.subTest(arguments=args, expected=expected): - with captured_stderr() as stderr: - tabnanny.errprint(*args) - self.assertEqual(stderr.getvalue() , expected) + with self.assertRaises(SystemExit): + with captured_stderr() as stderr: + tabnanny.errprint(*args) + self.assertEqual(stderr.getvalue() , expected) class TestNannyNag(TestCase): @@ -203,14 +204,16 @@ def test_when_wrong_indented(self): err = ('unindent does not match any outer indentation level' ' (, line 3)\n') err = f"{file_path!r}: Indentation Error: {err}" - self.verify_tabnanny_check(file_path, err=err) + with self.assertRaises(SystemExit): + self.verify_tabnanny_check(file_path, err=err) def test_when_tokenize_tokenerror(self): """A python source code file eligible for raising 'tokenize.TokenError'.""" with TemporaryPyFile(SOURCE_CODES["incomplete_expression"]) as file_path: err = "('EOF in multi-line statement', (7, 0))\n" err = f"{file_path!r}: Token Error: {err}" - self.verify_tabnanny_check(file_path, err=err) + with self.assertRaises(SystemExit): + self.verify_tabnanny_check(file_path, err=err) def test_when_nannynag_error_verbose(self): """A python source code file eligible for raising `tabnanny.NannyNag`. @@ -236,7 +239,8 @@ def test_when_no_file(self): path = 'no_file.py' err = (f"{path!r}: I/O Error: [Errno {errno.ENOENT}] " f"{os.strerror(errno.ENOENT)}: {path!r}\n") - self.verify_tabnanny_check(path, err=err) + with self.assertRaises(SystemExit): + self.verify_tabnanny_check(path, err=err) def test_errored_directory(self): """Directory containing wrongly indented python source code files.""" @@ -251,7 +255,8 @@ def test_errored_directory(self): err = ('unindent does not match any outer indentation level' ' (, line 3)\n') err = f"{e_file!r}: Indentation Error: {err}" - self.verify_tabnanny_check(tmp_dir, err=err) + with self.assertRaises(SystemExit): + self.verify_tabnanny_check(tmp_dir, err=err) class TestProcessTokens(TestCase): @@ -287,9 +292,12 @@ def test_with_errored_codes_samples(self): class TestCommandLine(TestCase): """Tests command line interface of `tabnanny`.""" - def validate_cmd(self, *args, stdout="", stderr="", partial=False): + def validate_cmd(self, *args, stdout="", stderr="", partial=False, expect_failure=False): """Common function to assert the behaviour of command line interface.""" - _, out, err = script_helper.assert_python_ok('-m', 'tabnanny', *args) + if expect_failure: + _, out, err = script_helper.assert_python_failure('-m', 'tabnanny', *args) + else: + _, out, err = script_helper.assert_python_ok('-m', 'tabnanny', *args) # Note: The `splitlines()` will solve the problem of CRLF(\r) added # by OS Windows. out = os.fsdecode(out) @@ -310,7 +318,7 @@ def test_with_errored_file(self): stderr = f"{file_path!r}: Indentation Error: " stderr += ('unindent does not match any outer indentation level' ' (, line 3)') - self.validate_cmd(file_path, stderr=stderr) + self.validate_cmd(file_path, stderr=stderr, expect_failure=True) def test_with_error_free_file(self): """Should not display anything if python file is correctly indented.""" @@ -321,7 +329,7 @@ def test_command_usage(self): """Should display usage on no arguments.""" path = findfile('tabnanny.py') stderr = f"Usage: {path} [-v] file_or_directory ..." - self.validate_cmd(stderr=stderr) + self.validate_cmd(stderr=stderr, expect_failure=True) def test_quiet_flag(self): """Should display less when quite mode is on.""" diff --git a/Misc/NEWS.d/next/Library/2023-02-01-10-42-16.gh-issue-63301.XNxSFh.rst b/Misc/NEWS.d/next/Library/2023-02-01-10-42-16.gh-issue-63301.XNxSFh.rst new file mode 100644 index 00000000000000..e00e71fb8554f3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-01-10-42-16.gh-issue-63301.XNxSFh.rst @@ -0,0 +1 @@ +Set exit code when :mod:`tabnanny` CLI exits on error. From e4609cbe4ca2d3d4fc07c19a7d0bdec52f054c63 Mon Sep 17 00:00:00 2001 From: Dustin Rodrigues Date: Sat, 4 Mar 2023 16:35:25 -0500 Subject: [PATCH 238/247] gh-101992: update pstlib module documentation (#102133) --- Lib/plistlib.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/Lib/plistlib.py b/Lib/plistlib.py index 30f3f673ada577..3292c30d5fb29b 100644 --- a/Lib/plistlib.py +++ b/Lib/plistlib.py @@ -21,6 +21,9 @@ Generate Plist example: + import datetime + import plistlib + pl = dict( aString = "Doodah", aList = ["A", "B", 12, 32.1, [1, 2, 3]], @@ -28,22 +31,28 @@ anInt = 728, aDict = dict( anotherString = "", - aUnicodeValue = "M\xe4ssig, Ma\xdf", + aThirdString = "M\xe4ssig, Ma\xdf", aTrueValue = True, aFalseValue = False, ), someData = b"", someMoreData = b"" * 10, - aDate = datetime.datetime.fromtimestamp(time.mktime(time.gmtime())), + aDate = datetime.datetime.now() ) - with open(fileName, 'wb') as fp: - dump(pl, fp) + print(plistlib.dumps(pl).decode()) Parse Plist example: - with open(fileName, 'rb') as fp: - pl = load(fp) - print(pl["aKey"]) + import plistlib + + plist = b''' + + foo + bar + + ''' + pl = plistlib.loads(plist) + print(pl["foo"]) """ __all__ = [ "InvalidFileException", "FMT_XML", "FMT_BINARY", "load", "dump", "loads", "dumps", "UID" From eff9f43924fc836970b2378d58523388d9246194 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20G=C3=B6rgens?= Date: Sun, 5 Mar 2023 05:39:52 +0800 Subject: [PATCH 239/247] gh-96821: Add config option `--with-strict-overflow` (#96823) Co-authored-by: C.A.M. Gerlach Co-authored-by: Erlend E. Aasland Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Co-authored-by: Shantanu --- Doc/using/configure.rst | 5 ++ ...2-09-14-10-38-15.gh-issue-96821.Zk2a9c.rst | 3 + configure | 78 ++++++++++++++++--- configure.ac | 54 ++++++++++--- 4 files changed, 122 insertions(+), 18 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2022-09-14-10-38-15.gh-issue-96821.Zk2a9c.rst diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst index 8fa8d250d533c9..8936bd381c9d97 100644 --- a/Doc/using/configure.rst +++ b/Doc/using/configure.rst @@ -326,6 +326,11 @@ also be used to improve performance. Enable C-level code profiling with ``gprof`` (disabled by default). +.. cmdoption:: --with-strict-overflow + + Add ``-fstrict-overflow`` to the C compiler flags (by default we add + ``-fno-strict-overflow`` instead). + .. _debug-build: diff --git a/Misc/NEWS.d/next/Build/2022-09-14-10-38-15.gh-issue-96821.Zk2a9c.rst b/Misc/NEWS.d/next/Build/2022-09-14-10-38-15.gh-issue-96821.Zk2a9c.rst new file mode 100644 index 00000000000000..865cfde8b06359 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2022-09-14-10-38-15.gh-issue-96821.Zk2a9c.rst @@ -0,0 +1,3 @@ +Explicitly mark C extension modules that need defined signed integer overflow, +and add a configure option :option:`--with-strict-overflow`. +Patch by Matthias Görgens and Shantanu Jain. diff --git a/configure b/configure index 557519ad86e06d..9e99352f589f21 100755 --- a/configure +++ b/configure @@ -1054,6 +1054,7 @@ with_assertions enable_optimizations with_lto enable_bolt +with_strict_overflow with_dsymutil with_address_sanitizer with_memory_sanitizer @@ -1825,6 +1826,8 @@ Optional Packages: --with-lto=[full|thin|no|yes] enable Link-Time-Optimization in any build (default is no) + --with-strict-overflow if 'yes', add -fstrict-overflow to CFLAGS, else add + -fno-strict-overflow (default is no) --with-dsymutil link debug information into final executable with dsymutil in macOS (default is no) --with-address-sanitizer @@ -8376,6 +8379,64 @@ case $CC in fi esac +save_CFLAGS=$CFLAGS +CFLAGS="-fstrict-overflow -fno-strict-overflow" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC supports -fstrict-overflow and -fno-strict-overflow" >&5 +$as_echo_n "checking if $CC supports -fstrict-overflow and -fno-strict-overflow... " >&6; } +if ${ac_cv_cc_supports_fstrict_overflow+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_cc_supports_fstrict_overflow=yes +else + ac_cv_cc_supports_fstrict_overflow=no + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cc_supports_fstrict_overflow" >&5 +$as_echo "$ac_cv_cc_supports_fstrict_overflow" >&6; } +CFLAGS=$save_CFLAGS + +if test "x$ac_cv_cc_supports_fstrict_overflow" = xyes; then : + STRICT_OVERFLOW_CFLAGS="-fstrict-overflow" + NO_STRICT_OVERFLOW_CFLAGS="-fno-strict-overflow" +else + STRICT_OVERFLOW_CFLAGS="" + NO_STRICT_OVERFLOW_CFLAGS="" +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for --with-strict-overflow" >&5 +$as_echo_n "checking for --with-strict-overflow... " >&6; } + +# Check whether --with-strict-overflow was given. +if test "${with_strict_overflow+set}" = set; then : + withval=$with_strict_overflow; + if test "x$ac_cv_cc_supports_fstrict_overflow" = xno; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: --with-strict-overflow=yes requires a compiler that supports -fstrict-overflow" >&5 +$as_echo "$as_me: WARNING: --with-strict-overflow=yes requires a compiler that supports -fstrict-overflow" >&2;} +fi + +else + with_strict_overflow=no + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_strict_overflow" >&5 +$as_echo "$with_strict_overflow" >&6; } + # Check if CC supports -Og optimization level save_CFLAGS=$CFLAGS CFLAGS="-Og" @@ -8428,15 +8489,8 @@ if test "${OPT-unset}" = "unset" then case $GCC in yes) - # For gcc 4.x we need to use -fwrapv so lets check if its supported - if "$CC" -v --help 2>/dev/null |grep -- -fwrapv > /dev/null; then - WRAP="-fwrapv" - fi - if test -n "${cc_is_clang}" then - # Clang also needs -fwrapv - WRAP="-fwrapv" # bpo-30104: disable strict aliasing to compile correctly dtoa.c, # see Makefile.pre.in for more information CFLAGS_ALIASING="-fno-strict-aliasing" @@ -8447,7 +8501,7 @@ then if test "$Py_DEBUG" = 'true' ; then OPT="-g $PYDEBUG_CFLAGS -Wall" else - OPT="-g $WRAP -O3 -Wall" + OPT="-g -O3 -Wall" fi ;; *) @@ -8580,6 +8634,12 @@ UNIVERSAL_ARCH_FLAGS= # tweak BASECFLAGS based on compiler and platform +if test "x$with_strict_overflow" = xyes; then : + BASECFLAGS="$BASECFLAGS $STRICT_OVERFLOW_CFLAGS" +else + BASECFLAGS="$BASECFLAGS $NO_STRICT_OVERFLOW_CFLAGS" +fi + case $GCC in yes) CFLAGS_NODIST="$CFLAGS_NODIST -std=c11" @@ -27049,7 +27109,7 @@ fi as_fn_append MODULE_BLOCK "MODULE__CTYPES_STATE=$py_cv_module__ctypes$as_nl" if test "x$py_cv_module__ctypes" = xyes; then : - as_fn_append MODULE_BLOCK "MODULE__CTYPES_CFLAGS=$LIBFFI_CFLAGS$as_nl" + as_fn_append MODULE_BLOCK "MODULE__CTYPES_CFLAGS=$NO_STRICT_OVERFLOW_CFLAGS $LIBFFI_CFLAGS$as_nl" as_fn_append MODULE_BLOCK "MODULE__CTYPES_LDFLAGS=$LIBFFI_LIBS$as_nl" fi diff --git a/configure.ac b/configure.ac index 982f669acbcfe5..31b7a2157a2bcc 100644 --- a/configure.ac +++ b/configure.ac @@ -2073,6 +2073,45 @@ case $CC in fi esac +dnl Historically, some of our code assumed that signed integer overflow +dnl is defined behaviour via twos-complement. +dnl Set STRICT_OVERFLOW_CFLAGS and NO_STRICT_OVERFLOW_CFLAGS depending on compiler support. +dnl Pass the latter to modules that depend on such behaviour. +_SAVE_VAR([CFLAGS]) +CFLAGS="-fstrict-overflow -fno-strict-overflow" +AC_CACHE_CHECK([if $CC supports -fstrict-overflow and -fno-strict-overflow], + [ac_cv_cc_supports_fstrict_overflow], + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[]], [[]])], + [ac_cv_cc_supports_fstrict_overflow=yes], + [ac_cv_cc_supports_fstrict_overflow=no] + ) +) +_RESTORE_VAR([CFLAGS]) + +AS_VAR_IF([ac_cv_cc_supports_fstrict_overflow], [yes], + [STRICT_OVERFLOW_CFLAGS="-fstrict-overflow" + NO_STRICT_OVERFLOW_CFLAGS="-fno-strict-overflow"], + [STRICT_OVERFLOW_CFLAGS="" + NO_STRICT_OVERFLOW_CFLAGS=""]) + +AC_MSG_CHECKING([for --with-strict-overflow]) +AC_ARG_WITH([strict-overflow], + AS_HELP_STRING( + [--with-strict-overflow], + [if 'yes', add -fstrict-overflow to CFLAGS, else add -fno-strict-overflow (default is no)] + ), + [ + AS_VAR_IF( + [ac_cv_cc_supports_fstrict_overflow], [no], + [AC_MSG_WARN([--with-strict-overflow=yes requires a compiler that supports -fstrict-overflow])], + [] + ) + ], + [with_strict_overflow=no] +) +AC_MSG_RESULT([$with_strict_overflow]) + # Check if CC supports -Og optimization level _SAVE_VAR([CFLAGS]) CFLAGS="-Og" @@ -2103,15 +2142,8 @@ if test "${OPT-unset}" = "unset" then case $GCC in yes) - # For gcc 4.x we need to use -fwrapv so lets check if its supported - if "$CC" -v --help 2>/dev/null |grep -- -fwrapv > /dev/null; then - WRAP="-fwrapv" - fi - if test -n "${cc_is_clang}" then - # Clang also needs -fwrapv - WRAP="-fwrapv" # bpo-30104: disable strict aliasing to compile correctly dtoa.c, # see Makefile.pre.in for more information CFLAGS_ALIASING="-fno-strict-aliasing" @@ -2122,7 +2154,7 @@ then if test "$Py_DEBUG" = 'true' ; then OPT="-g $PYDEBUG_CFLAGS -Wall" else - OPT="-g $WRAP -O3 -Wall" + OPT="-g -O3 -Wall" fi ;; *) @@ -2237,6 +2269,10 @@ AC_DEFUN([PY_CHECK_CC_WARNING], [ ]) # tweak BASECFLAGS based on compiler and platform +AS_VAR_IF([with_strict_overflow], [yes], + [BASECFLAGS="$BASECFLAGS $STRICT_OVERFLOW_CFLAGS"], + [BASECFLAGS="$BASECFLAGS $NO_STRICT_OVERFLOW_CFLAGS"]) + case $GCC in yes) CFLAGS_NODIST="$CFLAGS_NODIST -std=c11" @@ -7213,7 +7249,7 @@ PY_STDLIB_MOD([_crypt], [$LIBCRYPT_CFLAGS], [$LIBCRYPT_LIBS]) PY_STDLIB_MOD([_ctypes], [], [test "$have_libffi" = yes], - [$LIBFFI_CFLAGS], [$LIBFFI_LIBS]) + [$NO_STRICT_OVERFLOW_CFLAGS $LIBFFI_CFLAGS], [$LIBFFI_LIBS]) PY_STDLIB_MOD([_curses], [], [test "$have_curses" != "no"], [$CURSES_CFLAGS], [$CURSES_LIBS] From a74cd3ba5de1aad1a1e1ee57328b54c22be47f77 Mon Sep 17 00:00:00 2001 From: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> Date: Sun, 5 Mar 2023 12:15:22 +0530 Subject: [PATCH 240/247] GH-97546: fix flaky asyncio `test_wait_for_race_condition` test (#102421) --- Lib/test/test_asyncio/test_waitfor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_asyncio/test_waitfor.py b/Lib/test/test_asyncio/test_waitfor.py index ed80540b2b3852..d5c02ba4a01df9 100644 --- a/Lib/test/test_asyncio/test_waitfor.py +++ b/Lib/test/test_asyncio/test_waitfor.py @@ -159,7 +159,7 @@ async def test_wait_for_race_condition(self): fut = loop.create_future() task = asyncio.wait_for(fut, timeout=0.2) - loop.call_later(0.1, fut.set_result, "ok") + loop.call_soon(fut.set_result, "ok") res = await task self.assertEqual(res, "ok") From 5da379ca7dff44b321450800252be01041b3320b Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sun, 5 Mar 2023 12:31:56 +0300 Subject: [PATCH 241/247] Move around example in to_bytes() to avoid confusion (#101595) Moves an example to be closer to the sentence that refers to it. --- Doc/library/stdtypes.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 1240f80b0f11f0..550f808a16dfaa 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -530,12 +530,14 @@ class`. In addition, it provides a few more methods: is ``False``. The default values can be used to conveniently turn an integer into a - single byte object. However, when using the default arguments, don't try - to convert a value greater than 255 or you'll get an :exc:`OverflowError`:: + single byte object:: >>> (65).to_bytes() b'A' + However, when using the default arguments, don't try + to convert a value greater than 255 or you'll get an :exc:`OverflowError`. + Equivalent to:: def to_bytes(n, length=1, byteorder='big', signed=False): From 66aa78cbe604a7c5731f074b869f92174a8e3b64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marta=20G=C3=B3mez=20Mac=C3=ADas?= Date: Sun, 5 Mar 2023 12:00:41 +0100 Subject: [PATCH 242/247] gh-102356: Add thrashcan macros to filter object dealloc (#102426) Add thrashcan macros to the deallocator of the filter objects to protect against deeply nested destruction of chains of nested filters. --- Lib/test/test_builtin.py | 10 ++++++++++ Misc/ACKS | 1 + .../2023-03-04-20-56-12.gh-issue-102356.07KvUd.rst | 2 ++ Python/bltinmodule.c | 2 ++ 4 files changed, 15 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-03-04-20-56-12.gh-issue-102356.07KvUd.rst diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 9e19af0ae90fc1..e7a79bc13b7f3d 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -926,6 +926,16 @@ def test_filter_pickle(self): f2 = filter(filter_char, "abcdeabcde") self.check_iter_pickle(f1, list(f2), proto) + def test_filter_dealloc(self): + # Tests recursive deallocation of nested filter objects using the + # thrashcan mechanism. See gh-102356 for more details. + max_iters = 1000000 + i = filter(bool, range(max_iters)) + for _ in range(max_iters): + i = filter(bool, i) + del i + gc.collect() + def test_getattr(self): self.assertTrue(getattr(sys, 'stdout') is sys.stdout) self.assertRaises(TypeError, getattr) diff --git a/Misc/ACKS b/Misc/ACKS index c591cd3bfe4b9e..7bbde3af99782b 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -637,6 +637,7 @@ Tim Golden Yonatan Goldschmidt Mark Gollahon Mikhail Golubev +Marta Gómez Macías Guilherme Gonçalves Tiago Gonçalves Chris Gonnerman diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-03-04-20-56-12.gh-issue-102356.07KvUd.rst b/Misc/NEWS.d/next/Core and Builtins/2023-03-04-20-56-12.gh-issue-102356.07KvUd.rst new file mode 100644 index 00000000000000..c03fd5266bc301 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-03-04-20-56-12.gh-issue-102356.07KvUd.rst @@ -0,0 +1,2 @@ +Fix a bug that caused a crash when deallocating deeply nested filter +objects. Patch by Marta Gómez Macías. diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 53439ab16040c4..12ca0ba6c4873c 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -553,9 +553,11 @@ static void filter_dealloc(filterobject *lz) { PyObject_GC_UnTrack(lz); + Py_TRASHCAN_BEGIN(lz, filter_dealloc) Py_XDECREF(lz->func); Py_XDECREF(lz->it); Py_TYPE(lz)->tp_free(lz); + Py_TRASHCAN_END } static int From 9a478be1a4314734c697dda7a7b0e633a6fb0751 Mon Sep 17 00:00:00 2001 From: Yeojin Kim Date: Sun, 5 Mar 2023 23:54:33 +0900 Subject: [PATCH 243/247] gh-101979: argparse: fix a bug where parentheses in metavar argument of add_argument() were dropped (#102318) --- Lib/argparse.py | 13 ++++++++--- Lib/test/test_argparse.py | 22 +++++++++++++++++++ ...-02-28-09-52-25.gh-issue-101979.or3hXV.rst | 2 ++ 3 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-02-28-09-52-25.gh-issue-101979.or3hXV.rst diff --git a/Lib/argparse.py b/Lib/argparse.py index 240625ff01084e..a819d2650e85f0 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -403,10 +403,18 @@ def _format_actions_usage(self, actions, groups): except ValueError: continue else: - end = start + len(group._group_actions) + group_action_count = len(group._group_actions) + end = start + group_action_count if actions[start:end] == group._group_actions: + + suppressed_actions_count = 0 for action in group._group_actions: group_actions.add(action) + if action.help is SUPPRESS: + suppressed_actions_count += 1 + + exposed_actions_count = group_action_count - suppressed_actions_count + if not group.required: if start in inserts: inserts[start] += ' [' @@ -416,7 +424,7 @@ def _format_actions_usage(self, actions, groups): inserts[end] += ']' else: inserts[end] = ']' - else: + elif exposed_actions_count > 1: if start in inserts: inserts[start] += ' (' else: @@ -490,7 +498,6 @@ def _format_actions_usage(self, actions, groups): text = _re.sub(r'(%s) ' % open, r'\1', text) text = _re.sub(r' (%s)' % close, r'\1', text) text = _re.sub(r'%s *%s' % (open, close), r'', text) - text = _re.sub(r'\(([^|]*)\)', r'\1', text) text = text.strip() # return the text diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index cabb2f837693ff..861da2326d1214 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -3764,6 +3764,28 @@ class TestHelpUsage(HelpTestCase): version = '' +class TestHelpUsageWithParentheses(HelpTestCase): + parser_signature = Sig(prog='PROG') + argument_signatures = [ + Sig('positional', metavar='(example) positional'), + Sig('-p', '--optional', metavar='{1 (option A), 2 (option B)}'), + ] + + usage = '''\ + usage: PROG [-h] [-p {1 (option A), 2 (option B)}] (example) positional + ''' + help = usage + '''\ + + positional arguments: + (example) positional + + options: + -h, --help show this help message and exit + -p {1 (option A), 2 (option B)}, --optional {1 (option A), 2 (option B)} + ''' + version = '' + + class TestHelpOnlyUserGroups(HelpTestCase): """Test basic usage messages""" diff --git a/Misc/NEWS.d/next/Library/2023-02-28-09-52-25.gh-issue-101979.or3hXV.rst b/Misc/NEWS.d/next/Library/2023-02-28-09-52-25.gh-issue-101979.or3hXV.rst new file mode 100644 index 00000000000000..1efe72439b3a4a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-28-09-52-25.gh-issue-101979.or3hXV.rst @@ -0,0 +1,2 @@ +Fix a bug where parentheses in the ``metavar`` argument to :meth:`argparse.ArgumentParser.add_argument` were +dropped. Patch by Yeojin Kim. From 7894bbe94ba319eb650f383cb5196424c77b2cfd Mon Sep 17 00:00:00 2001 From: JosephSBoyle <48555120+JosephSBoyle@users.noreply.github.com> Date: Sun, 5 Mar 2023 15:07:44 +0000 Subject: [PATCH 244/247] Fix unused classes in a typing test (GH-102437) As part of investigation issue https://github.com/python/cpython/issues/102433, I discovered what I believe to be an error where two classes `CI` and `DI` are not being used. The assertions beneath them act on `C` and `D`, duplicating existing assertions in this test. Automerge-Triggered-By: GH:AlexWaygood --- Lib/test/test_typing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 96496ec9279e3f..2eeaf91d78d8f3 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -2921,8 +2921,8 @@ class DI: def __init__(self): self.x = None - self.assertIsInstance(C(), P) - self.assertIsInstance(D(), P) + self.assertIsInstance(CI(), P) + self.assertIsInstance(DI(), P) def test_protocols_in_unions(self): class P(Protocol): From 32220543e2db36c6146ff2704ed1714a6adecc1b Mon Sep 17 00:00:00 2001 From: "Partha P. Mukherjee" Date: Sun, 5 Mar 2023 12:31:26 -0500 Subject: [PATCH 245/247] GH-102341: Improve the test function for pow (#102342) Co-authored-by: Terry Jan Reedy --- Lib/test/test_pow.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_pow.py b/Lib/test/test_pow.py index 5cea9ceb20f5cc..eeb482ec4b27e2 100644 --- a/Lib/test/test_pow.py +++ b/Lib/test/test_pow.py @@ -19,12 +19,11 @@ def powtest(self, type): self.assertEqual(pow(2, i), pow2) if i != 30 : pow2 = pow2*2 - for othertype in (int,): - for i in list(range(-10, 0)) + list(range(1, 10)): - ii = type(i) - for j in range(1, 11): - jj = -othertype(j) - pow(ii, jj) + for i in list(range(-10, 0)) + list(range(1, 10)): + ii = type(i) + inv = pow(ii, -1) # inverse of ii + for jj in range(-10, 0): + self.assertAlmostEqual(pow(ii, jj), pow(inv, -jj)) for othertype in int, float: for i in range(1, 100): From 96e10229292145012bc462a6ab3ce1626c8acf71 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Sun, 5 Mar 2023 21:37:29 +0000 Subject: [PATCH 246/247] gh-102444: Fix minor bugs in `test_typing` highlighted by pyflakes (#102445) --- Lib/test/test_typing.py | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 2eeaf91d78d8f3..0483ca3aa42f94 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -473,7 +473,6 @@ def test_var_substitution(self): def test_bad_var_substitution(self): T = TypeVar('T') - P = ParamSpec("P") bad_args = ( (), (int, str), Union, Generic, Generic[T], Protocol, Protocol[T], @@ -1037,8 +1036,6 @@ class G2(Generic[Unpack[Ts]]): pass def test_repr_is_correct(self): Ts = TypeVarTuple('Ts') - T = TypeVar('T') - T2 = TypeVar('T2') class G1(Generic[*Ts]): pass class G2(Generic[Unpack[Ts]]): pass @@ -1307,7 +1304,7 @@ def test_callable_args_are_correct(self): i = Callable[[None], *Ts] j = Callable[[None], Unpack[Ts]] self.assertEqual(i.__args__, (type(None), *Ts)) - self.assertEqual(i.__args__, (type(None), Unpack[Ts])) + self.assertEqual(j.__args__, (type(None), Unpack[Ts])) k = Callable[[None], tuple[int, *Ts]] l = Callable[[None], Tuple[int, Unpack[Ts]]] @@ -1435,8 +1432,6 @@ def g(*args: *Ts): pass self.assertEqual(g.__annotations__, {'args': (*Ts,)[0]}) def test_variadic_args_with_ellipsis_annotations_are_correct(self): - Ts = TypeVarTuple('Ts') - def a(*args: *tuple[int, ...]): pass self.assertEqual(a.__annotations__, {'args': (*tuple[int, ...],)[0]}) @@ -4918,7 +4913,6 @@ 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 -import asyncio T_a = TypeVar('T_a') @@ -7077,16 +7071,6 @@ class C: self.assertEqual(get_type_hints(C, globals())['classvar'], ClassVar[int]) self.assertEqual(get_type_hints(C, globals())['const'], Final[int]) - def test_hash_eq(self): - self.assertEqual(len({Annotated[int, 4, 5], Annotated[int, 4, 5]}), 1) - self.assertNotEqual(Annotated[int, 4, 5], Annotated[int, 5, 4]) - self.assertNotEqual(Annotated[int, 4, 5], Annotated[str, 4, 5]) - self.assertNotEqual(Annotated[int, 4], Annotated[int, 4, 4]) - self.assertEqual( - {Annotated[int, 4, 5], Annotated[int, 4, 5], Annotated[T, 4, 5]}, - {Annotated[int, 4, 5], Annotated[T, 4, 5]} - ) - def test_cannot_subclass(self): with self.assertRaisesRegex(TypeError, "Cannot subclass .*Annotated"): class C(Annotated): @@ -7515,7 +7499,6 @@ class Y(Generic[P, T]): self.assertEqual(B.__args__, ((int, str,), Tuple[bytes, float])) def test_var_substitution(self): - T = TypeVar("T") P = ParamSpec("P") subst = P.__typing_subst__ self.assertEqual(subst((int, str)), (int, str)) @@ -7835,7 +7818,7 @@ def test_special_attrs2(self): self.assertEqual(fr.__module__, 'typing') # Forward refs are currently unpicklable. for proto in range(pickle.HIGHEST_PROTOCOL + 1): - with self.assertRaises(TypeError) as exc: + with self.assertRaises(TypeError): pickle.dumps(fr, proto) self.assertEqual(SpecialAttrsTests.TypeName.__name__, 'TypeName') From 3572c861d8e8f03c34793608e7e2221e116aaec0 Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Sun, 5 Mar 2023 22:00:56 +0000 Subject: [PATCH 247/247] GH-101362: Call join() only when >1 argument supplied to pathlib.PurePath() (#101665) GH-101362: Call join() only when >1 argument supplied to pathlib.PurePath This reduces the time taken to run `PurePath("foo")` by ~15% --- Lib/pathlib.py | 5 ++++- .../Library/2023-02-07-21-16-41.gh-issue-101362.KMQllM.rst | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2023-02-07-21-16-41.gh-issue-101362.KMQllM.rst diff --git a/Lib/pathlib.py b/Lib/pathlib.py index dde573592fddce..ed0f2cc73dfa69 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -275,9 +275,12 @@ def __reduce__(self): def _parse_parts(cls, parts): if not parts: return '', '', [] + elif len(parts) == 1: + path = os.fspath(parts[0]) + else: + path = cls._flavour.join(*parts) sep = cls._flavour.sep altsep = cls._flavour.altsep - path = cls._flavour.join(*parts) if altsep: path = path.replace(altsep, sep) drv, root, rel = cls._flavour.splitroot(path) diff --git a/Misc/NEWS.d/next/Library/2023-02-07-21-16-41.gh-issue-101362.KMQllM.rst b/Misc/NEWS.d/next/Library/2023-02-07-21-16-41.gh-issue-101362.KMQllM.rst new file mode 100644 index 00000000000000..af4ee9ad904868 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-07-21-16-41.gh-issue-101362.KMQllM.rst @@ -0,0 +1,2 @@ +Speed up :class:`pathlib.PurePath` construction by calling +:func:`os.path.join` only when two or more arguments are given.

    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

  2. AebyOey!w1)WzE3EK13 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