From 3ba7743b0696202e9caa283a0be253fd26a5cfbd Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Tue, 21 Feb 2023 20:21:24 -0500 Subject: [PATCH 01/48] 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 02/48] 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 03/48] 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 04/48] 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 05/48] 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 06/48] 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 07/48] 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 08/48] 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 09/48] 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 10/48] 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 11/48] 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 12/48] 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 13/48] 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 14/48] 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 15/48] 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 16/48] 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 17/48] 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 18/48] 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 19/48] 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 20/48] 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 21/48] 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 22/48] 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 23/48] 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 24/48] 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 25/48] 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 26/48] 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 27/48] 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 28/48] 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 29/48] 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 30/48] 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 31/48] 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 32/48] 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 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 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 33/48] 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 34/48] 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 35/48] 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 36/48] 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 37/48] 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 38/48] 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 39/48] [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 40/48] 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 41/48] 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 42/48] 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 43/48] 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 44/48] 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 45/48] 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 46/48] 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 47/48] 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 48/48] 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.